如何在图形中找到共享共同特征的组节点

时间:2016-08-06 23:42:04

标签: graph neo4j

让我们说我有一个图表,将食物与酸味,甜味,辛辣味,浓郁味等特性联系起来......

如何查询图表,为我提供一组与每种可能的特征组合相匹配的食品。

即。

  • 所有甜而辛辣的食物
  • 所有酸甜的食物
  • 所有甜,酸,辣的食物

图形元组如下所示:

F1 > Spicy
F1 > Sweet
F2 > Sour
F2 > Sweet
F3 > Sour
...

查询应该输出与每种可能的特征组合相匹配的食物组。

Spicy => F1, F2, F3, F4, F5
Spicy & Sweet => F1, F3, F5
Spicy & Sweet & Sour => F3
Spicy & Sweet & Sour @ Tangy => F3

Spicy & Sour => ...
Spicy & Sour & Tangy => ...

Spicy & Tangy => ...

2 个答案:

答案 0 :(得分:1)

1)假设以下输入:

UNWIND [ {name: 'F1', traits: ['Spicy', 'Sweet'                ]},
         {name: 'F2', traits: ['Sour' , 'Sweet'                ]},
         {name: 'F3', traits: ['Tangy', 'Sour', 'Spicy'        ]},
         {name: 'F4', traits: ['Tangy', 'Sour', 'Spice', 'Tart']}  ] AS food
MERGE (F:Food {name: food.name}) WITH F, food
UNWIND food.traits as trait
  MERGE (T:Trait {name: trait})
  MERGE (F)-[:hasTrait]->(T)
RETURN F, T

2)现在我们需要获得所有特征组合。为此,我们需要apoc library

MATCH (T:Trait) 
WITH collect(T) as traits

// Here we count the number of combinations of traits as a power of two
WITH traits, toInt(round(exp( log(2) * size(traits) )))-1 as combCount

// Go through all the combinations
UNWIND RANGE(1, combCount) as combIndex
 UNWIND RANGE(0, size(traits)-1 ) as p

 // Check whether the trait is present in the combination
 CALL apoc.bitwise.op( toInt(round( exp(log(2) * p) )),'&',combIndex) YIELD value
 WITH combIndex, collect(CASE WHEN value > 0 THEN traits[p] END) as comb

 // Return all combinations of traits
 RETURN comb ORDER BY size(comb)

3)现在,对于每个组合,我们需要找到食物的交集点:

MATCH (T:Trait) 
WITH collect(T) as traits

// Here we count the number of combinations of traits as a power of two
WITH traits, toInt(round(exp( log(2) * size(traits) )))-1 as combCount

// Go through all the combinations
UNWIND RANGE(1, combCount) as combIndex
 UNWIND RANGE(0, size(traits)-1 ) as p

 // Check whether the trait is present in the combination
 CALL apoc.bitwise.op( toInt(round( exp(log(2) * p) )),'&',combIndex) YIELD value
 WITH combIndex, collect(CASE WHEN value > 0 THEN traits[p] END) as comb

// Take foods for the first trait:
WITH comb, head(comb) as ft
  OPTIONAL MATCH (ft)<-[:hasTrait]-(F:Food)

// We find the intersection of each food with other traits
WITH comb, collect(F) as testFoods
  UNWIND testFoods as food
    UNWIND comb as trait
      OPTIONAL MATCH p = (food)-[:hasTrait]->(trait)
      WITH comb, food, trait, size(collect(p)) as pairs

      // Check that the number of crossings for food with traits 
      // for each combination of the same number of traits
      WITH comb, food, collect(CASE WHEN pairs > 0 THEN trait END) as pairs
      WITH comb, collect(CASE WHEN size(pairs)=size(comb) THEN food END) as pairs

      // Return combinations where there is a common food
      WITH comb, pairs WHERE size(pairs)>0
RETURN comb, pairs ORDER BY size(comb)

答案 1 :(得分:0)

请记住,neo4j查询输出的格式是为具有列的行设计的,而不是您想要的输出格式,所以这会让事情变得有点棘手。

我强烈建议您在每一行输出食物,使用布尔列作为每个不同简单特征的成员资格,然后在您的应用程序代码中,将食物对象插入每个特征的集合中。然后使用应用程序逻辑,您可以计算所需的所有特征组合,并执行集合交集以生成它们。

这将使neo4j查询变得非常简单:

MATCH (f:Food)
WITH f
RETURN f.name, EXISTS((f)-[:IS]->(:Trait{name:'tangy'})) AS tangy,
EXISTS((f)-[:IS]->(:Trait{name:'sweet'})) AS sweet, 
EXISTS((f)-[:IS]->(:Trait{name:'sour'})) AS sour, 
EXISTS((f)-[:IS]->(:Trait{name:'spicy'})) AS spicy 

也就是说,如果您决定使用neo4j查询完成整个事情,那么它将会变得混乱,因为您需要跟踪并生成您需要的所有组合。对于交叉口操作,您需要安装APOC procedures library

在我看来,最好的开始是根据每个特征创建一组食物节点。

MATCH (f:Food)-[:IS]->(:Trait{name:'spicy'})
WITH COLLECT(f) AS spicyFood
MATCH (f:Food)-[:IS]->(:Trait{name:'sour'})
WITH COLLECT(f) AS sourFood, spicyFood
MATCH (f:Food)-[:IS]->(:Trait{name:'sweet'})
WITH COLLECT(f) AS sweetFood, sourFood, spicyFood
MATCH (f:Food)-[:IS]->(:Trait{name:'tangy'})
WITH COLLECT(f) AS tangyFood, sweetFood, sourFood, spicyFood

现在您拥有了这些,您可以与您感兴趣的每个组合进行交叉。

CALL apoc.coll.intersection(tangyFood, sweetFood) YIELD value AS tangySweetFood
CALL apoc.coll.intersection(tangyFood, sourFood) YIELD value AS tangySourFood
CALL apoc.coll.intersection(tangyFood, spicyFood) YIELD value AS tangySpicyFood 
CALL apoc.coll.intersection(tangySweetFood, sourFood) YIELD value AS tangySweetSourFood 
CALL apoc.coll.intersection(tangySweetFood, spicyFood) YIELD value AS tangySweetSpicyFood 
CALL apoc.coll.intersection(tangySourFood, spicyFood) YIELD value AS tangySourSpicyFood 
CALL apoc.coll.intersection(tangySweetSourFood, spicyFood) YIELD value AS tangySweetSourSpicyFood 
CALL apoc.coll.intersection(sweetFood, sourFood) YIELD value AS sweetSourFood
CALL apoc.coll.intersection(sweetFood, spicyFood) YIELD value AS sweetSpicyFood
CALL apoc.coll.intersection(sweetSourFood, spicyFood) YIELD value AS sweetSourSpicyFood
CALL apoc.coll.intersection(sourFood, spicyFood) YIELD value AS sourSpicyFood
RETURN tangyFood, sweetFood, sourFood, spicyFood,
tangySweetFood, tangySourFood, tangySpicyFood, 
tangySweetSourFood, tangySweetSpicyFood, tangySourSpicyFood,
tangySweetSourSpicyFood,
sweetSourFood, sweetSpicyFood, 
sweetSourSpicyFood, 
sourSpicyFood