2017-02-01 3 views
1

In meinem Neo4j/Spring Data Neo4j 4 Projekt Ich habe eine Entitäten: ProductNeo4j Cypher Abfrage für Knoten, basierend auf Eigenschaften zu finden Deltas

jeder Product hat eine Integer Eigenschaft - price

Zum Beispiel habe ich eine folgende haben Produkte mit Preisen:

Product1.price = 100 
Product2.price = 305 
Product3.price = 10000 
Product4.price = 1000 
Product5.price = 220 

Produkte sind nicht miteinander mit Beziehungen verbunden.

Ich brauche basierend auf dem anfänglichen Preiswert (Cypher-Abfrageparameter) einen Satz (Pfad) von Produkten, die sich durch ein maximales Preisdelta voneinander unterscheiden (Cypher-Abfrageparameter).

Zum Beispiel muss ich alle Produkte in Neo4j Datenbank Ausgangspreis finden = 50 und Preisdelta = 150. Als Ausgangs Ich erwarte, dass die folgenden Produkte erhalten:

Product1.price = 100 
Product5.price = 220 
Product2.price = 305 

Die Berechnung wie folgt aussieht:

Startpunkt Preis = 50 so sollte das erste Produkt einen Preis von nicht weniger als 50 und nicht mehr als 200 (50 + 150) haben. Auf dieser Grundlage haben wir ein Produkt aus unserem Katalog mit einem Preis = 100 gefunden. Das zweite Produkt sollte einen Preis von nicht weniger als 100 und nicht mehr als 250 (100 + 150) haben. Dies ist ein Produkt mit einem Preis = 220 .. und der dritte Preis nicht weniger als 220 und nicht mehr 370. Dies ist ein Produkt mit einem Preis = 305

Könnten Sie bitte eine Cypher-Abfrage zeigen, die solche Art von Produkten finden wird.

+0

Können Sie klären, wie Preis und Preis Delta Faktor in Ihre Berechnung einbeziehen? Meine Annahme war, dass Sie mit dem Preis beginnen wollten, und das Delta würde Ihnen einen Boden und eine Decke des Preises geben -/+ das Delta, aber das würde bedeuten, dass die Obergrenze 200 (50 + 150) wäre. Die Preise in Ihrer Ausgabe übersteigen das, also meine Annahme ist falsch, aber ich kann nicht sagen, welche Berechnung Sie verwenden möchten. – InverseFalcon

+0

So etwas? MIT 50 AS initPrice, 150 AS delta MIT \t CASE \t WHEN (initPrice-delta)> 0 THEN (initPrice-delta) ELSE 0 END AS LowRange, (initPrice + delta) AS Hochtonbereich MATCH (n : Produkt) WHERE lowRange logisima

+0

Das scheint meiner Annahme zu entsprechen, aber die Werte in den Preisen in Ihrer Beispielausgabe passen nicht zu dieser Formel (Preise von 220 und 305 sind beide höher als die berechneter Grenzwert von 200). Wenn das in Ihrer Beispielausgabe nur ein Problem ist, könnten Sie es beheben, um Verwirrung zu vermeiden? – InverseFalcon

Antwort

1

Als alternative Lösung sollte die viel schneller abfragen, aber mehr Wartung erfordert und Pflege richtig arbeiten zu halten (vor allem bei schnell wechselnden Produkt Preisdaten), können Sie Beziehungen zwischen Ihren Produktknoten in aufsteigender Preisreihenfolge erstellen und die Deltas als Beziehungseigenschaften beibehalten.

Hier ist, wie Sie könnte dies mit APOC Procedures erstellen:

MATCH (p:Product) 
WITH p 
ORDER BY p.price ASC 
WITH apoc.coll.pairsMin(COLLECT(p)) as products 
UNWIND products as prodPairs 
WITH prodPairs[0] as prod1, prodPairs[1] as prod2 
CREATE (prod1)-[r:NextProd]->(prod2) 
SET r.delta = prod2.price - prod1.price 

Und hier ist, wie Sie könnte diese Abfrage, sobald es eingerichtet.

WITH {startPrice:50, delta:150} as params 
WITH params, params.startPrice + params.delta as ceiling 
MATCH (start:Product) 
WHERE params.startPrice <= start.price <= ceiling 
WITH start, params 
ORDER BY start.price ASC 
LIMIT 1 
MATCH (start)-[r:NextProd*0..]->(product:Product) 
WHERE ALL(rel in r WHERE rel.delta <= params.delta) 
RETURN DISTINCT product 

Dies sollte eine ziemlich schnelle Abfrage, wie die ALL() Prädikat sein soll, das variable Spiel abgeschnitten, wenn es eine Beziehung, die das gewünschte Delta übersteigt erreicht.

Der Nachteil ist natürlich, dass Sie sicherstellen müssen, dass jede Operation, die sich auf die Struktur der verknüpften Liste auswirkt (Hinzufügen oder Entfernen von Produkten und Ändern der Produktpreise), die Struktur korrekt anpasst Ansätze, um die Threadsicherheit zu gewährleisten, damit Sie die verknüpfte Liste nicht verfälschen, wenn Produkte und/oder Preise gleichzeitig aktualisiert werden.

+0

Danke! Sieht so aus, als ob dies der beste Weg ist, um jetzt zu gehen. – alexanoid

