Skip to content

(RFC) Refactor search interface with unified SearchDispatch trait#773

Open
narendatha wants to merge 15 commits intomainfrom
users/narendatha/search_refactor
Open

(RFC) Refactor search interface with unified SearchDispatch trait#773
narendatha wants to merge 15 commits intomainfrom
users/narendatha/search_refactor

Conversation

@narendatha
Copy link

@narendatha narendatha commented Feb 12, 2026

Refactor search interface with unified SearchDispatch trait

Summary

This PR refactors the search interface in diskann to provide a unified entry point via the SearchDispatch trait, improving code organization and API clarity. This also removes (un)popular SearchParams and search specific structs. This PR also removes (un)popular SearchParamsand replaces with search specific structs, so it is clear what params are used and which ones are ignored.

Changes

New Architecture

  • SearchDispatch trait - A unified dispatch interface that all search types implement, enabling a single index.search() entry point
  • Separate search type files:
    • dispatch.rs - Core trait definition
    • graph_search.rs - Standard k-NN graph search (GraphSearch, RecordedGraphSearch)
    • range_search.rs - Range-based search (RangeSearch, RangeSearchOutput)
    • multihop_search.rs - Label-filtered search (MultihopSearch)
    • diverse_search.rs - Diversity-aware search (DiverseSearch, feature-gated)

API Changes

Before After
index.execute_search(..., parameters, ...) index.search(..., search_params, ...)
index.search_recorded(...) index.debug_search(...)
Multiple search methods Single search() with dispatch

Code Organization

  • Moved internal implementations (range_search_internal, multihop_search_internal) to their respective search type files
  • Moved NotInMutWithLabelCheck helper to multihop_search.rs
  • Added From impls for SearchParams -> GraphSearch and RangeSearchParams -> RangeSearch
  • Removed convenience methods (graph_search, multihop_search, range_search) from DiskANNIndex to avoid encouraging direct use
  • Added local test helper functions where needed for backwards compatibility

Other

  • Renamed search_recorded -> debug_search to clearly signal debug-only intent
  • Bumped version to 0.46.0

Usage Example

use diskann::graph::{GraphSearch, RangeSearch, MultihopSearch};

// Standard k-NN search
let params = GraphSearch::new(10, 100, None)?;
let stats = index.search(&strategy, &context, &query, &params, &mut output).await?;

// Range search
let params = RangeSearch::new(100, 0.5)?;
let result = index.search(&strategy, &context, &query, &params, &mut ()).await?;

// Filtered search
let params = MultihopSearch::new(GraphSearch::new(10, 100, None)?, &filter);
let stats = index.search(&strategy, &context, &query, &params, &mut output).await?;

Why flat_search is not included

flat_search remains a separate method because it has unique constraints incompatible with SearchDispatch: (1) it requires an additional generic bound SearchAccessor<'a>: IdIterator<I> that forces the strategy's accessor to support iteration over all IDs - a capability not all strategies provide, and (2) it takes an extra vector_filter closure parameter that other search types don't need. Since flat_search is fundamentally a linear scan with filtering rather than a graph traversal, keeping it as a distinct method with its own signature is cleaner than complicating the trait to accommodate it.

Testing

  • All existing tests pass
  • cargo build --workspace
  • cargo test -p diskann -p diskann-providers -p diskann-disk

- Add SearchDispatch trait for unified search entry point
- Split search types into separate files (graph_search, range_search, multihop_search, diverse_search)
- Rename execute_search to search, parameters to search_params
- Rename search_recorded to debug_search to signal debug-only intent
- Move internal implementations to respective search type files
- Add From impls for SearchParams/RangeSearchParams to new types
- Add test helper functions in test modules for backwards compat
- Bump version to 0.46.0
@narendatha narendatha requested review from a team and Copilot February 12, 2026 16:55
@narendatha narendatha changed the title Refactor search interface with unified SearchDispatch trait (RFC) Refactor search interface with unified SearchDispatch trait Feb 12, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the search interface in the diskann crate to provide a unified entry point via the SearchDispatch trait. The refactoring improves code organization by moving search implementations to dedicated files and consolidates the API around a single index.search() method that dispatches to type-specific implementations.

