{"record":{"id":"58313b76bea7a152","repo":"MemPalace/mempalace","slug":"facet-counts-does-not-support-local-only-filters","errorCode":null,"errorMessage":"facet_counts does not support local-only filters","messagePattern":"facet_counts does not support local-only filters","errorType":"exception","errorClass":"UnsupportedCapabilityError","httpStatus":null,"severity":"error","filePath":"mempalace/backends/pgvector.py","lineNumber":1234,"sourceCode":"            if self._marker_exists():\n                raise CollectionNotInitializedError(self._collection_name)\n            return 0\n        return self._client.count_rows(self._table)\n\n    def facet_counts(\n        self,\n        field: str,\n        where: Optional[dict] = None,\n        limit: int = 1000,\n    ) -> dict[str, int]:\n        self._ensure_open()\n        # Validate the filter before the existence short-circuit so an\n        # unsupported local-only filter raises even on an unmaterialized\n        # collection — matches the order used by get()/lexical_search() and\n        # qdrant.facet_counts (PR #1868 review).\n        _validate_where(where)\n        if _requires_local_filter(where):\n            raise UnsupportedCapabilityError(\"facet_counts does not support local-only filters\")\n        if not self._table_exists():\n            if self._marker_exists():\n                raise CollectionNotInitializedError(self._collection_name)\n            return {}\n        return self._client.facet_counts(self._table, field=field, where=where, limit=limit)\n\n    def lexical_search(self, *, query: str, n_results: int = 10, where: Optional[dict] = None):\n        _validate_where(where)\n        pushdown = None if _requires_local_filter(where) else where\n        rows = self._scroll(where=pushdown, with_embedding=False)\n        rows = [row for row in rows if _matches_where(row[\"metadata\"], where)]\n        scores = _bm25_scores(query, [row[\"document\"] for row in rows])\n        hits = [\n            LexicalHit(\n                id=row[\"id\"],\n                document=row[\"document\"],\n                metadata=row[\"metadata\"],\n                score=score,","sourceCodeStart":1216,"sourceCodeEnd":1252,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/backends/pgvector.py#L1216-L1252","documentation":"facet_counts() on the pgvector backend translates the where filter into SQL. Some filter shapes (per _requires_local_filter) cannot be expressed in SQL pushdown and would need row-by-row local evaluation, which facet counting does not implement — so it raises UnsupportedCapabilityError. Validation happens before the unmaterialized-table short-circuit, so the error fires consistently even for empty collections.","triggerScenarios":"Calling facet_counts(field=\"room\", where={\"tags\": {\"$in\": [\"a\",\"b\"]}}) or any filter containing operators/shapes flagged as local-only by _requires_local_filter.","commonSituations":"Reusing a complex filter that worked for query() (which falls back to _query_local_exact) on facet_counts(); generic facet widgets that let users build arbitrary ChromaDB-style filters.","solutions":["Simplify the filter to operators pgvector can push down (equality, $and/$or of simple clauses).","Compute facet counts client-side: scroll/get the filtered rows and count field values in Python.","Catch UnsupportedCapabilityError and degrade to a client-side aggregation path."],"exampleFix":"# before\ncounts = col.facet_counts(field=\"room\", where={\"tags\": {\"$in\": [\"a\", \"b\"]}})\n\n# after\nrows = col.get(where={\"tags\": {\"$in\": [\"a\", \"b\"]}}, include=[\"metadatas\"])\ncounts = Counter(r[\"room\"] for r in rows[\"metadatas\"])","handlingStrategy":"fallback","validationCode":"# only pushdown-safe filters: equality + $and/$or\nsafe = all(not isinstance(v, dict) or set(v) <= {\"$eq\"} for v in where.values()) if where else True\ncounts = col.facet_counts(field=f, where=where) if safe else client_side_facets(col, f, where)","typeGuard":"def is_pushdown_safe(where: dict) -> bool:\n    return not _requires_local_filter(where)  # if importable; else whitelist operators","tryCatchPattern":"try:\n    counts = col.facet_counts(field=\"room\", where=filters)\nexcept UnsupportedCapabilityError:\n    rows = col.get(where=filters, include=[\"metadatas\"])\n    counts = Counter(r[\"room\"] for r in rows[\"metadatas\"])","preventionTips":["Keep facet filters simple (equality, $and/$or).","Catch UnsupportedCapabilityError in generic facet tooling and fall back to client-side counting.","Reuse one validated filter builder for facets across backends."],"tags":["pgvector","filters","unsupported-operation","facets"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}