incubator-hugegraph icon indicating copy to clipboard operation
incubator-hugegraph copied to clipboard

[Question] 单个更新顶点上 list 或者 set 类型的数据要怎么操作

Open Ckuangf opened this issue 4 years ago • 15 comments

Note ( 特别注意 ) :

  1. 请先搜索, 并确认现有的 IssuesFAQ 中没有与您相同 / 相关的问题, 请勿重复提交
  2. 我们需要尽可能详细的信息来分析问题, 越详细的信息 (包括日志 / 截图 / 配置等) 会越快被响应和处理
  3. 请关注提交的 issue, 缺乏信息 / 长时间 ( > 14 天) 没有回复, issue 可能会被 关闭 (需要时可再开启)

Environment ( 环境信息 - 必填 )

  • Server Version: v0.11.x (refer here)
  • Backend: Cassandra 3.x, x nodes, HDD or SSD
  • OS: xx CPUs, xx G RAM, Centos 7.x
  • Data Size: xx vertices, xx edges (like 1000W 点, 9000W 边)

Your Question ( 问题描述 )

请问下,如果要单个更新顶点上 list或者set类型的数据要怎么操作,目前在官方文档中没有找到单个顶点更新可以指定更新策略的方式,而且如果是list或者set类型的字段默认是添加操作,批量更新的时候,主键类型是不支持Automatic的,如果是可空字段可以通过先删除后增加的方式,但是非空字段呢要怎么操作 xxx

注: 图使用 / 配置相关问题, 请优先参考 REST-API 文档, 以及 Server 配置文档

Related information ( 补充相关信息 ) :

Provide related "Data & Schema" info (Click to expand)

Vertex/Edge example ( 问题点 / 边数据举例 )

// JSON of Vertex / Edge ⬇

Schema [VertexLabel, EdgeLabel, IndexLabel] ( 元数据结构 )

// JSON of GraphSchema ⬇

Ckuangf avatar May 27 '21 13:05 Ckuangf

  1. 更新策略目前的确只有批量接口支持, 不过你传入一个点也就是单点 + 更新策略的更新了, 没有影响的
  2. 是否非空都可以通过增加 OVERRIDE 参数选择 覆盖 还是 保留 原有非空的值, 空值直接覆盖也并不影响. 其他的描述没太理解意思, 你可以试试单个点的批量更新, 然后有其他问题再更新反馈

(另外标题简短描述问题即可, 希望能保持原有模板风格 --> [Question] xxx , 关于 issue 模板的建议也欢迎反馈~ 😄 )

imbajin avatar May 27 '21 13:05 imbajin

  1. 更新策略目前的确只有批量接口支持, 不过你传入一个点也就是单点 + 更新策略的更新了, 没有影响的
  2. 是否非空都可以通过增加 OVERRIDE 参数选择 覆盖 还是 保留 原有非空的值, 空值直接覆盖也并不影响. 其他的描述没太理解意思, 你可以试试单个点的批量更新, 然后有其他问题再更新反馈

(另外标题简短描述问题即可, 希望能保持原有模板风格 --> [Question] xxx , 关于 issue 模板的建议也欢迎反馈~ 😄 )

针对第二点:因为批量更新对于主键策略有限制,目前是只支持custom和primaryKey,如果是automatic策略的顶点是不支持批量更新的,而单个更新的接口对于list和set类型的数据又是追加的方式,这样就会导致,automatic类型的顶点中如果有list或者set类型的字段值就无法进行覆盖更新

Ckuangf avatar May 28 '21 01:05 Ckuangf

这样, 那就是说你也不想覆盖其他属性, 只是单纯希望更新的时候覆盖 list / set 属性, 又是自增策略的 ID是吧.

那目前默认的确没太好的办法, 不过是否能更新某个属性也跟后端有关的, 你可以 gremlinproperty() 尝试单个更新覆盖试试.

PS: 因为自增 ID 我理解一般是测试的时候用的, 它本身也有不少局限性, 以至于有些接口就没特殊考虑它, 不然会增加不少复杂性, 正常生产环境应该不会用自增 ID 的方式吧.

imbajin avatar May 28 '21 02:05 imbajin

这样, 那就是说你也不想覆盖其他属性, 只是单纯希望更新的时候覆盖 list / set 属性, 又是自增策略的 ID是吧.

那目前默认的确没太好的办法, 不过是否能更新某个属性也跟后端有关的, 你可以 gremlinproperty() 尝试单个更新覆盖试试.

PS: 因为自增 ID 我理解一般是测试的时候用的, 它本身也有不少局限性, 以至于有些接口就没特殊考虑它, 不然会增加不少复杂性, 正常生产环境应该不会用自增 ID 的方式吧.

啊,感觉自增id在生产环境下使用场景还是有不少的吧,还是说这个策略目前在hugegraph 自增并不能保证唯一,分布式部署情况也不能保证唯一呢

Ckuangf avatar May 28 '21 03:05 Ckuangf

分布式下也可以保证唯一性的, 用的开源的 snowflake 算法, 不过图上如果你 vid eid 都是自增生成的话, 查询的时候如何根据 id 查呢? TP 场景下基本都是由点/边 id 出发的遍历 (或者是用于图计算场景么)