+0

Das Hauptproblem bei diesem Ansatz, dass ich nicht über einige Produkte in dieser Liste springen kann oder sogar verschiedene Pfade auf der Basis unterschiedlicher Bedingungen in der Abfrage durchläuft. Zum Beispiel wenn ich ' Ich möchte etwas zusätzliche Bedingung zum Preis hinzufügen .. wie WHERE product.color = 'red' dann dieser Ansatz, sieht aus wie wird nicht funktionieren – alexanoid

+0

Sie können nicht über Produkte springen, aber Sie können zusätzliche Bedingungen hinzufügen, einschließlich auf die Knoten selbst.Fügen Sie in der ALL() -Funktion diese am Ende an: 'AND endNode (rel) .color = 'red''. Dies fügt die zusätzliche Anforderung hinzu, dass alle Produkte im Pfad rot sein müssen. – InverseFalcon

1

Dies ist ziemlich komplex in Cypher durchzuführen. Der einzige Ansatz, der mir kommt, ist die Verwendung der REDUCE() - Funktion zusammen mit einer CASE-Anweisung, um das Produkt am Ende der Liste bedingt hinzuzufügen, wenn es innerhalb des Preisdeltas des letzten Produkts in der Liste liegt.

Beachten Sie, dass es bei diesem Ansatz keine Möglichkeit gibt, die Verarbeitung von Produkten kurzzuschließen. Wenn es 1 Million Gesamtprodukte gibt und wir in der geordneten Liste von Produkten finden, dass nur die ersten beiden Produkte innerhalb dieses Delta-Musters sind, wird diese Abfrage weiterhin jedes einzelne verbleibende dieser Millionenprodukte prüfen, obwohl keines von ihnen sein wird zu unserer Liste hinzugefügt.

Diese Abfrage sollte für Sie arbeiten.

WITH {startPrice:50, delta:150} as params 
MATCH (p:Product) 
WHERE p.price >= params.startPrice 
WITH params, p 
ORDER BY p.price asc 
WITH params, COLLECT(p) as products 
WITH params, TAIL(products) as products, HEAD(products) as first 
WHERE first.price <= params.startPrice + params.delta 
WITH REDUCE(prods = [first], prod in products | 
    CASE WHEN prod.price <= LAST(prods).price + params.delta 
     THEN prods + prod 
     ELSE prods END) as products 
RETURN products 
+0

Vielen Dank! Ist eine Möglichkeit, dieses Schema zu optimieren, um die Leistung bei der Suche nach einem Produkt nicht zu beeinträchtigen. Fügen Sie vielleicht einige Arten von Beziehungen hinzu, wenn Sie ein neues Produkt im Katalog oder etwas anderes erstellen? Die Leistung ist eines der wichtigen Kriterien in diesem System. – alexanoid

+0

Die einzige Optimierung, die ich bei der Verwendung dieser Route in Betracht ziehen kann, besteht darin, Produkte in aufsteigender Reihenfolge des Preises zu verbinden und eine Delta-Eigenschaft für jede dieser Beziehungen als Delta zwischen den Preisen der verbundenen Knoten festzulegen. Auf diese Weise können Sie mithilfe von ALL() eine effiziente Anpassung variabler Länge durchführen, um sicherzustellen, dass alle Beziehungsdeltas im Pfad unter Ihrem delta-Parameter liegen. Dies ist jedoch wartungsintensiv, wenn sich Preise ändern oder Produkte regelmäßig entfernt oder eingefügt werden. Ich kann das als separate Antwort hinzufügen, wenn Sie mehr Details wünschen. – InverseFalcon

1

Die Lösung erfordert die Übertragung eines Zwischenergebnisses während der Iteration. Ein interessantes Problem, denn die Chiffre bietet diese Möglichkeit heute nicht direkt an. Als eine Übung (Skizze), um die apoc.periodic.commit Verfahren von APOC -Bibliothek verwenden:

CALL apoc.create.uuid() YIELD uuid 
CALL apoc.periodic.commit(" 
    MERGE (H:tmpVars {id: {tmpId}}) 
    ON CREATE SET H.prices = [], 
       H.lastPrice = {lastPrice}, 
       H.delta = {delta} 
    WITH H 
    MATCH (P:Product) WHERE P.price > H.lastPrice AND 
          P.price < H.lastPrice + H.delta 
    WITH H, max(P.price) as lastPrice 
    SET H.lastPrice = lastPrice, 
     H.prices = H.prices + lastPrice 
    RETURN 1 
    ", {tmpId: uuid, delta: 150, lastPrice: 50} 
) YIELD updates, executions, runtime 
MATCH (T:tmpVars {id: uuid}) 
WITH T, T.prices as prices DETACH DELETE T 
WITH prices 
UNWIND prices as price 
MATCH (P:Product) WHERE P.price = price 
RETURN P ORDER BY P.price ASC 
+0

Danke für Ihre Antwort! Könnten Sie dies bitte auch unter dem Gesichtspunkt der Leistung kommentieren? Wie hängt dieser Ansatz von der Produktnummer in der Datenbank ab? – alexanoid

+0

@alexanoid Entschuldigung. Dies ist nur eine Skizze, eine Idee. Keine Leistung :) –

Verwandte Themen