2016-11-11 8 views
0

Ich versuche ein Basis-Empfehlungssystem auf Neo4j zu implementieren. Grundsätzlich habe ich Nutzer und Künstler von Nutzern gemocht. Ich möchte fragen "Benutzer, die Damien Reis mochten, mochte auch diese Künstler". Das ist einfach, mit folgenden:Gelegentlich häufige Empfehlung zu Neo4j mit Cypher

MATCH (n:Artist)<-[:LIKES]-(p:Person)-[:LIKES]->(n2:Artist {artist_name: "damien rice"}) 
RETURN n.artist_name, COUNT(n) AS COUNT 
ORDER BY COUNT DESC 
LIMIT 30 

Obwohl dieser Ansatz Art wahr, es gibt Coldplay, The Beatles (Benutzer, die für jedermann beliebt sind) wie folgt:

n.artist_name  COUNT 
coldplay    6193 
radiohead   5377 
the beatles   3998 
death cab for cutie 3647 
muse     3252 
the killers   3064 
jack johnson   2966 

Ich neige dazu, , um herauszufinden, ungewöhnlich häufig Vorschläge. Mein beabsichtigter Ansatz ist es, coldplay mit calculating (6193/totalNumberOfLikesForColdplay) eine Punktzahl zu geben. Zum Beispiel, wenn insgesamt 61930 Leute Coldplay mögen, dann hat es eine Punktzahl von 9163/91630 = 0.1 und ich möchte alle Künstler in Abhängigkeit von dieser Punktzahl sortieren.

Ich habe versucht, die folgenden:

MATCH (n:Artist)<-[:LIKES]-(p:Person)-[:LIKES]->(n2:Artist {artist_name: "damien rice"}) 
MATCH (n2:Artist {artist_name: "damien rice"})<-[:LIKES]-(p2:Person) 
RETURN n.artist_name, COUNT(n)/COUNT(n2) AS SCORE 
ORDER BY SCORE DESC 
LIMIT 30 

Aber es tooks für immer. Welche Art von Abfrage sollte ich eingeben, um das Ergebnis möglichst effizient zu erhalten?

Edit: Ich habe gerade festgestellt, dass die Abfrage, die ich oben versuchte, nicht ist, dass ich will. Es berechnet numberOfPeopleBothLikedColdplay_DamienRice/numberOfPeopleLikedDamienRice numberOfPeopleBothLikedTheBeatles_DamienRice/numberOfPeopleLikedDamienRice und so weiter

aber ich numberOfPeopleBothLikedColdplay_DamienRice/numberOfPeopleLikedColdplay numberOfPeopleBothLikedTheBeatles_DamienRice/numberOfPeopleLikedTheBeatles ...

Vielleicht berechnen möchten i t kann aktualisiert werden als

MATCH (n:Artist)<-[:LIKES]-(p:Person)-[:LIKES]->(n2:Artist {artist_name: "damien rice"}) 
MATCH (n2:Artist {artist_name: n.name})<-[:LIKES]-(p2:Person) 
RETURN n.artist_name, COUNT(p)/COUNT(p2) AS SCORE 
ORDER BY SCORE DESC 
LIMIT 30 

Aber jetzt gibt es mir "(keine Zeilen)" als Ergebnis zurück.

Edit2: Wie es vorgeschlagen wird, ich Abfrage wie folgt aktualisiert:

MATCH (p2:Person)-[:LIKES]->(n:Artist)<-[:LIKES]-(p:Person)-[:LIKES]-> 
    (n2:Artist {artist_name: "damien rice"}) 
RETURN n.artist_name, COUNT(p)/COUNT(p2) AS SCORE 
ORDER BY SCORE DESC 
LIMIT 30 

Aber es läuft noch immer. Übrigens habe ich 292516 Künstler, 359347 Menschen, 17549962 LIKES Beziehungen zwischen Künstler und Menschen. Und Sie können a annehmen: Person kann nur ein mögen: Künstler einmal, und das nur: Personen können mögen: Künstler

+0

Wenn Sie die Anzahl der Likes berechnen möchten, sollten Sie die "Personen" zählen, die den Künstler mochten, also "COUNT (p)/COUNT (p2)" verwenden. –

Antwort

0

Es gibt einige Verbesserungen, die wir hier machen können.

Es ist hilfreich zu verstehen, warum Ihre Abfrage so lange dauert. Erinnern Sie sich, dass Neo4j das zurückgibt, was Zeilen von Datenspalten ist, und dies wird aufgebaut, während Sie die Abfrage durchlaufen. Nach dem zweiten Match werden die Reihen n2 und jede Kombination einer Person, die n2 mag, mit jeder Person, die n2 mag (da Ihr zweites Spiel ein kartesisches Produkt auf demselben Personenkreis erstellt) mit jedem anderen Künstler kombiniert mochte von diesen Leuten. Es ist eine sehr ineffiziente Abfrage (zumindest in der Komplexität), und eine lange oder nie fertiggestellte Ausführungszeit wird vollständig erwartet.

