Skip to content

[BUG] merge_entities=True merges entities with different id, type AND name (Person 'Alice' + Organization 'Acme' collapse into one) #1137

Description

@hanifjaafar

Bug Description

With merge_entities=True and default settings, GraphBuilder merges two entities that share
no identifier, no type and no name. A Person named "Alice" and an Organization
named "Acme" are collapsed into a single entity, and the Organization is lost.

merge_entities=True is the value used in the Neo4j example in docs/quickstart.md, so this is
reachable by following the documentation.

This is distinct from #1085 / #1110 / #1111, which concern preserving entity_id aliases and
remapping endpoints when merging entities that genuinely are duplicates. Here the problem is
upstream of that: two entities that are not duplicates by any field are grouped as duplicates.

Steps to Reproduce

No graph store required — reproduces purely in memory:

from semantica.kg import GraphBuilder

entities = [
    {"id": "e1", "type": "Person",       "text": "Alice", "name": "Alice"},
    {"id": "e2", "type": "Organization", "text": "Acme",  "name": "Acme"},
]
relationships = [{"source_id": "e1", "target_id": "e2", "type": "knows"}]

for merge in (False, True):
    g = GraphBuilder(merge_entities=merge).build(
        {"entities": entities, "relationships": relationships}
    )
    ents = g.get("entities") or g.get("nodes") or []
    print(merge, len(ents), [e.get("name") or e.get("text") for e in ents])

Expected Behavior

False 2 ['Alice', 'Acme']
True  2 ['Alice', 'Acme']

Two entities differing in id, type and name are not duplicates under any reasonable
similarity threshold.

Actual Behavior

False 2 ['Alice', 'Acme']
True  1 ['Alice']

The Organization entity is silently dropped. Progress output shows
DuplicateDetector creating one candidate pair and MergeStrategyManager merging via
keep_most_complete, so the pair is being scored as a duplicate rather than discarded by a
threshold.

Persisting the same input to Neo4j writes 1 node instead of 2, which is how this was first noticed.

Environment

  • OS: macOS (Darwin 25.6.0, arm64)
  • Python Version: 3.12.14
  • Semantica Version: 0.6.5 (origin/main @ 2a303cf)
  • Installation Method: pipx (pipx install "semantica[graph-neo4j]")

Notes / possible directions

  • A type guard would catch this specific case cheaply: entities with different type values are
    rarely duplicates, and refusing to merge across types would be a conservative default.
  • Worth checking what similarity the default DuplicateDetector actually assigns to this pair —
    if it is above threshold, the scoring may be dominated by a field that is empty in both
    (both entities here have no properties), which would make short/sparse entities merge readily.
  • Silently dropping an entity is a strong default. Even with merging enabled, reporting what was
    merged into what (or requiring an explicit strategy) would make the behaviour auditable.

Happy to test a patch against this reproduction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions