localvectordb.cursor module

QueryCursor for streaming query results with lazy SQLite hydration.

A QueryCursor holds cached FAISS/FTS search results (lightweight ID+score pairs) and lazily loads content and metadata from SQLite in batches as the consumer iterates. This avoids the cost of re-querying for paginated access.

class localvectordb.cursor.CursorCandidate(score: float, source: Literal['vector', 'keyword', 'hybrid'], faiss_id: int | None = None, chunk_rowid: int | None = None, document_id: str | None = None, raw_rank: float | None = None)

Bases: object

A raw candidate from FAISS/FTS before SQLite hydration.

score: float
source: Literal['vector', 'keyword', 'hybrid']
faiss_id: int | None = None
chunk_rowid: int | None = None
document_id: str | None = None
raw_rank: float | None = None
__init__(score: float, source: Literal['vector', 'keyword', 'hybrid'], faiss_id: int | None = None, chunk_rowid: int | None = None, document_id: str | None = None, raw_rank: float | None = None) None
class localvectordb.cursor.CursorConfig(search_type: Literal['vector', 'keyword', 'hybrid'], return_type: Literal['documents', 'chunks', 'sections', 'context', 'enriched'], search_level: Literal['chunks', 'sections', 'documents', 'fused'], score_threshold: float, filters: Dict[str, Any] | None, vector_weight: float, context_window: int, semantic_dedup_threshold: float | None, document_scoring_method: Literal['best', 'average', 'frequency_boost'], document_scoring_options: dict | None, total_k: int, context_unit: str = 'chunks', context_truncate: bool = False)

Bases: object

Configuration captured at cursor creation time.

search_type: Literal['vector', 'keyword', 'hybrid']
return_type: Literal['documents', 'chunks', 'sections', 'context', 'enriched']
search_level: Literal['chunks', 'sections', 'documents', 'fused']
score_threshold: float
filters: Dict[str, Any] | None
vector_weight: float
context_window: int
semantic_dedup_threshold: float | None
document_scoring_method: Literal['best', 'average', 'frequency_boost']
document_scoring_options: dict | None
total_k: int
context_unit: str = 'chunks'
context_truncate: bool = False
__init__(search_type: Literal['vector', 'keyword', 'hybrid'], return_type: Literal['documents', 'chunks', 'sections', 'context', 'enriched'], search_level: Literal['chunks', 'sections', 'documents', 'fused'], score_threshold: float, filters: Dict[str, Any] | None, vector_weight: float, context_window: int, semantic_dedup_threshold: float | None, document_scoring_method: Literal['best', 'average', 'frequency_boost'], document_scoring_options: dict | None, total_k: int, context_unit: str = 'chunks', context_truncate: bool = False) None
class localvectordb.cursor.DocumentCandidate(document_id: str, score: float, chunk_scores: List[float] = <factory>)

Bases: object

Pre-aggregated document-level candidate for document return type.

document_id: str
score: float
chunk_scores: List[float]
__init__(document_id: str, score: float, chunk_scores: List[float] = <factory>) None
class localvectordb.cursor.QueryCursor(db: SearchMixin, candidates: List[CursorCandidate], config: CursorConfig, *, ttl_seconds: float = 300.0, default_batch_size: int = 50)

Bases: object

Holds cached search results with lazy SQLite metadata/content loading.

Lifecycle: 1. Created by SearchMixin.query_cursor() which runs FAISS/FTS search 2. Consumer calls fetch_batch() or iterates via stream() / __aiter__() 3. Each batch triggers a SQLite query for only that batch’s metadata 4. Cursor tracks position and expires after configurable TTL 5. Must be closed explicitly or via context manager

Parameters:
  • db (LocalVectorDBBase) – Reference to the database for lazy SQLite loading.

  • candidates (list of CursorCandidate) – Scored candidates from FAISS/FTS, sorted by score descending.

  • config (CursorConfig) – Query configuration captured at cursor creation time.

  • ttl_seconds (float) – Time-to-live in seconds (default 300 = 5 minutes).

  • default_batch_size (int) – Default number of results per batch (default 50).

__init__(db: SearchMixin, candidates: List[CursorCandidate], config: CursorConfig, *, ttl_seconds: float = 300.0, default_batch_size: int = 50)
close() None

Close the cursor and release the database reference.

property closed: bool
property total_candidates: int

Total number of scored candidates (or documents for document return type).

property remaining: int

Number of unfetched candidates.

property is_exhausted: bool
fetch_batch(batch_size: int | None = None) List[QueryResult]

Fetch the next batch of results, hydrating from SQLite lazily.

Parameters:

batch_size (int, optional) – Number of results to fetch. Defaults to default_batch_size.

Returns:

The next batch. Empty list when exhausted.

Return type:

list of QueryResult

Raises:

CursorExpiredError – If the cursor has been closed or its TTL has elapsed.

fetch_all() List[QueryResult]

Fetch all remaining results at once.

stream(batch_size: int | None = None) Iterator[List[QueryResult]]

Yield batches of results as a sync generator.

stream_individual(batch_size: int | None = None) Iterator[QueryResult]

Yield individual QueryResult objects.

async fetch_batch_async(batch_size: int | None = None) List[QueryResult]

Async version of fetch_batch. Hydrates from SQLite using the async pool.

Parameters:

batch_size (int, optional) – Number of results to fetch. Defaults to default_batch_size.

Return type:

list of QueryResult

async fetch_all_async() List[QueryResult]

Fetch all remaining results asynchronously.

async stream_async(batch_size: int | None = None) AsyncIterator[List[QueryResult]]

Yield batches of results as an async generator.

async stream_individual_async(batch_size: int | None = None) AsyncIterator[QueryResult]

Yield individual QueryResult objects asynchronously.