imbajin avatar May 28 '21 08:05 imbajin

分布式下也可以保证唯一性的, 用的开源的 snowflake 算法, 不过图上如果你 vid eid 都是自增生成的话, 查询的时候如何根据 id 查呢? TP 场景下基本都是由点/边 id 出发的遍历 (或者是用于图计算场景么)

其实我有点没太get到为啥查询的时候会由id出发作为查询步骤的开始,即使需要从id出发开始遍历的时候,也应该是已知顶点id的情况,这里其实是会有个前置的查询顶点id的步骤的吧。对于id生成策略的不同理应是不同的业务场景下选用会有差异,但是对于vertex或者edge来说id应该只是一个唯一标志,不同的id策略不应该产生不同的操作限制吧,就像在关系型数据库中无论一个id是数值还是字符串都不应该影响我通过id对某一条记录的修改才对。还是说这里的限制在hugegraph有其他设计考虑呢

Ckuangf avatar May 29 '21 09:05 Ckuangf

而单个更新的接口对于list和set类型的数据又是追加的方式

@Ckuangf 这个结论不是预期的,单个更新接口对任何属性都是覆盖的方式。 另外,只要知道id的情况下,即使是automatic场景也是支持更新策略的。

javeme avatar May 31 '21 03:05 javeme

而单个更新的接口对于list和set类型的数据又是追加的方式

@Ckuangf 这个结论不是预期的,单个更新接口对任何属性都是覆盖的方式。 另外,只要知道id的情况下,即使是automatic场景也是支持更新策略的。

可是目前操作起来的话,对于list和set类型的都是进行的追加,文档上面的描述也是追加,我用的是0.11.2版本的,对于automatic场景下 如果可以进行覆盖的话应该要怎么操作呢,因为我看到代码上是有专门做了这个地方的判断,如果是automatic 直接返回了

Ckuangf avatar May 31 '21 09:05 Ckuangf

而单个更新的接口对于list和set类型的数据又是追加的方式

@Ckuangf 这个结论不是预期的,单个更新接口对任何属性都是覆盖的方式。 另外,只要知道id的情况下,即使是automatic场景也是支持更新策略的。

这个更新这里还发现有个问题,如果我设置了unique索引,然后在更新顶点属性的时候,如果没有变更unique索引的字段值就会报错,好尴尬的设定,难道更新的时候是会先创建一个顶点然后进行替换么

Ckuangf avatar May 31 '21 12:05 Ckuangf

可是目前操作起来的话,对于list和set类型的都是进行的追加,文档上面的描述也是追加,我用的是0.11.2版本的,对于automatic场景下 如果可以进行覆盖的话应该要怎么操作呢,因为我看到代码上是有专门做了这个地方的判断,如果是automatic 直接返回了

  1. 关于你说的遍历方式, 可否给几个你现在使用 gremlin 的语句例子呢? 大部分查询一般是 g.V(vid).xxx() 类似的, 所以的确是已知点 id 的情况比较多, 如果点 id 完全不清楚, 那在没有索引的情况, 如何开始遍历呢? 图和 mysql 模型差别很大, 所以他的 vid/eid 都比传统关系数据库重得多, 而且它的 eid 直接由 vid 组成, 也跟关系型不同
  2. 目前单条更新的情况, 如果是 list 或 set, 的确是只有追加/删除更新的, 但是如果你只是需要覆盖集合属性, 似乎直接用普通写入就可以覆盖了, 当然这样需要你传入其他属性的旧值
  3. 批量更新之前的确不支持 automatic, 因为默认这个策略没有指定的 vid, 更新需要已知之前的 vid, 后续可以支持手动传入 vid 的 automatic 策略批量更新 (TODO)
  4. 关于你说的 unique index 的问题, 那种情况是不符合预期的, 应该是一个 bug, 你可以单独开一个 BUG issue 记录一下, 给一下具体的截图和数据例子, 方便复现和修复. 谢谢

imbajin avatar May 31 '21 17:05 imbajin

可是目前操作起来的话,对于list和set类型的都是进行的追加,文档上面的描述也是追加,我用的是0.11.2版本的,对于automatic场景下 如果可以进行覆盖的话应该要怎么操作呢,因为我看到代码上是有专门做了这个地方的判断,如果是automatic 直接返回了

  1. 关于你说的遍历方式, 可否给几个你现在使用 gremlin 的语句例子呢? 大部分查询一般是 g.V(vid).xxx() 类似的, 所以的确是已知点 id 的情况比较多, 如果点 id 完全不清楚, 那在没有索引的情况, 如何开始遍历呢? 图和 mysql 模型差别很大, 所以他的 vid/eid 都比传统关系数据库重得多, 而且它的 eid 直接由 vid 组成, 也跟关系型不同
  2. 目前单条更新的情况, 如果是 list 或 set, 的确是只有追加/删除更新的, 但是如果你只是需要覆盖集合属性, 似乎直接用普通写入就可以覆盖了, 当然这样需要你传入其他属性的旧值
  3. 批量更新之前的确不支持 automatic, 因为默认这个策略没有指定的 vid, 更新需要已知之前的 vid, 后续可以支持手动传入 vid 的 automatic 策略批量更新 (TODO)
  4. 关于你说的 unique index 的问题, 那种情况是不符合预期的, 应该是一个 bug, 你可以单独开一个 BUG issue 记录一下, 给一下具体的截图和数据例子, 方便复现和修复. 谢谢

