2015-10-08 2 views
1

J'ai un graphique d'étiquettes qui sont liées entre elles. Mon but est de créer une requête Cypher, qui retournera toutes les balises qui sont liées à un tableau de balises d'entrée via 1 ou 2 sauts.Neo4j correspond à des noeuds liés à tous les noeuds de la collection

J'ai fait une requête, qui ne fonctionne pas comme prévu.

MATCH (t:Tag) 
WHERE t.name IN ["A", "B", "C"] 
WITH t 
MATCH (a:Tag)-[:RELATED*1..2]-(t) 
RETURN DISTINCT a; 

Cette requête trouve d'abord les noeuds A, B, C puis recherche les étiquettes, qui sont liés à A, BouC via 1 nœud ou moins.

Ce que je veux faire bien est de trouver des balises, qui sont liés aux trois noeuds (A, BetC).


Je sais que je pourrais concaténer MATCH et WITH déclarations, et faire quelque chose comme ceci:

MATCH (t:Tag)-[:RELATED*1..2]-(a:Tag) 
WHERE t.name="A" 

WITH DISTINCT a 
MATCH (t:Tag)-[:RELATED*1..2]-(a) 
WHERE t.name="B" 

WITH DISTINCT a 
MATCH (t:Tag)-[:RELATED*1..2]-(a) 
WHERE t.name="C" 
... 
RETURN DISTINCT a; 

Mais il fonctionne très lent, lorsque le nombre d'étiquettes d'entrée augmentation (dans ce cas seulement 3 entrée étiquettes: A, B, C).


Y a-t-il un moyen de le faire en une seule requête, comme lors de mon premier essai?

+0

Est-il important d'utiliser une collection de noms de point en entrée, par opposition à plusieurs noms de points individuels? – jjaderberg

Répondre

5

Voici une solution qui ne nécessite qu'une seule clause MATCH.

MATCH (t:Tag)-[:RELATED*..2]-(other:Tag) 
WHERE t.name IN ["A", "B", "C"] 
WITH t, COLLECT(DISTINCT other) AS others 
WITH COLLECT(others) AS coll 
RETURN FILTER(x IN coll[0] WHERE ALL(y IN coll[1..] WHERE x IN y)) AS res; 
  • La requête trouve toutes les balises (other) qui sont "liées" (jusqu'à 2 étapes) à chaque vos balises nommées (t).
  • Il utilise ensuite l'agrégation pour collecter les distinctsother nœuds pour chaque t. Dans cet exemple, nous nous retrouvons avec 3 others collections - 1 pour chaque t. La chose importante à noter est que le résultat de la requête est l'intersection de chaque collectionothers. Il collecte ensuite toutes les collections others en une seule collection coll. Enfin, comme l'ensemble de résultats est l'intersection de chaque collection others, la requête parcourt les noeuds de la première collection others et extrait ceux qui se trouvent également dans les autres collections others. Et puisque chaque collection others contient déjà des nœuds distincts, le résultat doit également avoir des nœuds distincts.

si vous avez beaucoup de balises, la requête ci-dessus peut être fait un peu plus rapide par De plus,:

  1. Creating an index (ou uniqueness constraint, ce qui crée automatiquement un index pour vous) sur :Tag(name), puis
  2. Spécification de l'utilisation de cet index dans votre requête - en insérant la clause suivante entre les clauses MATCH et WHERE. Actuellement, le moteur Cypher n'utilise pas automatiquement l'index pour cette requête spécifique.

    USING INDEX t:Tag(name) 
    
+0

Merci pour l'explication étape par étape, cela semble raisonnable et il semble que cela devrait fonctionner comme je le veux. Mais (ici il arrive) quand j'exécute la requête, il retourne une collection de valeurs 'true' et' false'. Une idée de comment obtenir les nœuds? – lmazgon

+0

Oups. J'ai corrigé la requête. J'aurais dû utiliser 'FILTER' au lieu de' EXTRACT'. – cybersam

+0

Vous êtes mon héros! Bonne réponse, merci pour votre aide! – lmazgon

0

Que diriez-vous celui-ci:

MATCH (t:Tag)-[:RELATED*1..2]-(other:Tag) 
WHERE t.name IN ["A", "B", "C"] 
WITH t, collect(other.name) as others 
WHERE ALL(x in ["A","B","C"] WHERE x in others) 
RETURN t 

L'astuce est mis tous les nœuds associés pour t dans une collection (autres) et utiliser le prédicat ALL pour vous assurer que tous vos A, B et C font partie de ça.

+0

La syntaxe de votre requête n'est pas correcte - regardez l'instruction 'WHERE ALL', cela n'a pas vraiment de sens. Encore merci pour l'essai, je vais regarder dans le prédicat 'ALL', peut-être cela va résoudre mon problème. – lmazgon

+0

Je viens de mettre à jour ma réponse avec la déclaration correcte. –

+0

Je crains que vous ayez mal compris la question. Je n'essaie pas de m'assurer que 'A',' B' et 'C' sont dans la collection (autres), mais j'essaie plutôt de trouver tous les autres nœuds, qui sont connectés à' A', 'B 'et' C' simultanément. Ainsi, par exemple, si le noeud 'D' aurait une relation avec les trois noeuds d'entrée, alors il serait dans la sortie. De plus, si le noeud «E» ne serait connecté qu'à «A» et à «B», mais pas à «C», il ne serait pas dans la sortie. – lmazgon

1

Voici une alternative:

MATCH shortestPath((t:Tag)<-[:RELATED*1..2]-(source:Tag)) //make sure there are no duplicate paths 
WHERE source.name IN ["A","B","C"] AND NOT source.name = t.name //shortest path for identical node would throw an exception 
WITH COLLECT(t) as tags //all tags that were reachable, with duplicates for reachable from multiple tags 
UNWIND tags as tag //for each tag 
WITH tag, tags //using with as match would be a drastic slowdown 
WHERE size(filter(t IN tags WHERE ID(t) = ID(tag))) = 3 //if it is connected to all three, it must have been matched three times 
RETURN DISTINCT m //since any match will still be in there 3 (or n) times 

Il correspond d'abord tous les tags accessibles. Toutes les balises accessibles depuis toutes les balises d'une liste de longueur n doivent correspondre n fois si shortestPath est utilisé. Si vous filtrez ensuite selon ces critères (présents n fois), les balises voulues peuvent être récupérées avec des critères distincts.