Skip to content

graph.create() with py2neo.Relationship Overwrites Existing Relationships #967

Description

@noaft

Description

When creating multiple relationships of the same type between the same two nodes using py2neo.Relationship and graph.create(), Neo4j unexpectedly overwrites the existing relationship instead of creating a new, distinct one. This behavior leads to data loss, as only the last created relationship is persisted. This appears to stem from how py2neo handles relationship equality, which is based on the start node, end node, and relationship type. The official py2neo documentation notes that this behavior differs from Neo4j itself, which permits multiple relationships of the same type between the same nodes.

Reproduction

The following Python code demonstrates the issue:

from py2neo import Graph, Relationship, Node

graph = Graph("bolt://localhost:7687", auth=("neo4j", "password"))

a = Node("Person", name="Alice")
b = Node("Person", name="Bob")

graph.merge(a, "Person", "name")
graph.merge(b, "Person", "name")

# Create two identical relationships (same start, end, type)
r1 = Relationship(a, "KNOWS", b, since=2020)
r2 = Relationship(a, "KNOWS", b, since=2021)

graph.create(r1)
graph.create(r2)

Expected Behavior

Two distinct KNOWS relationships should exist between the "Alice" and "Bob" nodes, one with the property since: 2020 and the other with since: 2021. Neo4j inherently supports parallel relationships (i.e., multiple relationships of the same type between the same two nodes).

Actual Behavior

Only one KNOWS relationship between "Alice" and "Bob" exists in the database. The second graph.create(r2) call appears to overwrite the relationship created by graph.create(r1). The single remaining relationship will have the properties of r2 (i.e., since: 2021).

Verification via Cypher

The existence of only a single relationship can be verified with the following Cypher query, which will return only one relationship instead of the expected two:

MATCH (:Person {name: 'Alice'})-[r:KNOWS]->(:Person {name: 'Bob'})
RETURN r

In contrast, creating two relationships directly with Cypher demonstrates that Neo4j itself handles this correctly:

MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2020}]->(b)
CREATE (a)-[:KNOWS {since: 2021}]->(b)

Running the verification query after these Cypher statements will correctly return two distinct relationships.

Suggested Workaround

To create multiple relationships of the same type between two nodes, a direct Cypher query using graph.run() should be used instead of graph.create() with py2neo.Relationship objects.

from py2neo import Graph, Node

graph = Graph("bolt://localhost:7687", auth=("neo4j", "password"))

a = Node("Person", name="Alice")
b = Node("Person", name="Bob")

graph.merge(a, "Person", "name")
graph.merge(b, "Person", "name")

# Use graph.run() with a CREATE Cypher query
graph.run(
    "MATCH (a:Person {name: $a_name}), (b:Person {name: $b_name}) " +
    "CREATE (a)-[:KNOWS {since: 2020}]->(b)",
    a_name="Alice", b_name="Bob"
)

graph.run(
    "MATCH (a:Person {name: $a_name}), (b:Person {name: $b_name}) " +
    "CREATE (a)-[:KNOWS {since: 2021}]->(b)",
    a_name="Alice", b_name="Bob"
)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions