Skip to content

[Bug]: Triplet/EdgeType datapoints are created without belongs_to_set — node-set-filtered triplet search always returns nothing #4822

Description

@roman-taxy

Bug Description

Triplet and EdgeType datapoints are created without belongs_to_set, so node-set-filtered searches over their collections (Triplet_text, EdgeType_relationship_name) can never match anything — silently, and at full-scan cost.

In cognee/tasks/storage/add_data_points.py::_create_triplets_from_graph the triplet datapoint is built as:

triplets.append(
    Triplet(
        id=triplet_id,
        from_node_id=str(source_node_id),
        to_node_id=str(target_node_id),
        text=embeddable_text,
    )
)

Both endpoint nodes are available in node_map and carry belongs_to_set, but the tags are never propagated. Same pattern in cognee/tasks/storage/index_graph_edges.py::create_edge_type_datapointsEdgeType(relationship_name=text, number_of_edges=count) with no tags.

The result on a real corpus (~800k triplets, cognee 1.4.2 + PGVector): 0 of 800,305 Triplet_text payloads carry any belongs_to_set value (all empty arrays), while Entity_name payloads are correctly tagged (73,005/73,005). Every node-set-scoped triplet search (node_name=[...] → payload filter (payload -> 'belongs_to_set') ?| tags) therefore:

  1. returns nothing, always, and
  2. costs a near-full collection pass doing it (we measured 15–20 s per query on the unmatched filter before adding indexes; the correct result would have been found in ~100 ms).

Because the failure mode is an empty-but-valid result, it never throws — retrieval quietly degrades to no-graph-context for every filtered caller.

Steps to Reproduce

  1. add documents with node_set=["A"], cognify.
  2. Inspect the triplet collection: SELECT payload->'belongs_to_set' FROM "Triplet_text" LIMIT 5; → all [] (Entity payloads are tagged, so the pipeline's tagging itself works).
  3. Run a search that reaches the triplet collection with node_name=["A"] → 0 results regardless of content.

Expected behavior

Triplets (and EdgeTypes) inherit node-set membership from their endpoint nodes — e.g. the union of source.belongs_to_set and target.belongs_to_set — so scoped searches over pre-embedded triplets return the scoped subset instead of nothing.

We validated the union approach in production data by backfilling Triplet_text.payload.belongs_to_set via the deterministic triplet id (uuid5(source_id + relationship_name + target_id) from generate_node_id): scoped searches immediately returned correct results in ~75 ms.

Suggested fix

In _create_triplets_from_graph, after resolving source_node/target_node from node_map:

belongs_to_set = sorted(
    set(getattr(source_node, "belongs_to_set", None) or [])
    | set(getattr(target_node, "belongs_to_set", None) or [])
)
triplets.append(
    Triplet(
        id=triplet_id,
        from_node_id=str(source_node_id),
        to_node_id=str(target_node_id),
        text=embeddable_text,
        belongs_to_set=belongs_to_set or None,
    )
)

and the analogous propagation in create_edge_type_datapoints (union over the edges aggregated into each retrieval text).

Environment

  • cognee 1.4.2 (code unchanged in 1.5.3 — verified against the tag)
  • PGVector adapter (pgvector 0.8.2, PostgreSQL 15/17), but the omission is backend-agnostic: the tags are dropped before any adapter sees the datapoint.

Related but distinct: #3914 / #4078 / #4079 cover node-set scoping at query time; this one is the creation-time gap that makes triplet-collection scoping structurally empty.

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