Cypher: объединение запросов с начальным поиском узла - PullRequest
0 голосов
/ 27 мая 2019

Я пытаюсь написать запрос 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.

Ответы [ 2 ]

1 голос
/ 28 мая 2019

В качестве альтернативы ответу cybersam, если ваша структура графа такова, что MATCH (p:Person)-[:BELONGS_TO|LIKES*..2]->(food:Food) не будет работать (например, если вы работаете над графиком, где люди могут любить друг друга, и вы не хотите возвращатьсячеловек, которому нравится другой человек, который любит пиццу, которая будет придерживаться вашего шаблона), вы можете использовать отношение длины 0..1, чтобы представить необязательный шаг в шаблоне:

MATCH (p:Person)-[:BELONGS_TO*0..1]->()-[:LIKES]->(food:Food)
WHERE food.name = "pizza"
RETURN COLLECT(p) AS people;

Это позволяет сопоставить обаиз этих шаблонов одновременно:

(p:Person)-[:LIKES]->(food:Food)

и

(p:Person)-[:BELONGS_TO]->()-[:LIKES]->(food:Food)

Я написал базу знанийстатья об этом подходе.

1 голос
/ 28 мая 2019

[ОБНОВЛЕНО]

Это должно работать для вашего простого варианта использования:

MATCH (p:Person)-[:BELONGS_TO|LIKES*..2]->(food:Food {name:"pizza"})
RETURN food, COLLECT(p) AS people;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...