feat(edge): add query_batch for batched planned queries (#10100)

* feat(edge): add query_batch for batched planned queries

Expose the planned-query batch path as a public API so multiple
independent queries can share one planning pass over leaf searches
and scrolls. Wired through EdgeShardRead, FFI, and Python bindings.

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(edge): push batched query vectors down to segments

`query_batch` planned the whole batch at once but then executed every leaf
search on its own: one query context, one fan-out over all segments, and one
single-vector `Segment::search_batch` call per leaf.

Execute the batch as a batch instead:

- `EdgeReadView::search_batch` builds the query context once, visits the
  segments once, and hands each segment the leaves that agree on everything
  but their query vector as a single multi-vector `search_batch` call.
  `search` is now a thin wrapper over a one-element batch.
- Move `SearchType`/`BatchSearchParams` from `collection`'s segments searcher
  into `shard`, next to `CoreSearchRequest`, and add `group_search_batches`
  so both the collection and the edge read path share one grouping
  implementation. Edge computes the grouping once and reuses it per segment.
- `search_matrix` now issues its per-sample nearest queries through
  `query_batch`; they share filter, limit and vector name, so the whole
  sample is scored in one batched search per segment instead of one full
  segment pass per sampled point.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
qdrant-cloud-bot
2026-08-06 11:59:40 +02:00
committed by GitHub
co-authored by Claude Opus 5 Cursor generall
parent 908cd2f10a
commit 7364cc42ef
11 changed files with 777 additions and 227 deletions
+10
View File
@@ -145,6 +145,16 @@ impl PyEdgeShard {
Ok(points)
}
/// Execute several queries as one planned batch.
///
/// Cheaper than one `query` per request: the batch shares a single pass over the segments.
/// Returns one result list per request, in the same order as `queries`.
pub fn query_batch(&self, queries: Vec<PyQueryRequest>) -> Result<Vec<Vec<PyScoredPoint>>> {
let requests = queries.into_iter().map(Into::into).collect();
let batches = self.get_shard()?.query_batch(requests)?;
Ok(batches.into_iter().map(PyScoredPoint::wrap_vec).collect())
}
pub fn search(&self, search: PySearchRequest) -> Result<Vec<PyScoredPoint>> {
let points = self.get_shard()?.search(search.into())?;
let points = PyScoredPoint::wrap_vec(points);