如何在 neo4j 中聚合列表中的元素

How to aggregate across elements in list in neo4j

我是 neo4j 和密码查询的完全初学者。我有一个场景,其中两个 Person 节点 JamesKaren 在它们之间有 3 个节点。所有节点之间的关系有一个属性,叫做weight

CREATE 
(j:Person { name: "James" }),
(m:Person { name: "Mary" }),
(d:Person { name: "David" }),
(e:Person { name: "Emily" }),
(t:Person { name: "Todd" }),
(x:Person { name: "Xavier" }),
(k:Person { name: "Karen" }),

(j)-[:KNOWS{weight: 32}]->(m),
(j)-[:KNOWS{weight: 10}]->(d),
(j)-[:KNOWS{weight: 50}]->(e),
(j)-[:KNOWS{weight: 86}]->(t),
(j)-[:KNOWS{weight: 86}]->(x),

(d)-[:KNOWS{weight: 12}]->(e),

(k)-[:KNOWS{weight: 76}]->(e),
(k)-[:KNOWS{weight: 8}]->(t),
(k)-[:KNOWS{weight: 2}]->(x)

我想对这些认识 JamesKaren 的人进行排名,使用 属性 关系的计算分数,定义为 (x - min(x)/max(x) - min(x),一些规范化形式,即

MATCH (:Person{name:'James'})-[:KNOWS]->(:Person)<-[r1:KNOWS]-(:Person{name: 'Karen'})
WITH collect(r1.weight) as a
WITH  apoc.coll.min(a) as min_val, apoc.coll.max(a) as max_val
MATCH (:Person{name:'James'})-[:KNOWS]->(p:Person)<-[r2:KNOWS]-(:Person{name: 'Karen'})
WITH p, r2, (r2.weight - min_val)/(max_val - min_val) as score
RETURN p, r2, score
ORDER BY score DESC

╒═════════════════╤═════════════╤═══════╕
│"p"              │"r2"         │"score"│
╞═════════════════╪═════════════╪═══════╡
│{"name":"Emily"} │{"weight":76}│1      │
├─────────────────┼─────────────┼───────┤
│{"name":"Xavier"}│{"weight":2} │0      │
├─────────────────┼─────────────┼───────┤
│{"name":"Todd"}  │{"weight":8} │0      │
└─────────────────┴─────────────┴───────┘

有没有更好的方法来做到这一点而不必重复寻找匹配节点的过程,即 MATCH (:Person{name:'James'})-[:KNOWS]->(p:Person)<-[r2:KNOWS]-(:Person{name: 'Karen'})

更新 这是另一种尝试

MATCH (:Person{name:'James'})-[:KNOWS]->(p:Person)<-[r:KNOWS]-(:Person{name: 'Karen'})
WITH r, apoc.map.fromLists(["name", "r_weight"],[p.name, r.weight]) as person
WITH collect(person) as people, collect(r.weight) as w
WITH people, apoc.coll.min(w) as min_val, apoc.coll.max(w) as max_val 
UNWIND people as pp
WITH pp, (pp.r_weight - min_val)/(max_val - min_val) as score
RETURN pp, score
ORDER BY score DESC

╒══════════════════════════════╤═══════╕
│"pp"                          │"score"│
╞══════════════════════════════╪═══════╡
│{"name":"Emily","r_weight":76}│1      │
├──────────────────────────────┼───────┤
│{"name":"Xavier","r_weight":2}│0      │
├──────────────────────────────┼───────┤
│{"name":"Todd","r_weight":8}  │0      │
└──────────────────────────────┴───────┘

有没有比我粗暴的方法更干净的方法?

我通常UNWIND 上一个RANGE 来处理并行列表。我就是这样做的。

MATCH (:Person{name:'James'})-[r1:KNOWS]->(p:Person)<-[r2:KNOWS]-(:Person{name: 'Karen'})
WITH p, r1.weight + r2.weight AS weight
WITH COLLECT(p) AS people, 
COLLECT(weight) as weights,
max(weight) AS maxWeight, 
min(weight) AS minWeight
UNWIND RANGE(0, SIZE(people)-1) AS idx
RETURN people[idx].name as person, 
weights[idx] AS weight,
toFloat(weights[idx] - minWeight)/(maxWeight - minWeight) AS score
ORDER BY score desc