Как заставить CREATE работать УНИКАЛЬНО с помощью подзапроса?
Вопрос
У меня есть запрос, подобный этому:
MATCH left, right
WHERE (ID(right) IN [1, 2, 3] AND ID(left) IN [4, 5, 6])
WITH left, right
LIMIT 1
RETURN left, right
UNION MATCH left, right
WHERE (ID(right) IN [1, 2, 3] AND ID(left) IN [4, 5, 6])
WITH left, right
SKIP 4 LIMIT 1
RETURN left, right
UNION MATCH left, right
WHERE (ID(right) IN [1, 2, 3] AND ID(left) IN [4, 5, 6])
WITH left, right
SKIP 8 LIMIT 1
RETURN left, right
CREATE UNIQUE left-[rel:FRIEND]->right
RETURN rel;
В общем, я просто создаю набор данных, чтобы позже использовать его в инструкции CREATE UNIQUE.
Очевидно, что это не работает - анализатор запросов говорит, что я могу использовать предложение RETURN только один раз.Мой вопрос в том, как составить набор данных в этом случае?Я попытался назначить псевдоним и использовать его в CREATE UNIQUE - тоже не смог заставить его работать.Что я делаю не так?Возможен ли вообще такой сценарий?
Решение
Возможно, я неправильно понимаю, к чему вы клоните, но вот что приходит мне в голову, когда я просматриваю ваш запрос.
Для начала, вот адаптация вашего запроса, который использует SKIP
и LIMIT
без RETURN
или UNION
.
MATCH left, right
WHERE ID(left) IN [1,2,3] AND ID(right) IN [4,5,6]
WITH left, right
LIMIT 1
CREATE UNIQUE left-[rel:FRIEND]->right
WITH [rel] as rels //If you want to return the relationship later you can put it in a collection and bring it WITH
MATCH left, right
WHERE ID(left) IN [1,2,3] AND ID(right) IN [4,5,6]
WITH left, right, rels
SKIP 4 LIMIT 1
CREATE UNIQUE left-[rel:FRIEND]->right
WITH rels + [rel] as rels
MATCH left, right
WHERE ID(left) IN [1,2,3] AND ID(right) IN [4,5,6]
WITH left, right, rels
SKIP 8 LIMIT 1
CREATE UNIQUE left-[rel:FRIEND]->right
WITH rels + [rel] as rels
RETURN LENGTH(rels), rels // You can return the relationships here but SKIP/LIMIT does its job also if you don't return anything
Но этот запрос немного дикий.На самом деле это три запроса, где два были искусственно втиснуты в качестве подзапросов первого.Он заново сопоставляет одни и те же узлы в каждом подзапросе, и на самом деле ничего не получается, если выполнять запросы таким образом, а не по отдельности (на самом деле это медленнее, потому что в каждом подзапросе вы сопоставляете также узлы, которые, как вы знаете, вы не будете использовать).
Итак, мое первое предложение - использовать START
вместо того, чтобы MATCH...WHERE
при получении узлов по идентификатору.В нынешнем виде запрос привязывает каждый узел в базе данных как "левый", а затем каждый узел в базе данных как "правый", и затем он отфильтровывает все узлы, привязанные к "левому", которые не соответствуют условию в WHERE
предложение, а затем то же самое для "правильного".Поскольку эта часть запроса повторяется три раза, все узлы в базе данных привязываются в общей сложности шесть раз.Это дорого стоит для создания трех отношений.Если вы используете START
вы можете сразу же связать нужные вам узлы.На самом деле это не ответ на ваш вопрос, но так будет быстрее, а запрос - чище.Итак, используйте START
чтобы получить узлы по их внутреннему идентификатору.
START left = node(1,2,3), right = node(4,5,6)
Второе, о чем я думаю, - это разница между узлами и "путями" или "элементами результата" при сопоставлении шаблонов.Когда вы связываете три узла "слева" и три других узла "справа", у вас получается не три элемента результата, а девять.Для каждого узла, связанного "слева", вы получаете три результата, потому что есть три возможных "справа", с которыми его можно объединить.Если вы хотите соотнести каждое "левое" с каждым "правым", отлично.Но я думаю, что то, что вы ищете, - это результирующие элементы (1),(4)
, (2),(5)
, (3),(6)
, и хотя кажется удобным связать три "левых" узла и три "правых" узла в одном запросе с коллекциями идентификаторов узлов, затем вам нужно выполнить всю эту фильтрацию, чтобы избавиться от 6 нежелательных совпадений.Запрос становится сложным и громоздким, и на самом деле это медленнее, чем выполнение запросов по отдельности.Другой способ сказать это - сказать , что (1)-[:FRIEND]->(4)
это отдельный шаблон, не связанный (релевантно) с другими шаблонами, которые вы создаете.Все было бы по-другому, если бы вы хотели создавать (1)-[:FRIEND]->(2)<-[:FRIEND]-(3)
, тогда вы хотели бы обрабатывать эти три узла вместе.Может быть, вы просто изучаете второстепенное использование cypher, но я подумал, что должен указать на это.Кстати, используя SKIP
и LIMIT
этот способ немного неуместен, на самом деле они не предназначены для сопоставления с образцом и фильтрации.Это также непредсказуемо, если только вы не используете ORDER BY
, поскольку нет никакой гарантии, что результаты будут в определенном порядке.Вы не знаете, какой именно результирующий элемент передается дальше.В любом случае, в данном случае, я думаю, было бы лучше связать узлы и создать связь в трех отдельных запросах.
START left = node(1), right = node(4)
CREATE UNIQUE left-[rel:FRIEND]->right
RETURN rel
START left = node(2), right = node(5)
CREATE UNIQUE left-[rel:FRIEND]->right
RETURN rel
START left = node(3), right = node(6)
CREATE UNIQUE left-[rel:FRIEND]->right
RETURN rel
Поскольку вы уже знаете, что вам нужны эти три пары, а не, скажем, (1),(4)
,(1),(5)
,(1),(6)
было бы разумно запросить только эти пары, и самый простой способ - запросить их по отдельности.
Но в - третьих, поскольку три запроса структурно идентичны, отличаясь только значением свойства (если id следует рассматривать как свойство), вы можете упростить запрос, обобщив или обезличив то, что их отличает, т.е.используйте параметры.
START left = node({leftId}), right = node({rightId})
CREATE UNIQUE left-[rel:FRIEND]->right
RETURN rel
parameters: {leftId:1, rightId:4}, {leftId:2, rightId:5}, {leftId:3, rightId:6}
Поскольку структура идентична, cypher может кэшировать план выполнения.Это обеспечивает хорошую производительность, а запрос является аккуратным, ремонтопригодным и может быть легко расширен, если позже вы захотите выполнить ту же операцию на других парах узлов.