第一点: 可能目前大部分是 g.V().hasLabel("label").xxxx,大部分场景可能都会根据这种情况查出一批顶点,之后才会像你说的那样g.V(vid).xxx()这样查询。 第二点:普通写入是指什么样的呢,文档中的 PUT http://127.0.0.1:8080/graphs/hugegraph/graph/vertices/"1:marko"?action=append,这个接口不行吧

第四点:我晚点 复现一下再提一下,之前的时候我把uniqueIndex给删掉了,不过我看了下代码,异常抛出的地方是BackendMutation 109行和GraphIndexTransaction 258行,master分支

Ckuangf avatar Jun 01 '21 01:06 Ckuangf

嗯呢, hasLabel() 实际就走了一层索引了, 不然 label 查询也会扫表, 因为 vid / eid 未知, 所以基本也就这类办法去扫表. 而且不知道你现在如何写边的, 因为你 vid 必须查询出来才知道, 所以如果要新增边, 应该也比较麻烦.

普通写入就是新增一个顶点的 api (指定 vid 和原来一致就行), PUT 在 REST-ful 语义里就是更新, 索引那个感谢反馈

imbajin avatar Jun 01 '21 03:06 imbajin

嗯呢, hasLabel() 实际就走了一层索引了, 不然 label 查询也会扫表, 因为 vid / eid 未知, 所以基本也就这类办法去扫表. 而且不知道你现在如何写边的, 因为你 vid 必须查询出来才知道, 所以如果要新增边, 应该也比较麻烦.

普通写入就是新增一个顶点的 api (指定 vid 和原来一致就行), PUT 在 REST-ful 语义里就是更新, 索引那个感谢反馈

客气客气,话说普通写入的话,我猜测目前这么做如果有unique索引的话应该是会有问题的,我看目前代码中检查的地方还是有不少,更新的时候uniqueIndex不也是出现了问题,不过这一点我没有去验证,哈哈哈。 就当前的问题而言,最好还是应该需要对hugegraph 中automatic类型的顶点不做特殊处理的,然后对于更新操作,单个更新操作和批量更新操作在更新策略的上的处理方式也不一样 给我感觉很奇怪,是不是也可以优化掉。

还有个问题是,“hasLabel() 实际就走了一层索引了, 不然 label 查询也会扫表, 因为 vid / eid 未知, 所以基本也就这类办法去扫表. 而且不知道你现在如何写边的, 因为你 vid 必须查询出来才知道, 所以如果要新增边, 应该也比较麻烦.” 针对你说的这一点,你们vid难道是代码中或者其他的地方有去维护一份么,不然总是需要先查出来的吧,感觉在查询id这一步是必不可少的呀。

Ckuangf avatar Jun 01 '21 09:06 Ckuangf

  • unique 索引是单独的问题, 就分开看就行.
  • automatic 策略和关系型 DB 的自增差别很大, 最典型的问题就是边 id 是由点 id 构成的, 比如 loader 你导入边, 但是因为 automatic 你都不知道 vid 是什么, 你必须先去读点才能知道 vid, 那自然跟其他自指定 id 的情况有很大差别
  • 单个更新和批量不是一个时间加入的, 主要是批量更新带了多种策略, 语法比单个的也要更复杂一些. 以后可以考虑统一
  • 你可以看看主键模式, 或者自定义数值/字符串, 它就不需要先单独查一次 vid 的, 因为 vid 业务自己就知道. (比如 8.8.8.8 作为 vid 直接做查询他的邻居)

imbajin avatar Jun 01 '21 14:06 imbajin

  • unique 索引是单独的问题, 就分开看就行.
  • automatic 策略和关系型 DB 的自增差别很大, 最典型的问题就是边 id 是由点 id 构成的, 比如 loader 你导入边, 但是因为 automatic 你都不知道 vid 是什么, 你必须先去读点才能知道 vid, 那自然跟其他自指定 id 的情况有很大差别
  • 单个更新和批量不是一个时间加入的, 主要是批量更新带了多种策略, 语法比单个的也要更复杂一些. 以后可以考虑统一
  • 你可以看看主键模式, 或者自定义数值/字符串, 它就不需要先单独查一次 vid 的, 因为 vid 业务自己就知道. (比如 8.8.8.8 作为 vid 直接做查询他的邻居)

其实我们说的id问题大部分是使用场景的时候,你说的可能是更偏向于初始创建阶段,这个时候如果已知的话 会比较方便的,我说的是后面查询修改阶段,这时候其实差别不大, 我现在是先改成了custom策略了。谢谢了

Ckuangf avatar Jun 02 '21 01:06 Ckuangf