mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-25 09:02:25 +02:00
sqlite: Add test for search with list filters (#4747)
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
Generated
+1
-1
@@ -346,7 +346,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-checkpoint-sqlite"
|
||||
version = "2.0.9"
|
||||
version = "2.0.10"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiosqlite" },
|
||||
|
||||
Generated
+1
-1
@@ -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" },
|
||||
|
||||
Generated
+1
-1
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user