Search and Query Iterators
Relevant source files
The following files were used as context for generating this wiki page:
Search and Query Iterators provide a powerful mechanism for handling large-scale data retrieval in Milvus by abstracting paginated interactions into a streaming interface. These iterators enable developers to traverse result sets that exceed memory limits or standard gRPC message sizes by fetching data in configurable batches. This system is essential for operations requiring complete dataset traversal or high-volume similarity searches where results must be processed incrementally.
The implementation relies on two primary structs: QueryIterator for scalar filtering and SearchIterator for vector similarity searches. Both utilize a session-based approach, tracking state through tokens and timestamps to ensure consistency across multiple batch requests. Sources: src/iterator.rs:253-279, src/iterator.rs:818-847
Architecture and Core Components
The iterator system is built upon a request-response cycle managed by the Client. When an iterator is initialized, it sets up a session-specific environment, including primary key (PK) detection and consistency timestamp (MVCC) synchronization.
System Components
| Component | Description |
|---|---|
IteratorCache | A singleton cache used to store overflow results from large batch fetches to optimize subsequent next() calls. |
QueryIteratorOptions | Configuration for scalar iterators, including batch_size, limit, and offset. |
SearchIteratorOptions | Configuration for vector iterators, adding anns_field and search-specific parameters. |
QueryCursor | A Protobuf-defined structure tracking session_ts and cursor_pk for pagination state. |
Sources: src/iterator.rs:26-55, src/iterator.rs:64-100, src/proto/milvus.proto.milvus.rs:696-709
Iterator Lifecycle
The following diagram illustrates the initialization and execution flow of a QueryIterator:
The iterator ensures technical accuracy by verifying batch sizes against MAX_BATCH_SIZE (16,384) and managing gRPC QueryRequest or SearchRequest parameters like search_iter_v2. Sources: src/iterator.rs:18-20, src/iterator.rs:430-466
Query Iterator Implementation
The QueryIterator performs scalar filtering using boolean expressions. It maintains state by tracking the last retrieved primary key value to construct the next filtering expression (e.g., (original_filter) and pk > last_pk). Sources: src/iterator.rs:502-530
State Management Logic
- Checkpointing: Supports saving and loading state to/from a file (
iterator_cp_file). This allows resuming long-running queries if the process is interrupted. Sources: src/iterator.rs:400-428 - MVCC Consistency: Uses
session_tsto ensure that all batches see a consistent snapshot of the data, even as the collection is actively modified. Sources: src/iterator.rs:370-398
Sources: src/iterator.rs:555-680
Search Iterator Implementation
The SearchIterator extends pagination to vector similarity searches. Unlike the Query Iterator, it manages specialized search parameters such as search_iter_id (token) and search_iter_last_bound. Sources: src/iterator.rs:904-935
Search Configuration Options
| Parameter | Type | Default | Description |
|---|---|---|---|
batch_size | Option<usize> | 1000 | Number of entities to retrieve per gRPC call. |
anns_field | Option<String> | None | The vector field to perform similarity search on. |
search_params | HashMap | Empty | Metric types (L2, IP) and index-specific params (nprobe). |
reduce_stop_for_best | Option<bool> | true | Optimization flag for merging search results. |
Sources: src/iterator.rs:163-200
Batch Execution
The SearchIterator::next() method builds a SearchRequest that includes NQ (number of queries), placeholder_group (the vector data), and internal iterator tokens. It processes SearchResult structures, which include distance scores and entity field data. Sources: src/iterator.rs:937-1010, src/query.rs:498-540
Internal Result Caching
To optimize performance, the SDK implements an IteratorCache. If a server response contains more data than the requested batch_size, the overflow is stored in a singleton cache. Subsequent next() calls will drain this cache before initiating new network requests. Sources: src/iterator.rs:34-53, src/iterator.rs:545-553
Sources: src/iterator.rs:26-62
Summary of Iterator Usage
Iterators are instantiated through the Client using query_iterator or search_iterator methods. They are designed to be used in a loop until they return None or are explicitly closed. Sources: examples/iterator.rs:34-60, src/iterator.rs:215-249
Code Example: Query Iteration
let query_options = QueryIteratorOptions::new()
.batch_size(50)
.filter(format!("age >= 18"))
.output_fields(vec!["id".to_string(), "name".to_string()]);
let mut query_iterator = client.query_iterator(COLLECTION_NAME, query_options).await?;
while let Some(batch) = query_iterator.next().await? {
// Process batch of FieldColumn
if batch.is_empty() { break; }
}
query_iterator.close();
Sources: examples/iterator.rs:77-110
Search and query iterators significantly improve the SDK's ability to handle high-volume data operations by providing a stable, memory-efficient streaming interface that leverages Milvus's internal pagination and consistency mechanisms.