diff --git a/backend/open_webui/retrieval/vector/dbs/qdrant.py b/backend/open_webui/retrieval/vector/dbs/qdrant.py index acf4e61993..7156a16c53 100644 --- a/backend/open_webui/retrieval/vector/dbs/qdrant.py +++ b/backend/open_webui/retrieval/vector/dbs/qdrant.py @@ -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( diff --git a/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py b/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py index 15e72f1d16..9b717644c5 100644 --- a/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py +++ b/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py @@ -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(