【问题标题】:Why is this query increasing count by more than expected? (cypher/neo4j)?为什么此查询的计数增加超过预期? (密码/neo4j)?
【发布时间】:2014-03-18 17:21:21
【问题描述】:

我有连接到内容节点的术语节点,并且一个查询意味着更新内容节点到这些术语节点的连接。

首先,我减少最初附加的每个术语节点的连接内容节点的数量,然后删除关系。

之后,我创建了与所有指定术语节点的新关系,尝试将每个新连接的术语节点的连接内容节点数增加一个。

问题是,在查询运行后,连接的内容节点的计数并没有增加一,而是增加了看起来像正在连接的新术语节点的总数。

我似乎仍然无法准确掌握查询背后的数据处理方式。我怀疑答案可能涉及对连接的节点进行计数,就像我之前遇到的情况一样。

这里是查询:

    var query = [
    "MATCH  (contentNode:content {UUID: {contentID} })-[r:TAGGED_WITH]->(oldTermNode:term) ",
    "SET oldTermNode.contentConnections = oldTermNode.contentConnections - 1 ",
    "DELETE r ",
    "WITH contentNode ",
    "MATCH (newTermNode:term) ",
    "WHERE newTermNode.UUID IN {termIDs} ",
    "CREATE UNIQUE contentNode-[:TAGGED_WITH]->newTermNode ",
    "SET newTermNode.contentConnections = newTermNode.contentConnections + 1 ",
].join('\n'); 

作为一个附带问题,在更新术语时,许多新术语通常与旧术语相同(用户只添加/删除一两个术语,其余的保持不变)。如果仅删除不会重新连接的关系然后仅添加新术语,是否会更有意义/具有更快的性能?

非常感谢。

【问题讨论】:

  • 您能否分析您的查询并发布执行计划? here's how 我认为您的计数变得不稳定的原因是您在WITH 有多个结果项,因此查询的第二部分被执行了多次。
  • 没错。您的基数是错误的,它是您的第一个 MATCH 生成的路径数,因此第一个匹配后的所有内容都会为每个匹配的路径执行。如果您使用WITH distinct contentNode,您会将该基数折叠回一 (1) 并丢弃所有其他路径。

标签: database graph neo4j cypher


【解决方案1】:

这并不能直接回答您的问题,但我想知道您的条款是否真的需要有一个“contentConnections”属性。如果不是,那么你原来的问题就没有实际意义了。

仅根据您问题中的信息,term.contentConnections 值似乎只是 :TAGGED_WITH 关系指向该术语的次数的计数。如果是这种情况,那么您应该能够通过以下方式获得等效计数:

MATCH ()-[:TAGGED_WITH]->(t:term {UUID:{termId}}) RETURN count(t);

如果您为术语节点的 UUID 属性创建索引(或者,可能更好的是唯一性约束),此查询会非常快。如果这对您有用,那么您可以简化并加快您的其他查询,因为不需要维护 contentConnections 值。

例如,您的原始查询可以简化为:

var query = [
    "MATCH  (contentNode:content {UUID: {contentID} })-[r:TAGGED_WITH]->(oldTermNode:term) ",
    "DELETE r ",
    "WITH contentNode ",
    "MATCH (newTermNode:term) ",
    "WHERE newTermNode.UUID IN {termIDs} ",
    "CREATE UNIQUE contentNode-[:TAGGED_WITH]->newTermNode ",
].join('\n');

【讨论】:

  • 有趣。实际上,我确实对术语 UUID 有唯一性约束——我假设每次运行查询时对每个术语进行计数的计算成本很高。我使用的所有计数是按不同查询中的连接数对节点进行排序,该查询返回与其他术语相关的术语。您认为仅在该查询中进行计数而不是仅从节点中读取值更有意义吗?
  • 我认为这样可能会很好,特别是如果您的查询已经获得了您想要比较的术语节点(因此甚至不需要使用索引来获取术语)。每个节点都有对其传入(以及传出)关系的引用,因此计算特定类型的传入关系集可能不会那么昂贵,尤其是在比较的术语相对较少的情况下。
  • 我选择了您的答案作为正确答案,因为这是我最终实施的解决方案。它确实简化了事情(在其他查询中操作时不必跟踪连接数)。非常感谢!
【解决方案2】:

我已经修改了您的查询,使其能够按照您所描述的那样运行。我所做的是将您的术语收集到一个不同的集合中,并遍历每个节点以增加和减少它们的连接计数。这在理论上应该可行,但我建议采取其他预防措施来保持您的关系计数在术语节点上的一致性。

我假设每个术语可能有无限的连接,并且在计算上轮询每个术语,然后计算连接,然后将其设置为节点上的权重会很昂贵。

MATCH  (contentNode:content {UUID: "1234" })-[r:TAGGED_WITH]->(oldTermNode:term)
WITH contentNode, collect(r) as oldRels, collect(DISTINCT oldTermNode) as oldTermNodes
FOREACH (oldTermNode in oldTermNodes | 
    SET oldTermNode.contentConnections = oldTermNode.contentConnections - 1)
FOREACH (r in oldRels | DELETE r)
WITH contentNode
MATCH (newTermNode:term)
WHERE newTermNode.UUID IN ["1112", "1113"]
CREATE UNIQUE (contentNode)-[:TAGGED_WITH]->(newTermNode)
WITH collect(DISTINCT newTermNode) as newTermNodes
FOREACH (newTermNode in newTermNodes |
    SET newTermNode.contentConnections = newTermNode.contentConnections + 1)

您需要重新插入参数,我为实际测试构建了此代码示例以确保其有效。

作为一个附带问题,在更新条款时,通常很多新的 条款与旧条款相同(用户只添加/删除一个或 两个术语,其余的保持不变)。它会更有意义/有吗 如果只有不存在的关系,则性能更快 重新连接被删除,然后只添加了新条款?

您可以通过指定只需要不在 newTermNode 集合中的 oldTermNode 来修改查询。所以是的,回答你的问题,这将防止不必要的写入,从而提高性能。您只需要确保从 newTermNodes 集合中删除任何多余的术语,这样脚本最后一行中的这些术语的 contentConnections 就不会增加。

【讨论】:

  • 感谢您的回答,我非常感谢 - 我曾尝试在术语节点上使用 collect 但忘记包含不同的约束。快速提问——无限期连接是什么意思?我对图表很陌生,需要一段时间才能完全理解。再次感谢!
  • 无限意味着您可以将短语“OF THE”连接到数百万个内容节点。如果您在每次想为应用程序查询某些内容时都计算连接数,那么在这种规模下,您的性​​能会随着时间的推移而下降。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多