如何使用子查询使CREATE UNIQUE工作?

时间:2013-11-15 13:58:09

标签: neo4j alias cypher

我有这样的查询:

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中使用它 - 无法让它工作。我究竟做错了什么?这种情况甚至可能吗?

1 个答案:

答案 0 :(得分:1)

我可能会误解你的目标,但是当我查看你的查询时,这就是我的想法。

首先,对您使用SKIPLIMIT而不使用RETURNUNION的查询进行修改。

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

但是这个查询有点疯狂。这实际上是三个查询,其中两个被人工挤压为第一个查询。它在每个子查询中重新匹配相同的节点,并且通过这种方式而不是单独运行查询实际上没有获得任何东西(它实际上更慢,因为在每个子查询中,您也匹配您知道不会使用的节点)

所以我的第一个建议是在按ID获取节点时使用START而不是MATCH...WHERE。按照目前的情况,查询将数据库中的每个节点绑定为“left”,然后将数据库中的每个节点绑定为“right”,然后它过滤掉所有绑定到“left”的节点,这些节点不符合条件WHERE子句,然后是“右”。由于这部分查询重复三次,因此数据库中的所有节点共绑定了六次。这对于创建三种关系来说是昂贵的。如果您使用START,则可以立即绑定您想要的节点。这并没有真正回答你的问题,但它会更快,查询会更清晰。因此,使用START按内部ID获取节点。

START left = node(1,2,3), right = node(4,5,6)

我想到的第二件事是匹配模式时节点与“路径”或“结果项”之间的差异。当您在“left”中绑定三个节点而在“right”中绑定另外三个节点时,您没有三个结果项,而是九个。对于“左”中绑定的每个节点,您将获得三个结果,因为有三个可能的“右”将其组合起来。如果你想将每个“左”与每个“右”联系起来,那就太好了。但我认为你要找的是结果项(1),(4)(2),(5)(3),(6),虽然绑定三个“左”节点和三个“右”节点似乎很方便在一个包含节点ID集合的查询中,您必须进行所有过滤以消除6个不需要的匹配。查询变得复杂和繁琐,实际上比单独运行查询要慢。另一种说法是说(1)-[:FRIEND]->(4)是一种独特的模式,而不是(相关地)连接到您正在创建的其他模式。如果您想创建(1)-[:FRIEND]->(2)<-[:FRIEND]-(3),那么情况会有所不同,那么您可能希望将这三个节点一起处理。也许你只是在探索密码的边缘用法,但我想我应该指出它。顺便说一句,以这种方式使用SKIPLIMIT是一个关键的关键,它们并不是真正用于模式匹配和过滤。它也是不可预测的,除非你也使用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可以缓存执行计划。这样可以获得良好的性能,并且查询是整洁的,可维护的,如果以后要在其他节点对上执行相同的操作,则可以轻松扩展。