mirror of
https://github.com/open-webui/open-webui.git
synced 2026-06-13 19:20:05 +00:00
fix: delete Qdrant points by ID so memory deletions don't orphan vectors (#25495)
The Qdrant backends implemented delete(ids=...) as a payload filter on
metadata.id, but points are stored with the item id as the Qdrant point id
(see _create_points), and not every point carries an id in its payload.
Memory points store only {created_at} in metadata (KB metadata embeddings
likewise), so deleting a single memory matched nothing and left an orphaned
vector that kept being injected into RAG context.
Delete by point id instead: PointIdsList for the standard backend, and a
tenant-scoped HasIdCondition for multitenancy (point ids are unique, so tenant
isolation is preserved). Filter-based deletion is unchanged.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -216,28 +216,23 @@ class QdrantClient(VectorDBBase):
|
||||
ids: Optional[list[str]] = None,
|
||||
filter: Optional[dict] = None,
|
||||
):
|
||||
# Delete the items from the collection based on the ids.
|
||||
field_conditions = []
|
||||
|
||||
# Delete by point ID: the point ID is the item's id (see _create_points).
|
||||
# Filtering on metadata.id silently misses points whose payload omits an
|
||||
# id (e.g. memories), leaving orphaned vectors behind.
|
||||
if ids:
|
||||
for id_value in ids:
|
||||
(
|
||||
field_conditions.append(
|
||||
models.FieldCondition(
|
||||
key='metadata.id',
|
||||
match=models.MatchValue(value=id_value),
|
||||
),
|
||||
),
|
||||
)
|
||||
elif filter:
|
||||
return self.client.delete(
|
||||
collection_name=f'{self.collection_prefix}_{collection_name}',
|
||||
points_selector=models.PointIdsList(points=ids),
|
||||
)
|
||||
|
||||
field_conditions = []
|
||||
if filter:
|
||||
for key, value in filter.items():
|
||||
(
|
||||
field_conditions.append(
|
||||
models.FieldCondition(
|
||||
key=f'metadata.{key}',
|
||||
match=models.MatchValue(value=value),
|
||||
),
|
||||
),
|
||||
field_conditions.append(
|
||||
models.FieldCondition(
|
||||
key=f'metadata.{key}',
|
||||
match=models.MatchValue(value=value),
|
||||
)
|
||||
)
|
||||
|
||||
return self.client.delete(
|
||||
|
||||
@@ -228,15 +228,17 @@ class QdrantClient(VectorDBBase):
|
||||
return None
|
||||
|
||||
must_conditions = [_tenant_filter(tenant_id)]
|
||||
should_conditions = []
|
||||
if ids:
|
||||
should_conditions = [_metadata_filter('id', id_value) for id_value in ids]
|
||||
# Delete by point ID within the tenant. The point ID is the item's id
|
||||
# (see _create_points); filtering on metadata.id silently misses points
|
||||
# whose payload omits an id (e.g. memories), leaving orphaned vectors.
|
||||
must_conditions.append(models.HasIdCondition(has_id=ids))
|
||||
elif filter:
|
||||
must_conditions += [_metadata_filter(k, v) for k, v in filter.items()]
|
||||
|
||||
return self.client.delete(
|
||||
collection_name=mt_collection,
|
||||
points_selector=models.FilterSelector(filter=models.Filter(must=must_conditions, should=should_conditions)),
|
||||
points_selector=models.FilterSelector(filter=models.Filter(must=must_conditions)),
|
||||
)
|
||||
|
||||
def search(
|
||||
|
||||
Reference in New Issue
Block a user