Devolver nodo si la relación no está presente

88

Estoy tratando de crear una consulta usando cifrado que "encontrará" los ingredientes faltantes que un chef podría tener. Mi gráfico está configurado así:

(ingredient_value)-[:is_part_of]->(ingredient)

(ingredient)tendría una clave / valor de name = "dye colors". (ingredient_value)podría tener una clave / valor de valor = "rojo" y "es parte de" (ingredient, name="dye colors").

(chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)

Estoy usando esta consulta para obtener todos los ingredientsvalores, pero no sus valores reales, que requiere una receta, pero me gustaría devolver solo lo ingredientsque el chef no tiene, en lugar de todos los ingredientes que requiere cada receta. Lo intenté

(chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)<-[:has_ingredient*0..0]-chef

pero esto no devolvió nada.

¿Es esto algo que puede lograr cypher / neo4j o es algo que se maneja mejor devolviendo todos los ingredientes y clasificándolos yo mismo?

Bono: también hay una manera de usar cypher para hacer coincidir todos los valores que tiene un chef con todos los valores que requiere una receta. Hasta ahora, solo he devuelto todas las coincidencias parciales que son devueltas por chef-[:has_value]->ingredient_value<-[:requires_value]-recipeay agregando los resultados yo mismo.

Nicolás
fuente
Consulte aquí para obtener información relevante para v3: stackoverflow.com/questions/25673223/…
Maciej
Para futuros usuarios; puede usar existsen una WHEREcláusula (también negarla ), neo4j.com/developer/subqueries/#existential-subqueries para obtener más información.
ozanmuyes

Respuestas:

157

Actualización 10/01/2013:

Encontré esto en la referencia de Neo4j 2.0 :

Trate de no usar relaciones opcionales. Sobre todo,

no los use así:

MATCH a-[r?:LOVES]->() WHERE r IS NULL donde solo te aseguras de que no existan.

En su lugar, haz esto así:

MATCH a WHERE NOT (a)-[:LOVES]->()

Usando cifrado para verificar si la relación no existe:

...
MATCH source-[r?:someType]-target
WHERE r is null
RETURN source

Los ? marca hace que la relación sea opcional.

O

En neo4j 2 haz:

...
OPTIONAL MATCH source-[r:someType]-target
WHERE r is null
RETURN source

Ahora puede verificar la relación no existente (nula).

Gil Stal
fuente
3
En Neo4j 2.0, use OPTIONAL MATCH para hacer coincidir relaciones opcionales, es decir, el primer ejemplo se vería como OPTIONAL MATCH (fuente) - [r: someType] - (target) RETURN source, r
boggle
Estoy tratando de tener un nodo etiquetado en DONDE NO, no funciona. Como: COINCIDIR DONDE NO (a) - [: AMA] -> (Extraño), en este 'Extraño' hay una etiqueta de nodo. Estoy usando neo4j versión 2.1.2
Krishna Shetty
1
No importa, entiendo por qué querría mostrar la progresión para llegar a esta respuesta: COINCIDIR DONDE NO (a) - [: AMA] -> ()
NumenorForLife
4
El MATCH a...ejemplo debería ser ahoraMATCH (a) WHERE NOT (a)-[:LOVES]->()
Liam
1
@ gil-stal Por qué no puedo usar el nombre de nodo con esta consulta como esta. COINCIDIR DONDE NO (a) - [: AMA] -> (b: SomeLabel). Si no uso el nombre de nodo, entonces funciona.
iit2011081
15

Para buscar nodos sin ninguna relación

Esta es la buena opción para verificar si la relación existe o no

MATCH (player)
    WHERE NOT(player)-[:played]->()
    RETURN player

También puede verificar múltiples condiciones para esto. Devolverá todos los nodos, que no tengan la relación "jugado" o "no jugado".

MATCH (player) 
 WHERE NOT (player)-[:played|notPlayed]->()
 RETURN player

