Skip to content

fix: paginate wildcard listing to prevent documents missing from Knowledge view - #2198

Open
yogichipalkatti wants to merge 2 commits into
mainfrom
fix/knowledge-missing-files
Open

fix: paginate wildcard listing to prevent documents missing from Knowledge view#2198
yogichipalkatti wants to merge 2 commits into
mainfrom
fix/knowledge-missing-files

Conversation

@yogichipalkatti

@yogichipalkatti yogichipalkatti commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Addresses Issue #1980. Both the default listing and the filter-panel sources dropdown call POST /api/search with query: "*". The backend responds with a match_all OpenSearch query and returns results sorted by internal document order (all scores are 1.0 for a match_all). Because the limit was one hard number — 10,000 chunks — large documents that happened to be indexed later, or whose chunks landed past position 10,000 in the shard, simply never appeared in the response. Their filenames were never in the payload, so the frontend never put them in the listing. Searching "Yes_" worked because semantic ranking scores those exact files at the top and surfaces all of them within the first 100 results.

The four changes address this in two complementary ways:

src/auth_context.py + src/api/search.py + src/services/search_service.py — add an offset parameter that flows from the HTTP body all the way into OpenSearch's "from" field. This is the standard OpenSearch pagination mechanism (equivalent to SQL OFFSET). Since offset defaults to 0, every existing caller is completely unaffected.

useGetSearchQuery.ts — the wildcard path now loops: it fetches 1,000 chunks at offset=0, then 1,000 at offset=1000, and so on, merging all chunks into a single fileMap keyed by filename until a page returns fewer than 1,000 chunks (the final page), at which point the loop stops. Every filename in the index is guaranteed to pass through the merge logic. Non-wildcard searches (any real text query) are entirely unchanged.

The aggregation size fixes in search_service.py (data_sources 20→10,000, owners 10→1,000, etc.) resolve the parallel issue where the filter-panel's Document Types, Owners, and Connector Types dropdowns were also silently truncated to their first 10 entries.

Summary by CodeRabbit

  • New Features

    • Wildcard searches now retrieve and combine results across multiple pages, improving completeness for large result sets.
    • Search requests support pagination, allowing results to be retrieved from a specified starting position.
    • Search filtering supports larger result groups across data sources, document types, owners, and connector types.
  • Bug Fixes

    • Search warnings are now preserved when results are combined.
    • Improved consistency in error handling across paginated and standard searches.

@github-actions github-actions Bot added frontend 🟨 Issues related to the UI/UX backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) bug 🔴 Something isn't working. labels Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Search requests now accept offsets. The backend propagates offsets through context into OpenSearch. Wildcard frontend searches fetch 1,000-result pages, merge chunks by file, preserve warnings, and stop at the final page or safety limit.

Changes

Search pagination

Layer / File(s) Summary
Search offset contract and context
src/api/search.py, src/auth_context.py
The search request accepts a nonnegative offset. The API forwards it, and async context provides offset accessors.
Search service pagination
src/services/search_service.py
SearchService.search stores the offset for search_tool, which applies it to OpenSearch. Aggregation bucket limits are increased.
Frontend page retrieval and aggregation
frontend/app/api/queries/useGetSearchQuery.ts
Wildcard searches fetch repeated 1,000-result pages, merge chunks by file identity, preserve first-page warnings, and enforce stopping conditions. Shared helpers handle requests and aggregation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FrontendSearchQuery
  participant SearchAPI
  participant SearchService
  participant OpenSearch
  FrontendSearchQuery->>SearchAPI: request wildcard search with offset
  SearchAPI->>SearchService: forward offset and filters
  SearchService->>OpenSearch: fetch page with from=offset
  OpenSearch-->>SearchService: return chunks and warnings
  SearchService-->>SearchAPI: return search page
  SearchAPI-->>FrontendSearchQuery: return page results
  FrontendSearchQuery->>FrontendSearchQuery: merge chunks by file identity
Loading

Suggested labels: enhancement

Suggested reviewers: phact, edwinjosechittilappilly, lucaseduoli

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the wildcard pagination fix that prevents documents from being omitted from the Knowledge view.
Docstring Coverage ✅ Passed Docstring coverage is 93.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/knowledge-missing-files

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added bug 🔴 Something isn't working. and removed bug 🔴 Something isn't working. labels Aug 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/services/search_service.py (1)

