Skip to content

Commit b83301e

Browse files
fix: FalkorDB changing pagination to standard method (#3688)
1 parent 55265c1 commit b83301e

2 files changed

Lines changed: 37 additions & 24 deletions

File tree

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -607,25 +607,24 @@ def get_metadata_field_unique_values(
607607
self,
608608
metadata_field: str,
609609
search_term: str | None = None,
610-
size: int | None = 10,
611-
after: dict[str, Any] | None = None,
612-
) -> tuple[list[Any], dict[str, Any] | None]:
610+
from_: int = 0,
611+
size: int = 10,
612+
) -> tuple[list[str], int]:
613613
"""
614614
Return distinct values for the given metadata field with optional filtering and pagination.
615615
616616
:param metadata_field: Metadata field name. May include or omit the `meta.` prefix.
617617
:param search_term: Optional case-insensitive substring filter applied to the metadata
618618
field's own value.
619-
:param size: Maximum number of values to return per page. Defaults to 10 000.
620-
:param after: Pagination cursor returned by a previous call. Pass `None` for the first page.
621-
:returns: Tuple of `(values, next_cursor)`. `next_cursor` is `None` on the last page.
619+
:param from_: The offset for pagination (0-based).
620+
:param size: Maximum number of values to return per page. Defaults to 10.
621+
:returns: Tuple of `(values, total_count)`. `total_count` is the number of distinct
622+
values matching the filter, independent of pagination.
622623
"""
623624
self._ensure_connected()
624625
field = metadata_field[5:] if metadata_field.startswith("meta.") else metadata_field
625-
offset = after.get("offset", 0) if after else 0
626-
limit = size if size is not None else 10000
627626

628-
query_params: dict[str, Any] = {}
627+
query_params: dict[str, Any] = {"from_": from_, "size": size}
629628
where_parts = [f"d.{field} IS NOT NULL"]
630629
if search_term:
631630
where_parts.append(f"toLower(toString(d.{field})) CONTAINS toLower($search_term)")
@@ -634,15 +633,16 @@ def get_metadata_field_unique_values(
634633
where = " AND ".join(where_parts)
635634
cypher = (
636635
f"MATCH (d:{self.node_label}) WHERE {where} "
637-
f"RETURN DISTINCT d.{field} AS val "
636+
f"WITH DISTINCT d.{field} AS val "
638637
f"ORDER BY val "
639-
f"SKIP {offset} LIMIT {limit + 1}"
638+
f"WITH collect(val) AS vals "
639+
f"RETURN vals[$from_..$from_ + $size] AS page, size(vals) AS total"
640640
)
641641
result = self.graph.query(cypher, query_params)
642-
rows = result.result_set
643-
values = [row[0] for row in rows[:limit]]
644-
next_cursor: dict[str, Any] | None = {"offset": offset + limit} if len(rows) > limit else None
645-
return values, next_cursor
642+
if not result.result_set:
643+
return [], 0
644+
page, total = result.result_set[0]
645+
return list(page), total
646646

647647
# ------------------------------------------------------------------
648648
# Internal retrieval helpers (called by retriever components)

integrations/falkordb/tests/test_document_store.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -371,26 +371,39 @@ def test_get_metadata_field_min_max(self, mock_falkordb, rows, expected):
371371

372372
def test_get_metadata_field_unique_values(self, mock_falkordb):
373373
_, _, graph = mock_falkordb
374-
graph.query.side_effect = [_result([]), _result([]), _result([["A"], ["B"], ["C"]])]
375-
values, cursor = FalkorDBDocumentStore().get_metadata_field_unique_values("category", size=10)
374+
graph.query.side_effect = [_result([]), _result([]), _result([[["A", "B", "C"], 3]])]
375+
values, total = FalkorDBDocumentStore().get_metadata_field_unique_values("category", size=10)
376376
assert values == ["A", "B", "C"]
377-
assert cursor is None
377+
assert total == 3
378378

379379
def test_get_metadata_field_unique_values_pagination(self, mock_falkordb):
380380
_, _, graph = mock_falkordb
381-
graph.query.side_effect = [_result([]), _result([]), _result([["A"], ["B"], ["C"]])]
382-
values, cursor = FalkorDBDocumentStore().get_metadata_field_unique_values("category", size=2)
383-
assert values == ["A", "B"]
384-
assert cursor == {"offset": 2}
381+
all_values = ["A", "B", "C", "D", "E"]
382+
total = len(all_values)
383+
pages = [all_values[i : i + 2] for i in range(0, total, 2)]
384+
graph.query.side_effect = [_result([]), _result([]), *(_result([[page, total]]) for page in pages)]
385+
store = FalkorDBDocumentStore()
386+
387+
for page_index, expected_page in enumerate(pages):
388+
from_ = page_index * 2
389+
values, returned_total = store.get_metadata_field_unique_values("category", from_=from_, size=2)
390+
assert values == expected_page
391+
assert returned_total == total
392+
_, params = graph.query.call_args[0]
393+
assert params["from_"] == from_
394+
assert params["size"] == 2
395+
396+
# last page is a partial page, smaller than the requested size
397+
assert len(pages[-1]) == 1
385398

386399
def test_get_metadata_field_unique_values_search_term_case_insensitive(self, mock_falkordb):
387400
_, _, graph = mock_falkordb
388-
graph.query.side_effect = [_result([]), _result([]), _result([["Apple"]])]
401+
graph.query.side_effect = [_result([]), _result([]), _result([[["Apple"], 1]])]
389402
values, _ = FalkorDBDocumentStore().get_metadata_field_unique_values("category", search_term="APP")
390403
assert values == ["Apple"]
391404
cypher, params = graph.query.call_args[0]
392405
assert "toLower(toString(d.category)) CONTAINS toLower($search_term)" in cypher
393-
assert params == {"search_term": "APP"}
406+
assert params == {"from_": 0, "size": 10, "search_term": "APP"}
394407

395408
def test_close(self):
396409
store = FalkorDBDocumentStore()

0 commit comments

Comments
 (0)