Neo4J Cypher获得组合

时间:2017-01-23 07:13:49

标签: neo4j cypher

我正在尝试找到一种将组合分组在一起的方法。 假设我们有类型人,爱好,地方,城市的节点。假设图表具有以下关系(合并)

CREATE
  (Joe:Person {name: 'Joe'}),
  (hike:Hobby {name: 'hike'}),
  (eat:Hobby {name: 'eat'}),
  (drink:Hobby {name: 'drink'}),
  (Mountain:Place {name: 'Mountain'}),
  (Lake:Place {name: 'Lake'}),
  (DavesBarGrill:Place {name: 'Daves BarGrill'}),
  (Diner:Place {name: 'Diner'}),
  (Lounge:Place {name: 'Lounge'}),
  (DiveBar:Place {name: 'Dive Bar'}),
  (Joe)-[:likes]->(hike),
  (Joe)-[:likes]->(eat),
  (Joe)-[:likes]->(drink),
  (hike)-[:canDoAt]->(Mountain),
  (hike)-[:canDoAt]->(Lake),
  (eat)-[:canDoAt]->(DavesBarGrill),
  (eat)-[:canDoAt]->(Diner),
  (drink)-[:canDoAt]->(Lounge),
  (drink)-[:canDoAt]->(DiveBar)

有一天,他计划做一次他的爱好,有8个地方可以徒步,吃喝。我希望能够在查询中捕获它。

天真的方法,

MATCH (p:Person)-[:likes]->(h:Hobby)-[:canDoAt]->(pl:Place)
RETURN p, h, pl

最多只能按人和爱好进行分组,这会导致相同爱好的行被组合在一起。我想要的是以某种方式按组合分组,即:

//Joe Combo 1// Joe,hike,Mountain
                Joe,eat,Daves
                Joe,drink,Lounge
//Joe Combo 2// Joe,hike,Lake  
                Joe,eat,Daves
                Joe,drink,Lounge 

有没有办法以某种方式为所有路径匹配分配一个数字,然后使用该分配进行排序?

2 个答案:

答案 0 :(得分:2)

这是一个非常好的问题!我还没有完整的解决方案,但有些想法:正如Martin Preusse所说,我们希望生成笛卡尔积。

这很难,但你可以通过大量黑客攻击来解决它,包括使用双重缩减:

WITH [['a', 'b'], [1, 2, 3], [true, false]] AS hs
WITH hs, size(hs) AS numberOfHobbys, reduce(acc = 1, h in hs | acc * size(h)) AS numberOfCombinations, extract(h IN hs | length(h)) AS hLengths
WITH hs, hLengths, numberOfHobbys, range(0, numberOfCombinations-1) AS combinationIndexes
UNWIND combinationIndexes AS combinationIndex
WITH
  combinationIndex,
  reduce(acc = [], i in range(0, numberOfHobbys-1) |
    acc + toInt(combinationIndex/(reduce(acc2 = 1, j in range(0, i-1) | acc2 * hLengths[j]))) % hLengths[i]
  ) AS indices,
  reduce(acc = [], i in range(0, numberOfHobbys-1) |  
    acc + reduce(acc2 = 1, j in range(0, i-1) | acc2 * hLengths[j])
  ) AS multipliers,
  reduce(acc = [], i in range(0, numberOfHobbys-1) |
    acc + hs[i][
      toInt(combinationIndex/(reduce(acc2 = 1, j in range(0, i-1) | acc2 * hLengths[j]))) % hLengths[i]
    ]
  ) AS combinations
RETURN combinationIndex, indices, multipliers, combinations

这个想法如下:我们乘以潜在值的数量,例如:对于['a', 'b'], [1, 2, 3], [true, false],我们使用查询中的第一个n = 2×3×2 = 12来计算reduce。然后我们从0迭代到n-1,并使用公式a×1 + b×2 + c×6为每个数字指定一行,其中a,b,c索引相应的值,因此所有都是非负整数和{{1} },a < 2b < 3

c < 2

结果是:

0×1 + 0×2 + 0×6 = 0
1×1 + 0×2 + 0×6 = 1
0×1 + 1×2 + 0×6 = 2
1×1 + 1×2 + 0×6 = 3
0×1 + 2×2 + 0×6 = 4
1×1 + 2×2 + 0×6 = 5
0×1 + 0×2 + 1×6 = 6
1×1 + 0×2 + 1×6 = 7
0×1 + 1×2 + 1×6 = 8
1×1 + 1×2 + 1×6 = 9
0×1 + 2×2 + 1×6 = 10
1×1 + 2×2 + 1×6 = 11

因此,对于您的问题,查询可能如下所示:

╒════════════════╤═════════╤═══════════╤═════════════╕
│combinationIndex│indices  │multipliers│combinations │
╞════════════════╪═════════╪═══════════╪═════════════╡
│0               │[0, 0, 0]│[1, 2, 6]  │[a, 1, true] │
├────────────────┼─────────┼───────────┼─────────────┤
│1               │[1, 0, 0]│[1, 2, 6]  │[b, 1, true] │
├────────────────┼─────────┼───────────┼─────────────┤
│2               │[0, 1, 0]│[1, 2, 6]  │[a, 2, true] │
├────────────────┼─────────┼───────────┼─────────────┤
│3               │[1, 1, 0]│[1, 2, 6]  │[b, 2, true] │
├────────────────┼─────────┼───────────┼─────────────┤
│4               │[0, 2, 0]│[1, 2, 6]  │[a, 3, true] │
├────────────────┼─────────┼───────────┼─────────────┤
│5               │[1, 2, 0]│[1, 2, 6]  │[b, 3, true] │
├────────────────┼─────────┼───────────┼─────────────┤
│6               │[0, 0, 1]│[1, 2, 6]  │[a, 1, false]│
├────────────────┼─────────┼───────────┼─────────────┤
│7               │[1, 0, 1]│[1, 2, 6]  │[b, 1, false]│
├────────────────┼─────────┼───────────┼─────────────┤
│8               │[0, 1, 1]│[1, 2, 6]  │[a, 2, false]│
├────────────────┼─────────┼───────────┼─────────────┤
│9               │[1, 1, 1]│[1, 2, 6]  │[b, 2, false]│
├────────────────┼─────────┼───────────┼─────────────┤
│10              │[0, 2, 1]│[1, 2, 6]  │[a, 3, false]│
├────────────────┼─────────┼───────────┼─────────────┤
│11              │[1, 2, 1]│[1, 2, 6]  │[b, 3, false]│
└────────────────┴─────────┴───────────┴─────────────┘

这看起来像这样:

MATCH (p:Person)-[:likes]->(h:Hobby)-[:canDoAt]->(pl:Place)
WITH p, h, collect(pl.name) AS places
WITH p, collect(places) AS hs
WITH hs, size(hs) AS numberOfHobbys, reduce(acc = 1, h in hs | acc * size(h)) AS numberOfCombinations, extract(h IN hs | length(h)) AS hLengths
WITH hs, hLengths, numberOfHobbys, range(0, numberOfCombinations-1) AS combinationIndexes
UNWIND combinationIndexes AS combinationIndex
WITH
  reduce(acc = [], i in range(0, numberOfHobbys-1) |
    acc + hs[i][
      toInt(combinationIndex/(reduce(acc2 = 1, j in range(0, i-1) | acc2 * hLengths[j]))) % hLengths[i]
    ]
  ) AS combinations
RETURN combinations

显然,我们也希望得到他/她的爱好的人和名字:

╒════════════════════════════════════╕
│combinations                        │
╞════════════════════════════════════╡
│[Diner, Lounge, Lake]               │
├────────────────────────────────────┤
│[Daves BarGrill, Lounge, Lake]      │
├────────────────────────────────────┤
│[Diner, Dive Bar, Lake]             │
├────────────────────────────────────┤
│[Daves BarGrill, Dive Bar, Lake]    │
├────────────────────────────────────┤
│[Diner, Lounge, Mountain]           │
├────────────────────────────────────┤
│[Daves BarGrill, Lounge, Mountain]  │
├────────────────────────────────────┤
│[Diner, Dive Bar, Mountain]         │
├────────────────────────────────────┤
│[Daves BarGrill, Dive Bar, Mountain]│
└────────────────────────────────────┘

结果:

MATCH (p:Person)-[:likes]->(h:Hobby)-[:canDoAt]->(pl:Place)
WITH p, h, collect([h.name, pl.name]) AS places
WITH p, collect(places) AS hs
WITH p, hs, size(hs) AS numberOfHobbys, reduce(acc = 1, h in hs | acc * size(h)) AS numberOfCombinations, extract(h IN hs | length(h)) AS hLengths
WITH p, hs, hLengths, numberOfHobbys, range(0, numberOfCombinations-1) AS combinationIndexes
UNWIND combinationIndexes AS combinationIndex
WITH
  p, reduce(acc = [], i in range(0, numberOfHobbys-1) |
    acc + [hs[i][
      toInt(combinationIndex/(reduce(acc2 = 1, j in range(0, i-1) | acc2 * hLengths[j]))) % hLengths[i]
    ]]
  ) AS combinations
RETURN p, combinations

我可能会过度思考这个问题,所以欢迎提出任何意见。

一个重要的评论:纯粹Cypher这么复杂的事实可能是一个好的迹象,表明您最好不要从客户端应用程序计算它。

答案 1 :(得分:1)

我很确定你不能在密码中做到这一点。您正在寻找的是按人和爱好分组的所有地方的笛卡尔积。

A: [ [Joe, hike, Mountain], [Joe, hike, Lake] ]
B: [ [Joe, eat, Daves], [Joe, eat, Diner] ]
C: [ [Joe, drink, Lounge], [Joe, drink, Bar] ]

您正在寻找A x B x C

据我所知,你不能像这样把Cypher的回报分组。您应该返回所有人,爱好,放置行,并在Python脚本中执行此操作,您将在其中构建分组集并计算笛卡尔积。

问题是你会在越来越多的爱好和场所中获得很多组合。