2016-08-06 64 views
1

Lets sagen, dass ich eine Grafik, die Nahrungsmittel zu Eigenschaften wie sauer, süß, würzig, würzig, ...Wie finde ich Gruppen Knoten gemeinsame Merkmale in einem Diagramm teilen

bezieht Wie kann ich die Grafik abfragen um mir eine Reihe von Nahrungsmitteln zu geben, die jeder möglichen Kombination von Merkmalen entsprechen.

dh

  • alle Lebensmittel, die sind süß und würzig
  • alle Lebensmittel, die sind süß und sauer
  • alle Lebensmittel, die süß, sauer sind, und würzigen

Die grafische Darstellung Tupeln würde wie folgt aussehen:

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

Die Abfrage sollte Gruppen von Nahrungsmitteln ausgeben, die mit jeder möglichen Kombination von Merkmalen übereinstimmen.

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 => ... 
+0

So zu klären, Züge sind Knoten, und es gibt eine Beziehung zwischen Lebensmitteln an ihren Eigenschaften? So etwas wie (: Essen {Name: 'F1'}) - [: IS] -> (: Merkmal {Name: 'Spicy'})? – InverseFalcon

Antwort

0

Beachten Sie, dass das Format der Neo4j Abfrageausgabe für die Zeilen mit Spalten ausgelegt ist, nicht das gewünschte Ausgabeformat, so dass dies macht die Dinge ein wenig kompliziert.

Ich würde empfehlen, nur Ihre Lebensmittel in jeder Zeile auszugeben, mit booleschen Spalten für die Mitgliedschaft in jedem einzelnen einfachen Merkmal, dann in Ihrem Anwendungscode, fügen Sie die Lebensmittel-Objekte in Sätze für jedes Merkmal. Dann können Sie mithilfe der Anwendungslogik alle möglichen Kombinationen von Merkmalen, die Sie benötigen, berechnen und einen Schnittpunkt festlegen, um sie zu generieren.

Dies würde die Neo4j Abfrage sehr einfach machen:

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 

Das heißt, wenn man die ganze Sache mit einem Neo4j Abfrage zu tun entschlossen sind, es chaotisch sein wird, da Sie brauchen Verfolgen und generieren Sie alle Kombinationen, die Sie selbst benötigen. Für Kreuzungsoperationen sollten Sie die APOC procedures library installieren.

Es scheint mir, dass der beste Start ist, Sets von Essen Knoten nach jedem einzelnen Merkmal zu erstellen.

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 

Jetzt, wo Sie diese haben, können Sie Ihre Kreuzungen tun mit jeder Kombination der Sie interessiert

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 
1

1) seien folgende Eingänge:.

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) Jetzt müssen wir alle Kombinationen von Eigenschaften erhalten. Dafür brauchen wir 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) Nun, für jede Kombination müssen wir die Kreuzung für Nahrung finden:

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)