Changes:

  • Introduces SearchDispatch trait as the unified search interface, with separate implementations for standard graph search, range search, multihop (label-filtered) search, and diversity-aware search
  • Replaces multiple search methods (execute_search, multihop_search, range_search) with a single search() method that delegates to SearchDispatch::dispatch()
  • Renames search_recorded to debug_search to clearly signal debug-only intent
  • Maintains backward compatibility by implementing SearchDispatch for the legacy SearchParams type

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
diskann/src/graph/search/dispatch.rs Core SearchDispatch trait definition
diskann/src/graph/search/graph_search.rs Standard k-NN graph search implementation (GraphSearch, RecordedGraphSearch)
diskann/src/graph/search/range_search.rs Range-based search implementation and range_search_internal helper
diskann/src/graph/search/multihop_search.rs Label-filtered search and multihop_search_internal helper, includes NotInMutWithLabelCheck predicate
diskann/src/graph/search/diverse_search.rs Feature-gated diversity-aware search implementation
diskann/src/graph/search/mod.rs Module organization with public re-exports
diskann/src/graph/mod.rs Top-level re-exports of search types
diskann/src/graph/index.rs New unified search() method, renamed debug_search(), removed old search methods, made internal helpers pub(crate)
diskann/src/graph/test/cases/grid.rs Updated to use GraphSearch::new() and params.k
diskann-providers/src/index/diskann_async.rs Test helper functions for backward-compatible multihop and range search calls
diskann-providers/src/index/wrapped_async.rs Converts SearchParams to GraphSearch for unified API
diskann-disk/src/search/provider/disk_provider.rs Updated to use GraphSearch and renamed debug_search
diskann-benchmark-core/src/search/graph/*.rs Updated benchmark code to use new search dispatch API
Cargo.toml, Cargo.lock Version bump to 0.46.0 across all workspace crates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

narendatha and others added 4 commits February 12, 2026 22:39
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This enables RecordedGraphSearch to implement SearchDispatch properly,
since it holds &mut recorder which requires mutable access.

- Change dispatch signature from &self to &mut self
- Update index.search to take &mut P for search params
- Implement SearchDispatch for RecordedGraphSearch
- Remove apologetic comment about trait limitations
- Update all callers to use &mut params
- RecordedGraphSearch in graph_search.rs
- MultihopSearch in multihop_search.rs
@narendatha
Copy link
Author

narendatha commented Feb 12, 2026 via email

Copy link
Contributor

@hildebrandmw hildebrandmw left a comment

Choose a reason for hiding this comment

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

Thanks @narendatha! I think this does a good job of tying together the disparate APIs we've gathered. I'm pretty strongly in favor of getting rid of the legacy SearchParams and RangeSearchParams to avoid clutter, and updating the structs defined in this PR to not expose all the methods as public and therefore render the constructor invariant checks invalid.

- Rename SearchDispatch trait to Search and move to mod.rs
- Remove unnecessary doc comment about &mut self (can be inferred)
- Remove 'feature-gated' mention from diverse_search.rs module doc
- Fix banner styles to use ///// format instead of //===
- Make create_diverse_scratch an inherent method on DiverseSearch
- Remove flat_search mention from index.search docs
- Redirect debug_search to use RecordedGraphSearch internally
- Revert version bump (0.46.0 -> 0.45.0)

Co-authored-by: hildebrandmw <hildebrandmw@users.noreply.github.com>
@narendatha
Copy link
Author

@microsoft-github-policy-service agree company="Microsoft"

…roUsize

- Rename GraphSearch to KnnSearch for clarity (k-NN search)
- Change k_value and l_value from usize to NonZeroUsize (compile-time zero check)
- Rename RecordedGraphSearch to RecordedKnnSearch
- Move RangeSearch validation from misc.rs to range_search.rs
- Remove deprecated SearchParams, SearchParamsError, RangeSearchParams, RangeSearchParamsError
- Update all callers across diskann, diskann-providers, diskann-disk, diskann-benchmark crates
- Remove ensure_positive helper (no longer needed with NonZeroUsize)
- Fix duplicated cfg attribute in diverse_search.rs
@codecov-commenter
Copy link

codecov-commenter commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 96.14792% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.00%. Comparing base (ea4078e) to head (6ce0d08).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...iskann-benchmark/src/backend/index/search/range.rs 0.00% 6 Missing ⚠️
diskann/src/graph/search/range_search.rs 97.65% 5 Missing ⚠️
diskann-providers/src/index/wrapped_async.rs 0.00% 3 Missing ⚠️
diskann/src/graph/search/multihop_search.rs 98.03% 3 Missing ⚠️
diskann-benchmark/src/backend/index/result.rs 50.00% 2 Missing ⚠️
diskann-benchmark/src/inputs/async_.rs 0.00% 2 Missing ⚠️
diskann-tools/src/utils/search_index_utils.rs 0.00% 2 Missing ⚠️
diskann-benchmark/src/backend/index/benchmarks.rs 0.00% 1 Missing ⚠️
diskann-benchmark/src/backend/index/search/knn.rs 80.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #773      +/-   ##
==========================================
- Coverage   90.29%   89.00%   -1.29%     
==========================================
  Files         428      431       +3     
  Lines       78420    78446      +26     
==========================================
- Hits        70809    69822     -987     
- Misses       7611     8624    +1013     
Flag Coverage Δ
miri 89.00% <96.14%> (-1.29%) ⬇️
unittests 89.00% <96.14%> (-1.29%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
diskann-benchmark-core/src/search/graph/knn.rs 94.91% <100.00%> (+0.02%) ⬆️
...iskann-benchmark-core/src/search/graph/multihop.rs 98.69% <100.00%> (ø)
diskann-benchmark-core/src/search/graph/range.rs 93.70% <100.00%> (+0.18%) ⬆️
diskann-benchmark/src/backend/index/spherical.rs 100.00% <ø> (ø)
diskann-disk/src/search/provider/disk_provider.rs 91.01% <100.00%> (+0.02%) ⬆️
diskann-providers/src/index/diskann_async.rs 96.35% <100.00%> (+<0.01%) ⬆️
diskann/src/error/ann_error.rs 96.57% <ø> (-0.04%) ⬇️
diskann/src/graph/index.rs 95.91% <100.00%> (-0.55%) ⬇️
diskann/src/graph/misc.rs 80.00% <ø> (-14.60%) ⬇️
diskann/src/graph/search/knn_search.rs 100.00% <100.00%> (ø)
... and 10 more

... and 63 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@hildebrandmw hildebrandmw left a comment

Choose a reason for hiding this comment

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

Thanks @narendatha, I'm really liking how this is coming together!

- Rename dispatch() to search() in Search trait and all implementations
- Reorder DiskANNIndex::search params: search_params now first argument
- KnnSearch::new takes usize instead of NonZeroUsize (with KZero/LZero errors)
- Change beam_width from Option<usize> to Option<NonZeroUsize>
- Add #[track_caller] to From<KnnSearchError> and From<RangeSearchError>
- Make DiverseSearch fields private with accessor methods
- Remove unused nz() helper functions
- Update all callers across workspace
…iverseSearch->Diverse

Drop 'Search' suffix from main search parameter types:
- KnnSearch -> Knn
- RangeSearch -> Range
- DiverseSearch -> Diverse
- RecordedKnnSearch -> RecordedKnn

Error types and output types retain their names:
- KnnSearchError (unchanged)
- RangeSearchError, RangeSearchOutput (unchanged)
- DiverseSearchParams (unchanged)
Search parameter types must now be accessed via graph::search::
- graph::search::Knn (was graph::Knn)
- graph::search::Range (was graph::Range)
- graph::search::Diverse (was graph::Diverse)
- graph::search::RecordedKnn (was graph::RecordedKnn)
- graph::search::MultihopSearch (was graph::MultihopSearch)

The Search trait and error types remain at graph:: level:
- graph::Search
- graph::KnnSearchError
- graph::RangeSearchError
- graph::RangeSearchOutput
- Move detailed algorithm docs from impl block to struct Knn
- Add implementation list to Search trait directing users to specific types
Change Search trait and DiskANNIndex::search to consume search params by value:
- fn search(&mut self, ...) -> fn search(self, ...)
- Callers no longer need &mut references
- Removes unnecessary mut declarations at call sites
- Knn, Range, etc. are Copy so no overhead from pass-by-value

This simplifies the API and makes the intent clearer - search params
are consumed, not modified and reused.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants