问题描述
我有一个存储不同类型实体的图形数据库,并且正在构建一个API以从图形中获取实体。但是,这有点复杂,因为对于每种类型的实体,都有一组规则用于获取相关实体以及原始实体。 为此,我使用了聚合步骤将要提取的所有相关实体聚合到集合中。
一个附加要求是获取一批实体(和相关实体)。我将通过更改获取实体以使用has
并将实体映射到每个找到的实体的P.within
步骤来实现。
如果我继续获取单个实体,则此方法有效,但是如果我要获取两个实体,则我的结果集对于第一个实体将是正确的,但是第二个的结果集将包含第一个实体的结果及其自身的结果。
我认为这是因为第二个集合将简单地从第一个集合添加到集合集合中,因为聚合键是相同的。
我还没有找到清除第一个和第二个之间的集合的任何方法,也没有找到动态聚合副作用密钥的任何方法。
代码:
return graph.traversal().V()
.hasLabel(ENTITY_LABEL)
.has("entity_ref",P.within(entityRefs)) // entityRefs is a list of entities I am looking for
.flatMap(
__.aggregate("match")
.sideEffect(
// The logic that applies the rules lives here. It will add items to "match" collection.
)
.select("match")
.fold()
)
.toStream()
...
结果应为实体列表的列表,其中外部列表中的第一个实体列表包含entityRefs
中第一个实体的结果,而第二个实体列表包含{中的第二个实体的结果{1}}。
示例:
我想获取实体引用entityRefs
和A
及其相关实体的顶点。
假设我期望结果为B
,但我得到的结果为[[A,C],[B,D,E]]
(第二个结果包含第一个结果)。
问题:
编辑: 这是一个微型问题的例子。该图的设置如下:
entityRef
我想获取一批实体,给定它们的ID并获取相关实体。应该返回哪些相关实体是原始实体类型的函数。 我当前的查询是这样的(在此示例中稍作修改):
g.addV('entity').property('id',1).property('type','customer').as('1').
addV('entity').property('id',2).property('type','email').as('2').
addV('entity').property('id',6).property('type','customer').as('6').
addV('entity').property('id',3).property('type','product').as('3').
addV('entity').property('id',4).property('type','subLocation').as('4').
addV('entity').property('id',7).property('type','location').as('7').
addV('entity').property('id',5).property('type','productGroup').as('5').
addE('AKA').from('1').to('2').
addE('AKA').from('2').to('6').
addE('HOSTED_AT').from('3').to('4').
addE('LOCATED_AT').from('4').to('7').
addE('PART_OF').from('3').to('5').iterate()
如果我仅获取一个实体,我将获得正确的结果。对于g.V().
hasLabel('entity').
has('id',P.within(1,3)).
flatMap(
aggregate('match').
sideEffect(
choose(values('type')).
option('customer',both('AKA').
has('type',P.within('email','phone')).
sideEffect(
has('type','email').
aggregate('match')).
both('AKA').
has('type','customer').
aggregate('match')).
option('product',bothE('HOSTED_AT','PART_OF').
choose(label()).
option('PART_OF',bothV().
has('type',P.eq('productGroup')).
aggregate('match')).
option('HOSTED_AT',P.eq('subLocation')).
aggregate('match').
both('LOCATED_AT').
has('type',P.eq('location')).
aggregate('match')))
).
select('match').
unfold().
dedup().
values('id').
fold()
).
toList()
我得到id: 1
,对于[1,2,6]
我得到id: 3
。但是,当我同时获取两者时,会得到:
[3,5,4,7]
第一个结果正确,但是第二个结果包含两个ID的结果。
解决方法
您可以利用group().by(key).by(value)
(不是很好的记录,说实话,但遍历步骤似乎很强大)。
这样一来,您就可以删除aggregate()
副作用步骤,该步骤会给您带来麻烦。作为一种选择,可以将与遍历匹配的多个顶点收集到列表中,我使用了union()
。
使用您发布的图表的示例(为简便起见,我仅包括“客户”选项):
g.V().
hasLabel('entity').
has('id',P.within(1,3)).
<String,List<Entity>>group()
.by("id")
.by(choose(values("type"))
.option('customer',union(
identity(),both('AKA').has('type','email'),within('email','phone')).both('AKA').has('type','customer'))
.map((traversal) -> new Entity(traversal.get())) //Or whatever business class you have
.fold() //This is important to collect all 3 paths in the union together
.option('product',union()))
.next()
这种遍历具有明显的缺点,即代码更加冗长。它声明它将两次从客户那里跳过“ AKA”。您的遍历只声明了一次。
但是,它确实使by(value)
步骤的group()
部分在不同键之间分开。这就是我们想要的。