Я пытаюсь написать запрос Cypher, который является своего рода «объединенным» запросом, в основном что-то вроде:
MATCH (myNodeUsedInAllSubQueries)
WITH myNodeUsedInAllSubQueries
MATCH (a)-[...]->(myNodeUsedInAllSubQueries)
RETURN a
UNION
MATCH (b)-[...]->(myNodeUsedInAllSubQueries)
RETURN b
...
Я хочу сопоставить (myNodeUsedInAllSubQueries)
один раз в начале, потому что этот узел используется во всех подзапросах.
Конечно, поиск этого узла в моем примере не так прост, как этот (он включает в себя отношения и предложение WHERE
, но здесь я сделаю это просто).
Я попытаюсь объяснить это простым примером и тем, что я пробовал до сих пор.
Пример
Графически графическая структура выглядит следующим образом:
(John: Person) ----------[LIKES]----------> (pizza: Food)
^
(Developers: Group) ------[LIKES]------┘
(Jim: Person) --[BELONGS_TO]--> (Business: Group) --[LIKES]--> (apple: Food)
Пример доступен здесь, в консоли Neo4J . Сценарий импорта находится здесь (на случай, если вы хотите воспроизвести проблему локально):
CREATE (john: Person{name: "John"});
CREATE (jim: Person{name: "Jim"});
CREATE (devs: Group{name: "Developers"});
CREATE (business: Group{name: "Business"});
CREATE (pizza: Food{name: "pizza"});
CREATE (apple: Food{name: "apple"});
MATCH (john: Person{name: "John"}), (pizza: Food{name: "pizza"})
CREATE (john)-[:LIKES]->(pizza);
MATCH (jim: Person{name: "Jim"}), (business: Group{name: "Business"})
CREATE (jim)-[:BELONGS_TO]->(business);
MATCH (devs: Group{name: "Developers"}), (pizza: Food{name: "pizza"})
CREATE (devs)-[:LIKES]->(pizza);
MATCH (business: Group{name: "Business"}), (apple: Food{name: "apple"})
CREATE (business)-[:LIKES]->(apple);
В этом конкретном примере мой вопрос: как получить все Person
узлы, которым нравится пицца? Зная, что:
- A
Person
может понравиться Food
(прямое отношение)
- A
Group
может понравиться Food
, и транзитивно, если человек A
BELONGS_TO
группа,
он LIKES
также пища, которую группа LIKES
(но есть нет прямого отношения
между Person
и Food
в этом случае)
Я также хотел бы получить узел pizza
один раз. Как я уже говорил во введении, в моем реальном примере получение pizza
может быть довольно сложным, поэтому я не хочу дублировать MATCH
в каждом случае.
Что я пробовал
Использование UNION
MATCH (pizza: Food{name: "pizza"})
WITH pizza
MATCH (p: Person)-[:LIKES]->(pizza)
RETURN p
UNION
MATCH (p: Person)-[:BELONGS_TO]->(g: Group)-[:LIKES]->(pizza)
RETURN p
Этот запрос неожиданно возвращает 2 узла: John
и Jim
. После расследования я понял, что
это потому, что (pizza)
во втором предложении UNION
не относится к узлу pizza
возвращается первым MATCH
, а точнее любым узлом. Мы можем подтвердить это, запустив:
MATCH (pizza: Food{name: "pizza"})
WITH pizza
MATCH (p: Person)-[:LIKES]->(pizza)
RETURN p
UNION
MATCH (p: Person)-[:BELONGS_TO]->(g: Group)-[:LIKES]->(anything)
WHERE anything = pizza
RETURN p
Это возвращает ошибку:
Neo.ClientError.Statement.SyntaxError: Variable `pizza` not defined (line 7, column 18 (offset: 184))
"WHERE anything = pizza"
, подтверждая тот факт, что pizza
узел не распространяется на второй MATCH
.
Использование WITH
и COLLECT
Это исправляет проблему узла pizza
, который нельзя передать с первого MATCH
на 3-й MATCH
.
MATCH (pizza: Food{name: "pizza"})
WITH pizza
MATCH (p: Person)-[:LIKES]->(pizza)
WITH pizza, COLLECT(p) AS people
MATCH (p: Person)-[:BELONGS_TO]->(g: Group)-[:LIKES]->(pizza)
RETURN people + COLLECT(p) AS people
Но этот запрос ничего не возвращает: это потому, что 3-й MATCH
ничего не возвращает
(нет Person
в Group
, LIKES
pizza
).
Если я добавлю Person
в Developers
Group
:
MATCH (devs: Group{name: "Developers"})
CREATE (emma: Person{name: "Emma"})-[:BELONGS_TO]->(devs)
Предыдущий запрос успешно возвращает Джона и Эмму, потому что последний MATCH
возвращает как минимум один Person
.
Но в моем случае, возможно, что MATCH
ничего не возвращает, так что не работает.
Надеюсь, этот пример прост и понятен. В этом случае запрос, который я ищу, должен возвращать только Джона, потому что он единственный Person
, которому нравится pizza
.