Skip to content

Commit 8cfdcc5

Browse files
test: ValkeyDocumentStore removing duplicated tests already covered by haystack Mixin tests + allow non-schema-typed metadata values in Valkey get_metadata_field_unique_value (#3786)
1 parent ac0c777 commit 8cfdcc5

3 files changed

Lines changed: 16 additions & 191 deletions

File tree

integrations/valkey/src/haystack_integrations/document_stores/valkey/document_store.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,10 +1039,8 @@ def get_metadata_field_unique_values(
10391039
:param size: Number of values to return (default 10).
10401040
:param filters: Optional filters to restrict the documents considered.
10411041
:return: Tuple of (list of unique values for the requested page, total count of unique values).
1042-
:raises ValueError: If the field is not configured for filtering.
10431042
:raises ValkeyDocumentStoreError: If the operation fails.
10441043
"""
1045-
self._validate_metadata_field_names([metadata_field])
10461044
try:
10471045
docs = self.filter_documents(filters=filters)
10481046
return ValkeyDocumentStore._get_metadata_field_unique_values_impl(
@@ -1072,10 +1070,8 @@ async def get_metadata_field_unique_values_async(
10721070
:param size: Number of values to return (default 10).
10731071
:param filters: Optional filters to restrict the documents considered.
10741072
:return: Tuple of (list of unique values for the requested page, total count of unique values).
1075-
:raises ValueError: If the field is not configured for filtering.
10761073
:raises ValkeyDocumentStoreError: If the operation fails.
10771074
"""
1078-
self._validate_metadata_field_names([metadata_field])
10791075
try:
10801076
docs = await self.filter_documents_async(filters=filters)
10811077
return ValkeyDocumentStore._get_metadata_field_unique_values_impl(
@@ -1329,8 +1325,10 @@ def _prepare_document_dict(self, doc: Document) -> dict[str, Any]:
13291325
if isinstance(value, bool):
13301326
value = int(value)
13311327
elif not isinstance(value, (int, float)):
1332-
msg = f"Field '{field_name}' expects numeric value but got {type(value).__name__}"
1333-
raise ValueError(msg)
1328+
# Not indexable as numeric; omit from the index. The original value is still
1329+
# preserved in `payload` above, so type-preserving reads (e.g.
1330+
# get_metadata_field_unique_values) remain correct.
1331+
value = None
13341332

13351333
doc_dict[field_name_with_prefix] = value
13361334

@@ -1491,17 +1489,20 @@ def _get_metadata_field_unique_values_impl(
14911489
) -> tuple[list[Any], int]:
14921490
"""Extract unique values for a metadata field with optional search and pagination."""
14931491
unique_vals: list[Any] = []
1494-
seen: set[str] = set()
1492+
# Key on (type, str value): values that share a string form (e.g. int 1 and str "1")
1493+
# must not collapse into a single entry.
1494+
seen: set[tuple[type, str]] = set()
14951495
for doc in documents:
14961496
val = (doc.meta or {}).get(ValkeyDocumentStore._metadata_field_to_doc_meta_key(metadata_field))
14971497
if val is None:
14981498
continue
14991499
str_val = str(val)
1500-
if str_val in seen:
1500+
dedup_key = (type(val), str_val)
1501+
if dedup_key in seen:
15011502
continue
15021503
if search_term is not None and search_term.lower() not in str_val.lower():
15031504
continue
1504-
seen.add(str_val)
1505+
seen.add(dedup_key)
15051506
unique_vals.append(val)
15061507
unique_vals.sort(key=str)
15071508
total = len(unique_vals)

integrations/valkey/tests/test_document_store.py

Lines changed: 6 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -738,82 +738,6 @@ def test_get_metadata_field_min_max_unknown_field_raises(self, document_store):
738738
with pytest.raises(ValueError, match="not configured for filtering"):
739739
document_store.get_metadata_field_min_max("unknown_field")
740740

741-
def test_get_metadata_field_unique_values(self, document_store):
742-
"""Test get_metadata_field_unique_values returns distinct values and total count."""
743-
docs = [
744-
Document(id="gmv1", content="doc 1", embedding=[0.1, 0.2, 0.3], meta={"category": "apple", "priority": 1}),
745-
Document(id="gmv2", content="doc 2", embedding=[0.2, 0.3, 0.4], meta={"category": "banana", "priority": 2}),
746-
Document(id="gmv3", content="doc 3", embedding=[0.3, 0.4, 0.5], meta={"category": "apple", "priority": 3}),
747-
]
748-
document_store.write_documents(docs)
749-
values, total = document_store.get_metadata_field_unique_values("category", from_=0, size=10)
750-
assert total == 2
751-
assert set(values) == {"apple", "banana"}
752-
assert len(values) == 2
753-
754-
def test_get_metadata_field_unique_values_pagination(self, document_store):
755-
"""Test get_metadata_field_unique_values with from_ and size."""
756-
docs = [
757-
Document(id=f"gmvp{i}", content=f"doc {i}", embedding=[0.1, 0.2, 0.3], meta={"category": f"cat_{i}"})
758-
for i in range(5)
759-
]
760-
document_store.write_documents(docs)
761-
values, total = document_store.get_metadata_field_unique_values("category", from_=1, size=2)
762-
assert total == 5
763-
assert len(values) == 2
764-
assert sorted(values)[0] >= "cat_0"
765-
766-
def test_get_metadata_field_unique_values_with_search_term(self, document_store):
767-
"""Test get_metadata_field_unique_values with search_term filter."""
768-
docs = [
769-
Document(id="gmvs1", content="doc 1", embedding=[0.1, 0.2, 0.3], meta={"category": "apple_pie"}),
770-
Document(id="gmvs2", content="doc 2", embedding=[0.2, 0.3, 0.4], meta={"category": "banana"}),
771-
Document(id="gmvs3", content="doc 3", embedding=[0.3, 0.4, 0.5], meta={"category": "apple_jam"}),
772-
]
773-
document_store.write_documents(docs)
774-
values, total = document_store.get_metadata_field_unique_values(
775-
"category", search_term="apple", from_=0, size=10
776-
)
777-
assert total == 2
778-
assert set(values) == {"apple_pie", "apple_jam"}
779-
780-
def test_get_metadata_field_unique_values_with_filters(self, document_store):
781-
"""Test get_metadata_field_unique_values restricts documents using the filters param."""
782-
docs = [
783-
Document(
784-
id="gmvf1", content="doc 1", embedding=[0.1, 0.2, 0.3], meta={"category": "A", "status": "active"}
785-
),
786-
Document(
787-
id="gmvf2", content="doc 2", embedding=[0.2, 0.3, 0.4], meta={"category": "B", "status": "active"}
788-
),
789-
Document(
790-
id="gmvf3", content="doc 3", embedding=[0.3, 0.4, 0.5], meta={"category": "C", "status": "inactive"}
791-
),
792-
]
793-
document_store.write_documents(docs)
794-
795-
filters = {"field": "meta.status", "operator": "==", "value": "active"}
796-
values, total = document_store.get_metadata_field_unique_values("category", filters=filters)
797-
assert set(values) == {"A", "B"}
798-
assert total == 2
799-
800-
def test_get_metadata_field_unique_values_preserves_non_string_types(self, document_store):
801-
"""Non-string metadata values (e.g. ints) are returned in their original type, not stringified."""
802-
docs = [
803-
Document(id="gmvt1", content="doc 1", embedding=[0.1, 0.2, 0.3], meta={"priority": 1}),
804-
Document(id="gmvt2", content="doc 2", embedding=[0.2, 0.3, 0.4], meta={"priority": 2}),
805-
Document(id="gmvt3", content="doc 3", embedding=[0.3, 0.4, 0.5], meta={"priority": 1}),
806-
]
807-
document_store.write_documents(docs)
808-
values, total = document_store.get_metadata_field_unique_values("priority")
809-
assert total == 2
810-
assert set(values) == {1, 2}
811-
812-
def test_get_metadata_field_unique_values_unknown_field_raises(self, document_store):
813-
"""Test get_metadata_field_unique_values raises for unconfigured field."""
814-
with pytest.raises(ValueError, match="not configured for filtering"):
815-
document_store.get_metadata_field_unique_values("unknown_field")
816-
817741
def test_count_unique_metadata_by_filter_invalid_field_raises(self, document_store):
818742
"""Test count_unique_metadata_by_filter raises for unconfigured field."""
819743
document_store.write_documents(
@@ -1256,17 +1180,19 @@ def test_prepare_document_dict_validates_tag_field_type():
12561180
store._prepare_document_dict(doc)
12571181

12581182

1259-
def test_prepare_document_dict_validates_numeric_field_type():
1260-
"""Test that numeric fields reject non-numeric values."""
1183+
def test_prepare_document_dict_omits_non_numeric_value_from_index():
1184+
"""Test that a non-numeric value for a numeric field is omitted from the index but kept in the payload."""
12611185
store = ValkeyDocumentStore(
12621186
index_name="test_validation",
12631187
embedding_dim=3,
12641188
metadata_fields={"priority": int},
12651189
)
12661190
doc = Document(content="test", embedding=[0.1, 0.2, 0.3], meta={"priority": "high"})
12671191

1268-
with pytest.raises(ValueError, match="Field 'priority' expects numeric value but got str"):
1269-
store._prepare_document_dict(doc)
1192+
doc_dict = store._prepare_document_dict(doc)
1193+
1194+
assert doc_dict["meta_priority"] is None
1195+
assert doc_dict["payload"]["meta"]["priority"] == "high"
12701196

12711197

12721198
@pytest.fixture

integrations/valkey/tests/test_document_store_async.py

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -565,105 +565,3 @@ async def test_get_metadata_field_min_max_empty_store_async(self, document_store
565565
result = await document_store.get_metadata_field_min_max_async("priority")
566566
assert result["min"] is None
567567
assert result["max"] is None
568-
569-
async def test_get_metadata_field_unique_values_async(self, document_store):
570-
"""Test async get_metadata_field_unique_values returns distinct values and total count."""
571-
test_id = str(uuid.uuid4())[:8]
572-
docs = [
573-
Document(
574-
id=f"gmv1_{test_id}",
575-
content="doc 1",
576-
embedding=[0.1, 0.2, 0.3],
577-
meta={"category": "apple", "priority": 1},
578-
),
579-
Document(
580-
id=f"gmv2_{test_id}",
581-
content="doc 2",
582-
embedding=[0.2, 0.3, 0.4],
583-
meta={"category": "banana", "priority": 2},
584-
),
585-
Document(
586-
id=f"gmv3_{test_id}",
587-
content="doc 3",
588-
embedding=[0.3, 0.4, 0.5],
589-
meta={"category": "apple", "priority": 3},
590-
),
591-
]
592-
await document_store.write_documents_async(docs)
593-
values, total = await document_store.get_metadata_field_unique_values_async("category", from_=0, size=10)
594-
assert total == 2
595-
assert set(values) == {"apple", "banana"}
596-
assert len(values) == 2
597-
598-
async def test_get_metadata_field_unique_values_with_search_term_async(self, document_store):
599-
"""Test async get_metadata_field_unique_values with search_term filter."""
600-
test_id = str(uuid.uuid4())[:8]
601-
docs = [
602-
Document(
603-
id=f"gmvs1_{test_id}",
604-
content="doc 1",
605-
embedding=[0.1, 0.2, 0.3],
606-
meta={"category": "apple_pie"},
607-
),
608-
Document(
609-
id=f"gmvs2_{test_id}",
610-
content="doc 2",
611-
embedding=[0.2, 0.3, 0.4],
612-
meta={"category": "banana"},
613-
),
614-
Document(
615-
id=f"gmvs3_{test_id}",
616-
content="doc 3",
617-
embedding=[0.3, 0.4, 0.5],
618-
meta={"category": "apple_jam"},
619-
),
620-
]
621-
await document_store.write_documents_async(docs)
622-
values, total = await document_store.get_metadata_field_unique_values_async(
623-
"category", search_term="apple", from_=0, size=10
624-
)
625-
assert total == 2
626-
assert set(values) == {"apple_pie", "apple_jam"}
627-
628-
async def test_get_metadata_field_unique_values_with_filters_async(self, document_store):
629-
"""Test async get_metadata_field_unique_values restricts documents using the filters param."""
630-
test_id = str(uuid.uuid4())[:8]
631-
docs = [
632-
Document(
633-
id=f"gmvf1_{test_id}",
634-
content="doc 1",
635-
embedding=[0.1, 0.2, 0.3],
636-
meta={"category": "A", "status": "active"},
637-
),
638-
Document(
639-
id=f"gmvf2_{test_id}",
640-
content="doc 2",
641-
embedding=[0.2, 0.3, 0.4],
642-
meta={"category": "B", "status": "active"},
643-
),
644-
Document(
645-
id=f"gmvf3_{test_id}",
646-
content="doc 3",
647-
embedding=[0.3, 0.4, 0.5],
648-
meta={"category": "C", "status": "inactive"},
649-
),
650-
]
651-
await document_store.write_documents_async(docs)
652-
653-
filters = {"field": "meta.status", "operator": "==", "value": "active"}
654-
values, total = await document_store.get_metadata_field_unique_values_async("category", filters=filters)
655-
assert set(values) == {"A", "B"}
656-
assert total == 2
657-
658-
async def test_get_metadata_field_unique_values_async_preserves_non_string_types(self, document_store):
659-
"""Non-string metadata values (e.g. ints) are returned in their original type, not stringified."""
660-
test_id = str(uuid.uuid4())[:8]
661-
docs = [
662-
Document(id=f"gmvt1_{test_id}", content="doc 1", embedding=[0.1, 0.2, 0.3], meta={"priority": 1}),
663-
Document(id=f"gmvt2_{test_id}", content="doc 2", embedding=[0.2, 0.3, 0.4], meta={"priority": 2}),
664-
Document(id=f"gmvt3_{test_id}", content="doc 3", embedding=[0.3, 0.4, 0.5], meta={"priority": 1}),
665-
]
666-
await document_store.write_documents_async(docs)
667-
values, total = await document_store.get_metadata_field_unique_values_async("priority")
668-
assert total == 2
669-
assert set(values) == {1, 2}

0 commit comments

Comments
 (0)