Also lasst uns das reparieren.

Zunächst können wir das zweite Match vollständig für die Berechnung der Anzahl der Likes für n2 loswerden. Statt dessen (vorausgesetzt, eine Person mag nur ein: Künstler einmal, und das nur: Personen können mögen: Künstler) können wir die Anzahl der LIKES-Beziehungen direkt zählen. Indem wir das zuerst neu anordnen, stellen wir auch sicher, dass diese Operation nur einmal für eine einzelne Reihe von Daten statt für eine große Anzahl von Reihen dupliziert wird. Dann können wir das erste MATCH laufen lassen.

MATCH (n2:Artist {artist_name: "damien rice"}) 
WITH n2, SIZE((n2)<-[:LIKES]-()) as n2Likes 
MATCH (n:Artist)<-[:LIKES]-()-[:LIKES]->(n2) 
WITH n, toFloat(COUNT(n))/n2Likes AS SCORE 
ORDER BY SCORE DESC 
LIMIT 30 
RETURN n.artist_name, SCORE 

EDIT, um geklärte Anforderungen zu adressieren. Auch geänderte Abfragen zur Verwendung von Gleitkommawerten für die Zählung, so dass der resultierende Wert eher eine Dezimalzahl als ein Int ist.

Wir können einen ähnlichen Ansatz verwenden, um die GRÖSSE() der Likes jedes Künstlers zu erhalten.

MATCH (n:Artist)<-[:LIKES]-()-[:LIKES]->(n2:Artist {artist_name: "damien rice"}) 
WITH n, toFloat(COUNT(n)) as likesBothCnt 
WITH n, likesBothCnt, SIZE(()-[:LIKES]->(n)) as likesArtist 
WITH n, likesBothCnt/likesArtist as SCORE 
ORDER BY SCORE DESC 
LIMIT 30 
RETURN n.artist_name, SCORE 

Allerdings wird diese Abfrage definitiv langsamer sein als die erste, die ich vorgeschlagen habe. Eine Möglichkeit, die Geschwindigkeit zu verbessern, besteht darin, einen Schnappschuss der gleichen Anzahl pro Künstler auf dem Künstlerknoten im Voraus zu speichern und dann den zwischengespeicherten Wert zu verwenden, wenn Sie eine Echtzeitberechnung benötigen. Sie müssen jedoch herausfinden, wie und wann die zwischengespeicherten Werte aktualisiert werden sollen.

+0

Danke für Ihre Antwort @InverseFalcon, Ihre Abfrage ist ziemlich schnell, aber ich habe gerade eine Bearbeitung der Frage gemacht, können Sie bitte einen Blick darauf werfen? –

+0

Abhängig von Ihrem Vorschlag und meiner Bearbeitung, habe ich die Abfrage wie folgt aktualisiert: MATCH (p2: Person) - [: LIKES] -> (n: Künstler) <- [: LIKES] - (p: Person) - [ : LIKES] -> (n2: Künstler {ARTIST_NAME: "Damien rice"}) RETURN n.artist_name, COUNT (p)/COUNT (p2) AS SCORE ORDER BY SCORE DESC LIMIT Aber läuft immer noch –

+0

Aktualisiert meine Antwort auf die neuen Anforderungen. – InverseFalcon

0

Gibt es einen Grund für die Verwendung von zwei getrennten MATCH Klauseln? Die Verwendung von zwei MATCH-Klauseln hat eine andere Semantik als die Verwendung eines einzigen, siehe die Hinweise in der Cypher-Dokumentation unter uniqueness. Im aktuellen Fall erlaubt die Verwendung von zwei MATCH Klauseln p2 den gleichen Wert wie p.

MATCH 
    (n:Artist)<-[:LIKES]-(p:Person)-[:LIKES]-> 
    (n2:Artist {artist_name: "damien rice"})<-[:LIKES]-(p2:Person) 
RETURN n.artist_name, COUNT(p)/COUNT(p2) AS SCORE 
ORDER BY SCORE DESC 
LIMIT 30 

Sie können auch die Variable in der gleichen MATCH Klausel wiederholen und den gleichen Satz von Ergebnissen.Zum Beispiel:

MATCH 
    (n:Artist)<-[:LIKES]-(p:Person)-[:LIKES]->(n2:Artist {artist_name: "damien rice"}), 
    (n2)<-[:LIKES]-(p2:Person) 
RETURN n.artist_name, COUNT(p)/COUNT(p2) AS SCORE 
ORDER BY SCORE DESC 
LIMIT 30 
+0

Vielen Dank für Ihre Antwort, aber ich habe gerade einen Schnitt zu der Frage gemacht, können Sie bitte einen Blick darauf werfen? –