Hi LangGraph team,
I’m implementing a custom BaseCheckpointSaver for a database that doesn’t support JSON path queries on large text columns (similar to how older database versions lack JSON support).
Context:
- My database stores metadata as a large text field (JSON string)
- Unlike SQLite’s json_extract() or PostgreSQL’s JSONB operators, there’s no SQL function to query JSON fields inside this column type
- This means I can’t do server-side filtering like the built-in checkpointers do
So I need to:
- Fetch all checkpoints for a thread (filtered by thread_id and checkpoint_ns only)
- Apply the filter parameter client-side in Python
- Then apply limit after filtering
My current implementation:
def list(self, config, *, filter=None, before=None, limit=None):
rows = self._fetch_checkpoints(thread_id, checkpoint_ns, before_id)
matched = 0
for row in rows:
checkpoint_tuple = self._row_to_tuple(row)
# Client-side metadata filter
if filter and not self._matches_metadata_filter(
checkpoint_tuple.metadata, filter
):
continue
matched += 1
yield checkpoint_tuple
# Apply limit AFTER filtering
if limit is not None and matched >= limit:
break
def _matches_metadata_filter(self, metadata, filter):
“”“Check if metadata matches all filter conditions.”“”
return all(metadata.get(k) == v for k, v in filter.items())
Questions:
- Is this approach acceptable? The conformance tests pass, but I want to ensure I’m not missing any edge cases or violating expectations about the filter parameter.
- Performance considerations: Most queries will fetch ~100 checkpoints per thread before filtering. Is there a recommended threshold where client-side filtering becomes problematic?
- Filter semantics: Should I support only exact equality (metadata.get(k) == v), or are there other operators I should handle for compatibility?
- Limit behavior: I’m applying limit AFTER metadata filtering (so if filter matches 10 rows, limit=5 returns 5 rows). Is this the expected behavior, or should limit be applied before
filtering? - Empty filter: When filter=None or filter={}, I skip filtering entirely. Correct?
- Nested keys: Should I handle nested JSON paths like {“user.role”: “admin”} or only top-level keys?
Any guidance on client-side filtering best practices would be appreciated!
Additional info:
- All 9 conformance tests pass with this implementation
- Typical use case: ~10-50 checkpoints per thread
- Most metadata filters are simple equality checks on 1-2 keys
Thanks!