【问题标题】:neo4j unique constraint violation when using MERGE?使用 MERGE 时违反 neo4j 唯一约束?
【发布时间】:2022-11-11 05:34:09
【问题描述】:

我有通过 python 驱动程序将节点添加到 neo4j 实例的代码。节点基于标签和“名称”进行匹配,并且对标签和“名称”施加了唯一约束。我使用 MERGE 以便我匹配或创建一个节点,具体取决于它是否存在。然后,如果我匹配现有节点但有新属性要从我的 python 图中添加,我会添加新属性。即使我编辑具有新属性的节点,此代码也一直有效。但是,最近当我更改节点(17)上的名称为“My”和标签“Person”的“count”属性时,它发生了故障。为什么更改(而不是添加)属性会导致此代码出现问题?

node_label 和 node_name 是 networkx 图中节点的属性,other_props 也是如此(other_props 包含“计数”)。

这是我的代码:

query = (
                f"MERGE (n: {node_label} {{name: \"{node_name}\"}})\n"
                f"ON CREATE\n"
                f"    SET n.created = timestamp()\n"
                f"SET n += {{{other_props}}}\n"
                f"RETURN n, n.created"
            )

我的理解是,如果我基于唯一约束中涉及的相同属性进行 MERGE 合并,则使用 MERGE 应该不可能违反我的唯一约束。

但我以某种方式收到以下错误:

如果有人对我如何可能违反唯一约束有任何想法,请告诉我。我对 Cypher 还很陌生,并且希望变得更好。

在 neo4j 浏览器中,完全相同的查询没有问题...

【问题讨论】:

  • 请给我们完整的代码,其中显示了 node_label、node_name 和 other_props 的值。我使用 python 驱动程序尝试了您的查询,它运行良好,但我对值进行了硬编码。

标签: python neo4j


【解决方案1】:

这是我想象的精简版你正在做的事情的一个简短的 Python 位

import neo4j
import neo4j.exceptions


def main():
    uri = "neo4j://localhost:7687"
    user = "neo4j"
    password = "pass"
    auth = neo4j.basic_auth(user, password)
    with neo4j.GraphDatabase.driver(uri, auth=auth) as driver:
        with driver.session() as session:
            create_query = "CREATE (:Person {name: $name})"
            match_query = "MATCH (p:Person {name: $name}) RETURN p"

            merge_query = ("MERGE (p:Person {name: $name}) "
                           "ON CREATE "
                           "  SET p.name=$name2")
            wrong_merge = ("MERGE (p:Person {name: $name}) "
                           "ON CREATE "
                           "  SET p.name='created' "
                           "SET p.name=$name2")

            session.run("CREATE CONSTRAINT person_name IF NOT EXISTS "
                        "FOR (p:Person) REQUIRE p.name IS UNIQUE")
            session.run("MATCH (n) DETACH DELETE n")
            session.run(create_query, name="Alice")
            session.run(create_query, name="Bob")
            try:
                session.run(create_query, name="Alice")
                assert False  # should not get here
            except neo4j.exceptions.Neo4jError as e:
                print(f"Fails as expected: {e}")

            # noop because Alice already exists
            session.run(merge_query, name="Alice", name2="Alice2")
            assert len(list(session.run(match_query, name="Alice"))) == 1
            assert not list(session.run(match_query, name="Alice2"))

            # finds Alice and always updates
            session.run(wrong_merge, name="Alice", name2="Alice2")
            assert not list(session.run(match_query, name="Alice"))
            assert len(list(session.run(match_query, name="Alice2"))) == 1

            # You can't rename Alice to Bob, bcs. Bob already exists
            try:
                session.run(wrong_merge, name="Alice2", name2="Bob")
                assert False  # should not get here
            except neo4j.exceptions.Neo4jError as e:
                print(f"Fails as expected: {e}")


if __name__ == "__main__":
    main()

查看wrong_merge 查询。第二个SET 总是被执行,不管MERGE 是创建一个新节点还是匹配一个现有节点。我强烈怀疑name 出现在您的other_props 中,因此试图将名称更改为已经存在的名称。

最后,无论是否是这种情况,我建议您习惯于通过字符串连接或插值来构建任何数据库查询(无论是哪个数据库)。几乎所有的数据库都提供了一种参数化查询的方法。这有两个好处:

  1. 数据库可以优化你的查询,只是参数不同
  2. 您永远不会陷入使自己容易受到查询注入攻击的情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多