sqlite: Add test for search with list filters (#4747)

This commit is contained in:
William FH
2025-05-18 23:50:27 -07:00
committed by GitHub
parent 6b28319796
commit 95f92069a7
7 changed files with 127 additions and 6 deletions
@@ -410,11 +410,12 @@ class BaseSqliteStore:
"json_extract(value, '$." + key + "') = " + str(value)
)
else:
# For complex objects, use param binding with JSON serialization
# Complex objects (list, dict, …) compare JSON text
filter_conditions.append(
"json_extract(value, '$." + key + "') = ?"
)
filter_params.append(orjson.dumps(value))
# orjson.dumps returns bytes → decode to str so SQLite sees TEXT
filter_params.append(orjson.dumps(value).decode())
# Vector search branch
if op.query and self.index_config:
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint-sqlite"
version = "2.0.9"
version = "2.0.10"
description = "Library with a SQLite implementation of LangGraph checkpoint saver."
authors = []
requires-python = ">=3.9"
@@ -657,3 +657,63 @@ async def test_list_namespaces(
# Clean up
for namespace in test_namespaces:
await store.adelete(namespace, "dummy")
async def test_search_items(
fake_embeddings: CharacterEmbeddings,
) -> None:
"""Test search_items functionality by calling store methods directly."""
base = "test_search_items"
test_namespaces = [
(base, "documents", "user1"),
(base, "documents", "user2"),
(base, "reports", "department1"),
(base, "reports", "department2"),
]
test_items = [
{"title": "Doc 1", "author": "John Doe", "tags": ["important"]},
{"title": "Doc 2", "author": "Jane Smith", "tags": ["draft"]},
{"title": "Report A", "author": "John Doe", "tags": ["final"]},
{"title": "Report B", "author": "Alice Johnson", "tags": ["draft"]},
]
async with create_vector_store(
fake_embeddings, text_fields=["key0", "key1", "key3"]
) as store:
# Insert test data
for ns, item in zip(test_namespaces, test_items):
key = f"item_{ns[-1]}"
await store.aput(ns, key, item)
# 1. Search documents
docs = await store.asearch((base, "documents"))
assert len(docs) == 2
assert all(item.namespace[1] == "documents" for item in docs)
# 2. Search reports
reports = await store.asearch((base, "reports"))
assert len(reports) == 2
assert all(item.namespace[1] == "reports" for item in reports)
# 3. Pagination
first_page = await store.asearch((base,), limit=2, offset=0)
second_page = await store.asearch((base,), limit=2, offset=2)
assert len(first_page) == 2
assert len(second_page) == 2
keys_page1 = {item.key for item in first_page}
keys_page2 = {item.key for item in second_page}
assert keys_page1.isdisjoint(keys_page2)
all_items = await store.asearch((base,))
assert len(all_items) == 4
john_items = await store.asearch((base,), filter={"author": "John Doe"})
assert len(john_items) == 2
assert all(item.value["author"] == "John Doe" for item in john_items)
draft_items = await store.asearch((base,), filter={"tags": ["draft"]})
assert len(draft_items) == 2
assert all("draft" in item.value["tags"] for item in draft_items)
for ns in test_namespaces:
key = f"item_{ns[-1]}"
await store.adelete(ns, key)
@@ -987,3 +987,63 @@ def test_list_namespaces_operations(
# Clean up
for namespace in test_namespaces:
store.delete(namespace, "dummy")
def test_search_items(
fake_embeddings: CharacterEmbeddings,
) -> None:
"""Test search_items functionality by calling store methods directly."""
base = "test_search_items"
test_namespaces = [
(base, "documents", "user1"),
(base, "documents", "user2"),
(base, "reports", "department1"),
(base, "reports", "department2"),
]
test_items = [
{"title": "Doc 1", "author": "John Doe", "tags": ["important"]},
{"title": "Doc 2", "author": "Jane Smith", "tags": ["draft"]},
{"title": "Report A", "author": "John Doe", "tags": ["final"]},
{"title": "Report B", "author": "Alice Johnson", "tags": ["draft"]},
]
with create_vector_store(
fake_embeddings, text_fields=["key0", "key1", "key3"]
) as store:
# Insert test data
for ns, item in zip(test_namespaces, test_items):
key = f"item_{ns[-1]}"
store.put(ns, key, item)
# 1. Search documents
docs = store.search((base, "documents"))
assert len(docs) == 2
assert all(item.namespace[1] == "documents" for item in docs)
# 2. Search reports
reports = store.search((base, "reports"))
assert len(reports) == 2
assert all(item.namespace[1] == "reports" for item in reports)
# 3. Pagination
first_page = store.search((base,), limit=2, offset=0)
second_page = store.search((base,), limit=2, offset=2)
assert len(first_page) == 2
assert len(second_page) == 2
keys_page1 = {item.key for item in first_page}
keys_page2 = {item.key for item in second_page}
assert keys_page1.isdisjoint(keys_page2)
all_items = store.search((base,))
assert len(all_items) == 4
john_items = store.search((base,), filter={"author": "John Doe"})
assert len(john_items) == 2
assert all(item.value["author"] == "John Doe" for item in john_items)
draft_items = store.search((base,), filter={"tags": ["draft"]})
assert len(draft_items) == 2
assert all("draft" in item.value["tags"] for item in draft_items)
for ns in test_namespaces:
key = f"item_{ns[-1]}"
store.delete(ns, key)
+1 -1
View File
@@ -346,7 +346,7 @@ dev = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "2.0.9"
version = "2.0.10"
source = { editable = "." }
dependencies = [
{ name = "aiosqlite" },
+1 -1
View File
@@ -1365,7 +1365,7 @@ dev = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "2.0.9"
version = "2.0.10"
source = { editable = "../checkpoint-sqlite" }
dependencies = [
{ name = "aiosqlite" },
+1 -1
View File
@@ -430,7 +430,7 @@ dev = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "2.0.9"
version = "2.0.10"
source = { editable = "../checkpoint-sqlite" }
dependencies = [
{ name = "aiosqlite" },