Para buscar nodos que no tengan ninguna relación.

MATCH (player) 
WHERE NOT (player)-[r]-()
RETURN player

Verificará que el nodo no tenga ninguna relación entrante / saliente.

Satish Shinde
fuente
4
MATCH (player) WHERE NOT (player)-[r]-() RETURN player da error Variable r no definido . ¿Cómo puedo definir r?
Chathura Wijeweera
para arreglar esto, especifique una relación (por ejemplo (player -[:rel]- ()) o déjelo vacío para cualquier relación(player -[]- ()
Archemar
MATCH (player) WHERE NOT (player)-[]-() RETURN player- Funciona bien
Prashanth Terala
Su primera consulta es realmente incorrecta. El patrón MATCH en sí mismo siempre devuelve solo las relaciones existentes, ninguna de ellas NULL. Entonces, su línea WHERE no tiene nada de qué filtrar.
Cristi S.
@CristiS. Gracias por hacérmelo saber. He actualizado la consulta que debería funcionar
Satish Shinde
8

Si necesita una semántica de "exclusión condicional", puede lograrlo de esta manera.

A partir de neo4j 2.2.1, puede usar la OPTIONAL MATCHcláusula y filtrar los NULLnodos no coincidentes ( ).

También es importante utilizar una WITHcláusula entre las cláusulas OPTIONAL MATCHy WHERE, de modo que la primera WHEREdefina una condición para la coincidencia opcional y la segunda se WHEREcomporte como un filtro.

Suponiendo que tenemos 2 tipos de nodos: Persony Communication. Si quiero obtener todas las Personas que nunca se han comunicado por teléfono, pero que pueden haberse comunicado de otras formas, haría esta consulta:

MATCH (p: Person) 
OPTIONAL MATCH p--(c: Communication) 
WHERE c.way = 'telephone'
WITH p, c 
WHERE c IS NULL 
RETURN p

El patrón de coincidencia coincidirá con todas las Personas con sus comunicaciones donde cserán NULLpara Comunicaciones no telefónicas. Luego, el filtro ( WHEREdespués WITH) filtrará las comunicaciones telefónicas dejando todas las demás.

Referencias:

http://neo4j.com/docs/stable/query-optional-match.html#_introduction_3 http://java.dzone.com/articles/new-neo4j-optional

Dieter Pisarewski
fuente
2

Escribí una esencia que muestra cómo se puede hacer esto de forma bastante natural usando Cypher 2.0

http://gist.neo4j.org/?9171581

El punto clave es usar una combinación opcional para los ingredientes disponibles y luego comparar con el filtro los ingredientes faltantes (nulos) o los ingredientes con el valor incorrecto.

Tenga en cuenta que la noción es declarativa y no necesita describir un algoritmo, solo escriba lo que necesita.

sobresaltarse
fuente
2

Completé esta tarea usando gremlin. yo hice

x=[]

g.idx('Chef')[[name:'chef1']].as('chef')
.out('has_ingredient').as('alreadyHas').aggregate(x).back('chef')
.out('has_value').as('values')
.in('requires_value').as('recipes')
.out('requires_ingredient').as('ingredients').except(x).path()

Esto devolvió los caminos de todos los ingredientes faltantes. No pude formular esto en el lenguaje cifrado, al menos para la versión 1.7.

Nicolás
fuente
2

La última consulta debería ser:

START chef = node(..)
MATCH (chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)
WHERE (ingredient)<-[:has_ingredient]-chef
RETURN ingredient

Este patrón: (ingredient)<-[:has_ingredient*0..0]-chef

Es la razón por la que no devolvió nada. *0..0significa que la longitud de las relaciones debe ser cero, lo que significa que el ingrediente y el chef deben ser el mismo nodo, lo que no es así.

Andres
fuente
Sí, pero no devuelve el ingrediente deseado. Devuelve lo que el chef ya tiene en común con la receta, quiero averiguar la diferencia.
Nicholas