langgenius / langgenius/dify

Weaviate: deleting a document never removes its vectors — delete_by_ids passes Dify segment ids to delete_by_id as object UUIDs

Open Beginner friendly
#40,457 3 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
156k
Forks
24.6k
Avg merge
22h 9m
Merged PRs (30d)
610

Description

### Self Checks
- [x] I have searched for existing issues, including closed ones.
- [x] I confirm that I am using English to submit this report.

### Dify version
1.16.1 (self-hosted Docker). Also present in 1.13.3 — not a regression from the
VDB workspace refactor (#34900); the same code exists at the old path
`api/core/rag/datasource/vdb/weaviate/weaviate_vector.py`.

### Cloud or Self Hosted
Self Hosted (Docker), `VECTOR_STORE=weaviate`

### Steps to reproduce
1. Create a knowledge base backed by Weaviate and index a few documents.
2. Delete one document through the UI or `DELETE /v1/datasets/{id}/documents/{doc_id}`.
3. `clean_document_task` logs `Cleaned document when document deleted ... succeeded`.
4. Count objects in the Weaviate collection: **none were removed**.

### Actual behaviour
`delete_by_ids` receives Dify **segment ids** (`index_node_id`), which are stored in the
object's `doc_id` property. It passes them to `delete_by_id()`, which expects the
**Weaviate object UUID**. Those UUIDs are `uuid5(URL_NAMESPACE, page_content)` — see
`_get_uuids` in the same file — so the two never coincide.

Every call returns 404, and the 404 is swallowed on purpose:

```python
# api/providers/vdb/vdb-weaviate/src/dify_vdb_weaviate/weaviate_vector.py:378
for uid in ids:
try:
col.data.delete_by_id(uid)
except UnexpectedStatusCodeError as e:
if getattr(e, "status_code", None) != 404:
raise
```

Nothing is deleted, no error surfaces, and the task reports success.

### Expected behaviour
Deleting a document removes its vectors from the collection.

### Impact
Orphan vectors are still returned by the vector search but are dropped afterwards,
because Dify keeps only hits that still have a matching row in Postgres. They silently
consume `top_k` slots, so retrieval quality degrades as deletions accumulate, until a
knowledge base returns nothing at all with no error anywhere.

Measured on one knowledge base: deleting 4 documents left **9165 orphan objects**
(`document_id` no longer present in Postgres). A `dryRun` batch delete with a
`document_id` filter reported the same count, confirming the filter and index are fine —
only the delete path is wrong.

Deleting a whole dataset is unaffected: `clean_dataset_task` drops the entire collection,
a different code path.

### Proposed fix
Delete by filtering on the `doc_id` property, in batches:

```python
batch_size = 100
for start in range(0, len(ids), batch_size):
chunk = ids[start : start + batch_size]
col.data.delete_many(
where=Filter.any_of([Filter.by_property("doc_id").equal(i) for i in chunk])
)
```

`Filter` is already imported in the module. `equal` on a word-tokenised text property
matches the whole token sequence, so distinct UUIDs cannot collide; `contains_any` would
match single hex fragments and over-delete.

Verified on a live instance: 128 segments → 113 objects; after deleting a document with
63 segments, 50 objects remain (113 − 63), zero orphans. Before the change all 113 stayed.

Contributor guide

Open the contributing guide

Research direction

The affected entry point is api/providers/vdb/vdb-weaviate/src/dify_vdb_weaviate/weaviate_vector.py, especially delete_by_ids and _get_uuids. Read those methods first and trace the document cleanup call that supplies segment ids. Done means deleting a document removes its vectors from the Weaviate collection without masking an unrelated failure; dataset deletion is a separate path.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
databases
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.