193-202: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix NameError: get_search_offset is not imported.

get_search_offset() is called at Line 202, but the from auth_context import (...) block at Lines 194-198 imports only get_score_threshold, get_search_filters, and get_search_limit. Ruff confirms this as an undefined name (F821). Every call to search_tool — every search request — raises NameError at runtime, since this line executes unconditionally before the wildcard/non-wildcard branch.

🐛 Proposed fix
         from auth_context import (
             get_score_threshold,
             get_search_filters,
+            get_search_offset,
             get_search_limit,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/search_service.py` around lines 193 - 202, Add get_search_offset
to the auth_context import block in the search_tool flow before its existing
call, preserving the current filters, limit, and offset handling.

Source: Linters/SAST tools

🧹 Nitpick comments (2)
frontend/app/api/queries/useGetSearchQuery.ts (1)

122-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated file-accumulator type into a shared alias.

The Map<string, {...}> value type is declared three times with identical shape: in mergeChunksIntoFileMap's parameter type, in the wildcard-path fileMap declaration, and in the non-wildcard-path fileMap declaration. A shared type alias removes the risk of the three declarations drifting apart when a field is added or renamed.

♻️ Proposed refactor
+type FileAccumulator = {
+  filename: string;
+  mimetype: string;
+  chunks: ChunkResult[];
+  totalScore: number;
+  source_url?: string;
+  owner?: string;
+  owner_name?: string;
+  owner_email?: string;
+  file_size?: number;
+  connector_type?: string;
+  embedding_model?: string;
+  embedding_dimensions?: number;
+  allowed_users?: string[];
+  allowed_groups?: string[];
+};
+
 function mergeChunksIntoFileMap(
   chunks: ChunkResult[],
-  fileMap: Map<
-    string,
-    {
-      filename: string;
-      mimetype: string;
-      chunks: ChunkResult[];
-      totalScore: number;
-      source_url?: string;
-      owner?: string;
-      owner_name?: string;
-      owner_email?: string;
-      file_size?: number;
-      connector_type?: string;
-      embedding_model?: string;
-      embedding_dimensions?: number;
-      allowed_users?: string[];
-      allowed_groups?: string[];
-    }
-  >,
+  fileMap: Map<string, FileAccumulator>,
   getFileIdentity: (chunk: ChunkResult) => string,
 ): void {

Then replace both fileMap declarations with new Map<string, FileAccumulator>().

Also applies to: 230-248, 325-343

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/api/queries/useGetSearchQuery.ts` around lines 122 - 142,
Extract the repeated file-map value shape into a shared FileAccumulator type
alias near mergeChunksIntoFileMap. Update that function’s fileMap parameter and
both wildcard and non-wildcard fileMap declarations to use Map<string,
FileAccumulator>, preserving all existing fields and behavior.
src/services/search_service.py (1)

498-506: 🚀 Performance & Scalability | 🔵 Trivial

Verify the memory/latency cost of a 10000-bucket terms aggregation on filename.

data_sources now requests up to size: 10000 buckets on the filename field. Terms aggregations of this size increase per-shard memory usage and response payload size, especially on a high-cardinality field like filename. Confirm this is bounded to an acceptable corpus size, or consider a lower ceiling with a documented rationale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/search_service.py` around lines 498 - 506, Review the
data_sources aggregation in the search_body construction and verify that
requesting 10,000 filename buckets is acceptable for the supported corpus and
cluster resource limits. If it is not, lower the terms size to an appropriate
bounded ceiling and document the rationale near the configuration, preserving
the other aggregations unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/api/search.py`:
- Around line 7-12: Remove the unused get_current_user import from the
dependencies import block in the search route module; retain get_search_service,
get_session_manager, and require_permission unchanged.

In `@src/auth_context.py`:
- Line 7: Update the typing imports in auth_context.py to remove the unused Dict
and Optional names, while retaining Any for the existing type annotations.

In `@src/services/search_service.py`:
- Around line 730-734: Update the auth_context import in the search() flow to
include set_search_offset alongside set_score_threshold and set_search_limit, so
the existing set_search_offset(offset) call resolves without changing search
behavior.
- Around line 527-529: Update the wildcard pagination flow in search_service.py
around the OpenSearch request using "from" and "size", together with the
pagination logic in frontend/app/api/queries/useGetSearchQuery.ts, so requests
remain valid through the configured WILDCARD_QUERY_LIMIT of 10000. Prefer
converting wildcard listing pagination to search_after; otherwise raise the
relevant index max_result_window setting to cover the full offset-plus-size
range at both affected sites.

---

Outside diff comments:
In `@src/services/search_service.py`:
- Around line 193-202: Add get_search_offset to the auth_context import block in
the search_tool flow before its existing call, preserving the current filters,
limit, and offset handling.

---

Nitpick comments:
In `@frontend/app/api/queries/useGetSearchQuery.ts`:
- Around line 122-142: Extract the repeated file-map value shape into a shared
FileAccumulator type alias near mergeChunksIntoFileMap. Update that function’s
fileMap parameter and both wildcard and non-wildcard fileMap declarations to use
Map<string, FileAccumulator>, preserving all existing fields and behavior.

In `@src/services/search_service.py`:
- Around line 498-506: Review the data_sources aggregation in the search_body
construction and verify that requesting 10,000 filename buckets is acceptable
for the supported corpus and cluster resource limits. If it is not, lower the
terms size to an appropriate bounded ceiling and document the rationale near the
configuration, preserving the other aggregations unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 58472b6a-787d-4dfe-bd8a-8ea9fcf6dc42

📥 Commits

Reviewing files that changed from the base of the PR and between 92f31cb and 02be20e.

📒 Files selected for processing (4)
  • frontend/app/api/queries/useGetSearchQuery.ts
  • src/api/search.py
  • src/auth_context.py
  • src/services/search_service.py

Comment thread src/api/search.py
Comment on lines 7 to 12
from dependencies import (
get_current_user,
get_search_service,
get_session_manager,
get_current_user,
require_permission,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the unused get_current_user import.

Ruff flags get_current_user as unused (F401). The route authenticates via require_permission("search:use") at Line 34, not get_current_user. Remove the import to keep the pipeline green.

🧹 Proposed fix
 from dependencies import (
-    get_current_user,
     get_search_service,
     get_session_manager,
     require_permission,
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from dependencies import (
get_current_user,
get_search_service,
get_session_manager,
get_current_user,
require_permission,
)
from dependencies import (
get_search_service,
get_session_manager,
require_permission,
)
🧰 Tools
🪛 GitHub Check: Ruff and mypy on changed files

[failure] 8-8: ruff (F401)
src/api/search.py:8:5: F401 dependencies.get_current_user imported but unused
help: Remove unused import: dependencies.get_current_user

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/search.py` around lines 7 - 12, Remove the unused get_current_user
import from the dependencies import block in the search route module; retain
get_search_service, get_session_manager, and require_permission unchanged.

Source: Linters/SAST tools

Comment thread src/auth_context.py

from contextvars import ContextVar
from typing import Optional, Dict, Any
from typing import Any, Dict, Optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove unused Dict and Optional imports.

Ruff flags Dict and Optional as unused (F401), and flags Dict as deprecated in favor of the built-in dict (UP035). The file already uses str | None and dict[str, Any] elsewhere, so Dict/Optional are dead weight.

🧹 Proposed fix
-from typing import Any, Dict, Optional
+from typing import Any
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from typing import Any, Dict, Optional
from typing import Any
🧰 Tools
🪛 GitHub Check: Ruff and mypy on changed files

[failure] 7-7: ruff (F401)
src/auth_context.py:7:31: F401 typing.Optional imported but unused
help: Remove unused import


[failure] 7-7: ruff (F401)
src/auth_context.py:7:25: F401 typing.Dict imported but unused
help: Remove unused import


[failure] 7-7: ruff (UP035)
src/auth_context.py:7:1: UP035 typing.Dict is deprecated, use dict instead

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/auth_context.py` at line 7, Update the typing imports in auth_context.py
to remove the unused Dict and Optional names, while retaining Any for the
existing type annotations.

Source: Linters/SAST tools

Comment on lines +527 to 529
"from": offset,
"size": limit,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate WILDCARD_QUERY_LIMIT's value and any max_result_window overrides.
rg -n "WILDCARD_QUERY_LIMIT" --type=ts -C2
rg -n "max_result_window" -C3

Repository: langflow-ai/openrag

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -u

echo "Tracked files matching search/query names:"
git ls-files | rg 'search_service\.py|queries/useGetSearchQuery\.ts|searchService|search' || true

echo
echo "Find constants definitions/usages without file-type filter:"
rg -n "WILDCARD_QUERY_LIMIT|WILDCARD_PAGE_SIZE|SEARCH_CONSTANTS|from\": offset|size\": limit|max_result_window|Result window is too large" . -C 3 || true

echo
echo "Candidate search_service.py lines around pagination:"
if [ -f src/services/search_service.py ]; then
  sed -n '500,545p' src/services/search_service.py | cat -n
  echo "--- lines 680-720 ---"
  sed -n '680,720p' src/services/search_service.py | cat -n
fi

echo
echo "Candidate frontend query lines around pagination:"
if [ -f frontend/app/api/queries.useGetSearchQuery.ts ]; then
  sed -n '230,290p' frontend/app/api/queries.useGetSearchQuery.ts | cat -n
fi
if [ -f frontend/app/api/queries/useGetSearchQuery.ts ]; then
  sed -n '230,290p' frontend/app/api/queries/useGetSearchQuery.ts | cat -n
fi

Repository: langflow-ai/openrag

Length of output: 16480


🏁 Script executed:

#!/bin/bash
set -u

echo "List files containing useGetSearchQuery:"
git ls-files | rg 'useGetSearchQuery|queries' || true

echo
echo "Search for Wildcard constant names/usages (case insensitive):"
rg -n "WILDCARD|QUERY_LIMIT|PAGE_SIZE|max_result_window|Result window|from.*offset|size.*limit" . -C 3 || true

Repository: langflow-ai/openrag

Length of output: 50376


Handle the OpenSearch result-window boundary for wildcard pagination.

frontend/lib/constants.ts caps WILDCARD_QUERY_LIMIT at 10000, but frontend/app/api/queries/useGetSearchQuery.ts paginates with WILDCARD_PAGE_SIZE = 1000 and search_service.py passes { "from": offset, "size": limit } directly to OpenSearch. A request where offset + size exceeds the index’s index.max_result_window still fails with “Result window is too large”. Raise the index setting to support the intended pagination depth, or switch the wildcard listing to search_after so it is not bounded by from/size.

📍 Affects 2 files
  • src/services/search_service.py#L527-L529 (this comment)
  • frontend/app/api/queries/useGetSearchQuery.ts#L250-L276
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/search_service.py` around lines 527 - 529, Update the wildcard
pagination flow in search_service.py around the OpenSearch request using "from"
and "size", together with the pagination logic in
frontend/app/api/queries/useGetSearchQuery.ts, so requests remain valid through
the configured WILDCARD_QUERY_LIMIT of 10000. Prefer converting wildcard listing
pagination to search_after; otherwise raise the relevant index max_result_window
setting to cover the full offset-plus-size range at both affected sites.

Comment on lines 730 to 734
from auth_context import set_score_threshold, set_search_limit

set_search_limit(limit)
set_search_offset(offset)
set_score_threshold(score_threshold)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix NameError: set_search_offset is not imported.

set_search_offset(offset) is called at Line 733, but the from auth_context import set_score_threshold, set_search_limit statement at Line 730 does not import set_search_offset. Ruff confirms this as an undefined name (F821). Every call to the public search() method — the entry point used by the API route — raises NameError at runtime.

🐛 Proposed fix
-        from auth_context import set_score_threshold, set_search_limit
+        from auth_context import set_score_threshold, set_search_limit, set_search_offset
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from auth_context import set_score_threshold, set_search_limit
set_search_limit(limit)
set_search_offset(offset)
set_score_threshold(score_threshold)
from auth_context import set_score_threshold, set_search_limit, set_search_offset
set_search_limit(limit)
set_search_offset(offset)
set_score_threshold(score_threshold)
🧰 Tools
🪛 GitHub Check: Ruff and mypy on changed files

[failure] 733-733: ruff (F821)
src/services/search_service.py:733:9: F821 Undefined name set_search_offset

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/search_service.py` around lines 730 - 734, Update the
auth_context import in the search() flow to include set_search_offset alongside
set_score_threshold and set_search_limit, so the existing
set_search_offset(offset) call resolves without changing search behavior.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) bug 🔴 Something isn't working. frontend 🟨 Issues related to the UI/UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant