Gremlin 中的决策树查询
Posted
技术标签:
【中文标题】Gremlin 中的决策树查询【英文标题】:Decision Tree query in Gremlin 【发布时间】:2021-12-28 17:04:02 【问题描述】:我已经简化了一个决策图。它以开始顶点开始,以决策结束。我的目标是在行驶不同的路径(到达决策顶点)时计算得分的总和(与顶点相关的得分)。
Graph 的输入是 JSON。
顶点之间的边包含可以从输入 JSON 中检查的变量和值。
输入 JSON 示例: "age":45,"income_source":"job"
输出是分数的总和 [10 + 15 + 22] = 47
在 Neo4j 中,Cypher 查询允许您将 JSON 输入作为查询参数传递,但我不知道如何在 Gremlin 中完成。
图表链接:https://gremlify.com/nwgxqs5h7zh/
g.addV('begin').as('beg').
addV('decision').property('score',0).property('decision_code',"minor").as('dec0').
addV('age').property('score',10).as('age10').
addV('age').property('score',20).as('age20').
addV('salary').property('score',15).as('sal15').
addV('salary').property('score',25).as('sal25').
addV('salary').property('score',18).as('sal18').
addV('salary').property('score',30).as('sal30').
addV('decision').property('score',22).property('decision_code',"decision_22").as('dec22').
addV('decision').property('score',45).property('decision_code',"decision_45").as('dec45').
addV('decision').property('score',18).property('decision_code',"decision_18").as('dec18').
addV('decision').property('score',30).property('decision_code',"decision_30").as('dec30').
addE('relation').property('var',"age").property('val',"").property('min',"10").property('max',"18").from('beg').to('dec0').
addE('relation').property('var',"age").property('val',"").property('min',"19").property('max',"48").from('beg').to('age10').
addE('relation').property('var',"age").property('val',"").property('min',"49").property('max',"80").from('beg').to('age20').
addE('relation').property('var',"income_source").property('val',"job").property('min',"-1").property('max',"-1").from('age10').to('sal15').
addE('relation').property('var',"income_source").property('val',"buisness").property('min',"-1").property('max',"-1").from('age10').to('sal25').
addE('relation').property('var',"income_source").property('val',"job").property('min',"-1").property('max',"-1").from('age20').to('sal18').
addE('relation').property('var',"income_source").property('val',"buisness").property('min',"-1").property('max',"-1").from('age20').to('sal30').
addE('relation').property('var',"").property('val',"").property('min',"-1").property('max',"-1").from('sal15').to('dec22').
addE('relation').property('var',"").property('val',"").property('min',"-1").property('max',"-1").from('sal25').to('dec45').
addE('relation').property('var',"").property('val',"").property('min',"-1").property('max',"-1").from('sal18').to('dec18').
addE('relation').property('var',"").property('val',"").property('min',"-1").property('max',"-1").from('sal30').to('dec30')
lt, gt, inside, between
谓词存在问题。它只接受数字而不接受任何计算为数字的东西。
g.inject(['val1':10,'val2':15]).as('data').V().
where(select('data').select('val1').is(lt(select('data').values('val2'))))
上面的查询失败Cannot compare '10' (Integer) and '[SelectOneStep(last,data), PropertiesStep([val2],value)]'...
由于这个问题下面的查询也失败了。
g.withSack(0).inject(['age':45,'source':'job']).as('data').
V().hasLabel('begin').
repeat(outE().as('e').where(select('data').select(select('e').values('var')).is(eq(select('e').values('val')).or(inside(select('e').values('min'),select('e').values('max'))))).inV().sack(sum).by('score')).
until(hasLabel('decision')).project('final_score','path').by(sack()).by(path())
如果这个问题可以用不同的方式建模以达到相同的输出分数,请告诉我
感谢您的宝贵时间。
【问题讨论】:
Tinkerpop 在“lt, gt, inside, between” predicate 中比较不同数据类型(字符串和整数)时抛出错误。在像 Neo4j 这样的情况下,它应该评估为 false。不确定 AWS Neptune、Janus 图、Cosmos DB 或其他在不匹配数据类型比较方面的表现 对于启用了 Apache TinkerPop 的图,通常不认为具有相同键名但值不同类型(例如整数和字符串)的属性是一种好的做法。即使在这种情况下返回false
也不理想(实际上它确实不正确),例如在 Groovy 中,'a' < 1
是 false
但 'a' > 1
是 true
。最好对图中的数据进行归一化处理。
没错,相同的键名具有相同的数据类型是有意义的。
【参考方案1】:
我已将输入 JSON 转换为列表。此列表中元素的顺序很重要。它决定了遍历的级别 比较列表中的哪个元素。
g.withSack(0).
inject(["age", 45, "income_source", "job"]).as("input").
# initialized sack and input List
V().hasLabel("begin").
outE().as('a').local(and(
select("input").unfold().range(0, 1).as("temp").
select("a").values("var").where(eq("temp")), # FILTER property "var"
select("input").unfold().range(1, 2).as("temp").
select("a").values("max").where(gte("temp")).
select("a").values("min").where(lte("temp")))). # FILTER by age from input.
inV().sack(sum).by("score").
outE().as("b").local(and(
select("input").unfold().range(2, 3).as("temp").
select("b").values("var").where(eq("temp")), # FILTER property "var"
select("input").unfold().range(3, 4).as("temp").
select("b").values("val").where(eq("temp")))). # FILTER property val
inV().sack(sum).by("score").
out().sack(sum).by("score").
sack()
【讨论】:
这工作正常,但有一个问题。当图形很大(40-50 个顶点),json 有 30-40 个键时,保持输入顺序可能真的很困难,而且图形的任何更改(修改顶点之间的路径)也需要更改查询。【参考方案2】:您可以将inject
映射到 Gremlin 查询中,该查询与您的 JSON 文档的形状基本相同。查询第一部分的基本构建块如下所示,我使用您的数据和 TinkerGraph 对其进行了测试。
gremlin> g.inject(['age':45,'source':'job']).as('data').
......1> V().hasLabel('begin').
......2> outE().as('e1').
......3> where(gte('e1')).
......4> by(select('data').select('age')).
......5> by('min').
......6> where(lte('e1')).
......7> by(select('data').select('age')).
......8> by('max').
......9> valueMap()
==>[min:19,max:48,var:age]
下一步是找到带有job
标签的边。
gremlin> g.inject(['age':45,'source':'job']).as('data').
......1> V().hasLabel('begin').
......2> outE().as('e1').
......3> where(gte('e1')).
......4> by(select('data').select('age')).
......5> by('min').
......6> where(lte('e1')).
......7> by(select('data').select('age')).
......8> by('max').
......9> inV().
.....10> outE().as('e2').
.....11> where(eq('e2')).
.....12> by(select('data').select('source')).
.....13> by('val').valueMap()
==>[val:job,var:income_source]
我们现在需要做的就是遍历最后一个节点并计算总和。
gremlin> g.withSack(0).
......1> inject(['age':45,'source':'job']).as('data').
......2> V().hasLabel('begin').
......3> outE().as('e1').
......4> where(gte('e1')).
......5> by(select('data').select('age')).
......6> by('min').
......7> where(lte('e1')).
......8> by(select('data').select('age')).
......9> by('max').
.....10> inV().
.....11> sack(sum).
.....12> by('score').
.....13> outE().as('e2').
.....14> where(eq('e2')).
.....15> by(select('data').select('source')).
.....16> by('val').
.....17> inV().
.....18> sack(sum).
.....19> by('score').
.....20> out().
.....21> sack(sum).
.....22> by('score').
.....23> sack()
==>47
【讨论】:
这在 Gremlify gremlify.com/k0auxbd8rim 中有一些问题。然而,对于大 json(约 50 个键),计算总和的查询可能会变得很长。我认为在 gremlin 中完成它不会像在 neo4j 中那样简单。在 neo4j 中,它类似于 ` WITH json_object as object MATCH p = (:beg)-[*]->(final:decision) WHERE ALL (r in Relations(p) WHERE (r.val=object[r.var ]) OR (r.min 如果您可以将该信息添加到原始问题中,将会很有帮助。您应该能够在上面的示例中添加一个repeat
步骤,以将树遍历到任意深度。
通过您的查询,我可以构建类似的东西,但受到 Tinkerpop 限制,在有关它的问题中更新它。以上是关于Gremlin 中的决策树查询的主要内容,如果未能解决你的问题,请参考以下文章