{"record":{"id":"cc705445329f35f3","repo":"MemPalace/mempalace","slug":"operator-key-r-not-supported-by-qdrant","errorCode":null,"errorMessage":"operator {key!r} not supported by qdrant","messagePattern":"operator (.+?) not supported by qdrant","errorType":"exception","errorClass":"UnsupportedFilterError","httpStatus":null,"severity":"error","filePath":"mempalace/backends/qdrant.py","lineNumber":132,"sourceCode":"        for term, freq in tf.items():\n            num = freq * (k1 + 1)\n            den = freq + k1 * (1 - b + b * dl / avgdl)\n            score += float(idf[term]) * num / den\n        scores.append(score)\n    return scores\n\n\ndef _validate_where(where: Optional[dict]) -> None:\n    if not where:\n        return\n    stack = [where]\n    while stack:\n        node = stack.pop()\n        if not isinstance(node, dict):\n            continue\n        for key, value in node.items():\n            if key.startswith(\"$\") and key not in _SUPPORTED_OPERATORS:\n                raise UnsupportedFilterError(f\"operator {key!r} not supported by qdrant\")\n            if isinstance(value, dict):\n                stack.append(value)\n            elif isinstance(value, list):\n                stack.extend(item for item in value if isinstance(item, dict))\n\n\ndef _coerce_comparable(value: Any):\n    if isinstance(value, bool):\n        return int(value)\n    return value\n\n\ndef _compare(actual: Any, op: str, expected: Any) -> bool:\n    actual = _coerce_comparable(actual)\n    expected = _coerce_comparable(expected)\n    if op == \"$eq\":\n        return actual == expected\n    if op == \"$ne\":","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/backends/qdrant.py#L114-L150","documentation":"The qdrant backend supports only a fixed set of filter operators (_SUPPORTED_OPERATORS). _validate_where walks the whole filter tree, and any $-prefixed key outside that set raises UnsupportedFilterError before any request hits Qdrant — so unsupported ChromaDB-style filters fail fast with a precise message instead of behaving inconsistently.","triggerScenarios":"Passing where={\"meta\": {\"$nin\": [1,2]}} or operators like $ne/$not (whichever are absent from _SUPPORTED_OPERATORS) to get/query/count on a qdrant collection.","commonSituations":"Copying filters from ChromaDB code; a shared filter-builder utility emitting the full Mongo-style operator set against multiple backends.","solutions":["Rewrite the filter using only the operators listed in qdrant.py's _SUPPORTED_OPERATORS (equality, $and/$or, common comparators).","Fetch with a broader pushdown filter and post-filter rows locally via _matches_where-compatible logic.","Centralize filter construction per backend rather than sharing one Mongo-dialect builder."],"exampleFix":"# before\ncol.get(where={\"status\": {\"$ne\": \"archived\"}})\n\n# after\ncol.get(where={\"$or\": [{\"status\": \"active\"}, {\"status\": \"pending\"}]})","handlingStrategy":"validation","validationCode":"SUPPORTED = {\"$and\", \"$or\", \"$eq\", \"$ne\", \"$gt\", \"$gte\", \"$lt\", \"$lte\", \"$in\", \"$contains\"}\ndef validate_filter(where):\n    stack = [where]\n    while stack:\n        node = stack.pop()\n        if not isinstance(node, dict):\n            continue\n        for k in node:\n            if k.startswith(\"$\") and k not in SUPPORTED:\n                raise ValueError(f\"filter uses unsupported operator {k}\")\n            v = node[k]\n            if isinstance(v, dict): stack.append(v)\n            elif isinstance(v, list): stack.extend(x for x in v if isinstance(x, dict))\n    return where","typeGuard":"def is_supported_filter(where: dict, supported: set) -> bool:\n    stack = [where]\n    while stack:\n        node = stack.pop()\n        if not isinstance(node, dict): continue\n        for k, v in node.items():\n            if k.startswith(\"$\") and k not in supported: return False\n            if isinstance(v, dict): stack.append(v)\n            elif isinstance(v, list): stack.extend(x for x in v if isinstance(x, dict))\n    return True","tryCatchPattern":"try:\n    col.get(where=filters)\nexcept UnsupportedFilterError as e:\n    logger.warning(\"filter rejected by qdrant, simplifying: %s\", e)\n    col.get(where=simplify(filters))","preventionTips":["Keep one whitelist of filter operators per backend in your codebase.","Validate filters client-side before sending them to any backend.","Avoid niche Mongo operators ($nin, $nor, $regex) in portable filter code."],"tags":["qdrant","filters","unsupported-operation"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}