1

Je travaille sur la mise en place d'un système de recommandations en plus de notre graphique Neo4J et je commence tout juste à examiner la requête que je prévois d'utiliser, mais il fonctionne beaucoup plus lentement que prévu.Filtrage collaboratif Neo4J plus lent que prévu

Statistiques

Neo4J Version: 2.3.1 
Nodes: 820K 
Relationships: 7.6M 

J'ai regardé dans l'optimisation des requêtes un peu, mais pour autant que je vois que je ne fais pas des pièges habituels/communs dans la structure de requête (mais je je ne suis pas un expert).

Voici une console dev avec un jeu de données de test: http://console.neo4j.org/r/b7jk2b

La requête

MATCH (u1:User {id: {user_id}})-[l1:LIKES]->(p1:Product) 
WITH u1, l1, p1 
ORDER BY p1.created_at DESC 
LIMIT 10 

MATCH (p1)<-[:LIKES]-(u2:User) 
WHERE NOT u1=u2 
WITH u1, l1, p1, u2, COUNT(u2) as rating 
ORDER BY rating DESC 
LIMIT 50 

MATCH (u2)-[l2:LIKES]->(recommendation:Product) 
WHERE NOT (p1)=(recommendation) 
WITH recommendation, COUNT(recommendation) as weight 
RETURN recommendation.id as id 
ORDER BY weight DESC 
LIMIT {limit} 

Nos indices

Indexes 
ON :LIKES(created_at)  ONLINE 
ON :Product(id)   ONLINE 
ON :Product(created_at) ONLINE 
ON :User(id)    ONLINE 
ON :User(date_joined)  ONLINE 

No constraints 

Profil de requête de sortie (contre une copie de notre produ jeu de données ction)

+-------------------+----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| Operator   | Estimated Rows | Rows | DB Hits | Identifiers        | Other             | 
+-------------------+----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +ProduceResults |    7 | 100 |  0 | id           | id              | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Projection  |    7 | 100 |  0 | anon[382], id, recommendation, weight  | anon[382]            | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Top    |    7 | 100 |  0 | anon[382], recommendation, weight   | Literal(100); weight         | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Projection  |    7 | 129342 | 129342 | anon[382], recommendation, weight   | recommendation.id; weight        | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +EagerAggregation |    7 | 129342 |  0 | recommendation, weight      | recommendation           | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Filter   |    44 | 442432 | 471953 | l1, l2, p1, rating, recommendation, u1, u2 | Ands(NOT(p1 == recommendation), recommendation:Product) | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Expand(All)  |    44 | 472039 | 472089 | l1, l2, p1, rating, recommendation, u1, u2 | (u2)-[l2:LIKES]->(recommendation)      | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Top    |    10 |  50 |  0 | l1, p1, rating, u1, u2      | Literal(50); rating          | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +EagerAggregation |    10 | 527 |  0 | l1, p1, rating, u1, u2      | u1, l1, p1, u2           | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Filter   |    92 | 563 |  563 | anon[82], anon[119], l1, p1, u1, u2  | Ands(NOT(u1 == u2), u2:User)       | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Expand(All)  |    92 | 574 |  584 | anon[82], anon[119], l1, p1, u1, u2  | (p1)<-[:LIKES]-(u2)          | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Top    |    5 |  10 |  0 | anon[82], l1, p1, u1      | Literal(10);           | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Projection  |    5 |  42 |  42 | anon[82], l1, p1, u1      | u1; l1; p1; p1.created_at        | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Filter   |    5 |  42 |  413 | l1, p1, u1         | p1:Product            | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +Expand(All)  |    6 | 413 |  414 | l1, p1, u1         | (u1)-[l1:LIKES]->(p1)         | 
| |     +----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 
| +NodeIndexSeek |    1 |  1 |  2 | u1           | :User(id)            | 
+-------------------+----------------+--------+---------+--------------------------------------------+---------------------------------------------------------+ 

J'ai vu des études de cas où les gens utilisent Neo4j pour faire le filtrage collaboratif en temps réel, donc je suppose qu'il doit être possible d'obtenir ce genre de requête de travail sur ce genre de données. Suis-je irréaliste? Nous l'exécutons sur un noeud Amazon EC2 Compute-Optimized (c4.large), donc j'ai pensé que ce serait assez performant.

Je me suis gratté la tête ici et j'apprécierais vraiment toute contribution.

Cheers, David.

+0

Vous pourriez être intéressé par: [neo4j-fiber] (https://github.com/VeliovGroup/neo4j-fiber). Ce paquet a une optimisation de requête intégrée. Voici [exemple d'application] (http: //neo4j-graph.meteor.com) –

+0

Combien de temps dure votre requête? –

Répondre

0

[A part. La console dev, lors de sa réouverture, ne Recréer pas d'index, de sorte qu'ils doivent être recréés manuellement]

Je ne sais pas si cela est assez bon pour vous, mais vous pouvez éliminer environ 44% des coups DB dans vos résultats profilés simplement ne pas spécifier les étiquettes pour la plupart des nœuds (p1, u2 et recommendation) dans votre requête:

MATCH (u1:User {id: {user_id}})-[l1:LIKES]->(p1) 
WITH u1, l1, p1 
ORDER BY p1.created_at DESC 
LIMIT 10 

MATCH (p1)<-[:LIKES]-(u2) 
WHERE NOT u1=u2 
WITH u1, l1, p1, u2, COUNT(u2) as rating 
ORDER BY rating DESC 
LIMIT 50 

MATCH (u2)-[l2:LIKES]->(recommendation) 
WHERE NOT (p1)=(recommendation) 
WITH recommendation, COUNT(recommendation) as weight 
RETURN recommendation.id as id 
ORDER BY weight DESC 
LIMIT {limit} 

L'étiquette u1 devrait encore être spécifié dans la requête, si nce, qui permet à Cypher d'indexer sur :User(id). En général, il faut évaluer soigneusement une requête pour voir quand les étiquettes de nœuds peuvent être éliminées. Dans votre cas, les nœuds p1, u2 et recommendation peuvent être trouvés en suivant les relations (et, je présume, le type de relation LIKE n'est utilisé que pour pointer vers Product nœuds), donc spécifier leurs étiquettes est superflue et cause un travail inutile.

Les résultats du profil de la requête ci-dessus auront une valeur de DB Hits0 pour tous les Filter étapes (et dans un cas, le tout sera éliminé étape Filter).

+0

Salut cybersam, Merci pour vos commentaires. En fait, je pensais que donner des étiquettes à chaque nœud rendait la requête plus ciblée et améliorait les performances: - | Heureux de découvrir maintenant que j'avais tort dans ce sens. Malheureusement nous avons aussi une relation qui est '(u: User) - [: LIKES] -> (p: Shop)' ce qui signifie que nous devons filtrer sur le type de noeud cible. En y réfléchissant maintenant, nous devrions probablement transformer les relations en quelque chose de plus spécifique comme '[: LIKES_SHOP]' pour éviter ce besoin. Vous m'avez donné matière à réflexion. Je vais revenir à la question et voir ce que je peux faire. Merci encore David –