{"record":{"id":"f1123a79a93e0b1b","repo":"jd-opensource/joyagent-jdgenie","slug":"ids-filters","errorCode":null,"errorMessage":"❌ 必须提供 ids 或 filters 中至少一个参数","messagePattern":"❌ 必须提供 ids 或 filters 中至少一个参数","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"genie-tool/genie_tool/util/qdrant_utils.py","lineNumber":168,"sourceCode":"        # 如果已经是 PointStruct 列表，则直接使用\n        \n        return self.client.upsert(\n            collection_name=self.collection_name,\n            points=points\n        )\n    \n    def delete(self, ids=None, filters=None):\n        \"\"\"\n        删除向量点，支持两种方式：\n        1. 指定 id 列表删除\n        2. 使用过滤条件删除（推荐用于复杂场景）\n\n        :param ids: int 或 List[int]，要删除的点 ID\n        :param filters: dict，过滤条件，格式同 search() 中的 filters\n        :return: 删除操作响应\n        \"\"\"\n        if ids is None and filters is None:\n            raise ValueError(\"❌ 必须提供 ids 或 filters 中至少一个参数\")\n        \n        if ids is not None:\n            if isinstance(ids, int):\n                ids = [ids]\n            delete_request = self.client.delete(\n                    collection_name=self.collection_name,\n                    points=ids  # 👈 旧版支持\n                )\n        else:\n            # 构建 filter 对象\n            must_conditions = []\n            for key, val in filters.items():\n                if isinstance(val, (str, bool, int, float)):\n                    must_conditions.append(\n                        FieldCondition(key=key, match=MatchValue(value=val))\n                    )\n                elif isinstance(val, list):\n                    must_conditions.append(","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/jd-opensource/joyagent-jdgenie/blob/2417e0b8b636d941ad5fb14c59b20dddfef5375d/genie-tool/genie_tool/util/qdrant_utils.py#L150-L186","documentation":"QdrantUtils.delete raises ValueError when neither ids nor filters is provided, because Qdrant's delete API requires a point selector — either explicit point IDs or a filter. The guard prevents an ambiguous/empty delete request.","triggerScenarios":"Calling client_wrapper.delete() with ids=None and filters=None, e.g. delete() with no args, or both parameters defaulting after a failed variable assignment.","commonSituations":"Building delete calls dynamically where the id/filter variables end up unset; copy-pasted calls dropping arguments; scripting bulk cleanup where the condition list came back empty.","solutions":["Pass point ids: delete(ids=42) or delete(ids=[1,2,3])","Pass a filter dict matching your search() filters syntax, e.g. delete(filters={\"must\": [{\"key\": \"status\", \"match\": {\"value\": \"old\"}}]})","Guard the call site: raise/log before calling delete if both selectors are empty instead of hitting the API","If the intent is to clear a collection, use a dedicated delete-collection API rather than delete() with no selector"],"exampleFix":"// before\nqdrant.delete()  # ValueError\n// after\nqdrant.delete(ids=[101, 102])\n# or\nqdrant.delete(filters={\"key\": \"tenant\", \"match\": {\"value\": \"test\"}})","handlingStrategy":"validation","validationCode":"def safe_delete(qdrant, ids=None, filters=None):\n    if ids is None and filters is None:\n        raise ValueError('delete requires ids or filters')\n    return qdrant.delete(ids=ids, filters=filters)","typeGuard":"def has_delete_selector(ids, filters):\n    return ids is not None or filters is not None","tryCatchPattern":"try:\n    qdrant.delete(ids=ids, filters=filters)\nexcept ValueError as e:\n    logger.error('refusing empty delete: %s', e)  # no points deleted","preventionTips":["Never call delete() without at least one selector","Assert selector presence in wrapper utilities","For full wipes use collection-level delete APIs","Dry-run the filter against search() first to confirm what would be deleted"],"tags":["qdrant","vector-database","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"2417e0b8b636d941ad5fb14c59b20dddfef5375d","analyzedAt":"2026-09-08T11:28:19.414Z","contentChangedAt":"2026-09-08T11:28:19.414Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}