gRPC API Reference
All services are defined under the laurus.v1 protobuf package.
Services Overview
| Service | RPCs | Description |
|---|---|---|
HealthService | Check | Health checking |
IndexService | CreateIndex, GetIndex, GetSchema, AddField, DeleteField | Index lifecycle and schema |
DocumentService | PutDocument, AddDocument, PutDocuments, AddDocuments, GetDocuments, DeleteDocuments, Commit, FlushWal | Document CRUD, bulk ingestion, commit, and WAL flush |
SearchService | Search, SearchStream | Unary and streaming search |
HealthService
Check
Returns the current serving status of the server.
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
Response fields:
| Field | Type | Description |
|---|---|---|
status | ServingStatus | SERVING_STATUS_SERVING when the server is ready |
IndexService
CreateIndex
Create a new index with the given schema. Fails with ALREADY_EXISTS if an index is already open.
rpc CreateIndex(CreateIndexRequest) returns (CreateIndexResponse);
Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
schema | Schema | Yes | Index schema definition |
Schema structure:
message Schema {
map<string, FieldOption> fields = 1;
repeated string default_fields = 2;
map<string, AnalyzerDefinition> analyzers = 3;
map<string, EmbedderConfig> embedders = 4;
DynamicFieldPolicy dynamic_field_policy = 5;
}
enum DynamicFieldPolicy {
DYNAMIC_FIELD_POLICY_UNSPECIFIED = 0;
DYNAMIC_FIELD_POLICY_STRICT = 1;
DYNAMIC_FIELD_POLICY_DYNAMIC = 2;
DYNAMIC_FIELD_POLICY_IGNORE = 3;
}
fields— Field definitions keyed by field name.default_fields— Field names used as default search targets when a query does not specify a field.analyzers— Custom analyzer pipelines keyed by name. Referenced byTextOption.analyzer.embedders— Embedder configurations keyed by name. Referenced by vector field options (HnswOption.embedder, etc.).dynamic_field_policy— How the engine treats fields that appear in an ingested document but are absent fromfields.UNSPECIFIEDis interpreted asDYNAMICfor forward compatibility. See Schema & Fields for the full behaviour matrix and the warning about silent truncation underDYNAMIC.
AnalyzerDefinition:
message AnalyzerDefinition {
repeated ComponentConfig char_filters = 1;
ComponentConfig tokenizer = 2;
repeated ComponentConfig token_filters = 3;
}
ComponentConfig (used for char filters, tokenizer, and token filters):
| Field | Type | Description |
|---|---|---|
type | string | Component type name (e.g. "whitespace", "lowercase", "unicode_normalization") |
params | map<string, string> | Type-specific parameters as string key-value pairs |
EmbedderConfig:
| Field | Type | Description |
|---|---|---|
type | string | Embedder type name (e.g. "precomputed", "candle_bert", "candle_colbert", "openai") |
params | map<string, string> | Type-specific parameters (e.g. "model" → "sentence-transformers/all-MiniLM-L6-v2"). "candle_colbert" (Issue #1349) also takes the optional "revision", "query_maxlen" and "doc_maxlen"; the lengths are decimal strings such as "32", and anything else is rejected |
Each FieldOption is a oneof with one of the following field types:
| Lexical Fields | Vector Fields |
|---|---|
TextOption (indexed, stored, multi_valued, position_increment_gap, term_vectors, doc_values, analyzer) | HnswOption (dimension, distance, m, ef_construction, base_weight, quantizer, embedder, rerank_storage, pq_codebook_path) |
IntegerOption (indexed, stored, multi_valued, doc_values) | FlatOption (dimension, distance, base_weight, quantizer, embedder, rerank_storage) |
FloatOption (indexed, stored, multi_valued, doc_values) | IvfOption (dimension, distance, n_clusters, n_probe, base_weight, quantizer, embedder, rerank_storage) |
BooleanOption (indexed, stored, multi_valued, doc_values) | MultiVectorOption (dimension, distance, embedder, storage) |
DateTimeOption (indexed, stored, multi_valued, doc_values) | |
GeoOption (indexed, stored, multi_valued, doc_values) | |
Geo3dOption (indexed, stored, multi_valued, doc_values) | |
BytesOption (stored, multi_valued) |
The embedder field in vector options specifies the name of an embedder defined in Schema.embedders. When set, the server automatically generates vectors from document text fields at index time. Leave empty to supply pre-computed vectors directly.
Doc values: doc_values (Issue #1047) is optional bool on every lexical option above except BytesOption, following the same tri-state contract as term_vectors: a client that omits it gets the engine’s default (true), distinguishable from an explicit false. It controls whether the field’s value is also copied into DocValues, the column-oriented store sorting and faceting/aggregation read from — a DocValues column is written only when stored and doc_values are both true. BytesOption carries no such field: a Bytes value is never written to DocValues regardless. Turning doc_values off for a field that is never sorted or faceted on shrinks its segment footprint; the field remains fully searchable and retrievable either way.
Multi-valued geo, datetime, boolean, text and bytes: multi_valued on GeoOption / Geo3dOption (Issue #1174), on DateTimeOption (Issue #1184) and on BooleanOption (Issue #1180) is proto field number 4 — 3 is already taken by doc_values there, unlike IntegerOption / FloatOption; on TextOption (Issue #1175) it is field number 6, because Text already used 3–5 for term_vectors, analyzer and doc_values; on BytesOption (Issue #1176) it is field number 2 — 1 is already taken by stored, and BytesOption has neither an indexed nor a doc_values field. When true, a geo field accepts GeoArrayValue / Geo3dArrayValue (see the Value table below) and distance / bounding-box queries (plus geo3d_nearest) match a document if any of its points satisfies the predicate, scoring it by its closest point. A datetime field accepts DatetimeArrayValue (repeated int64 Unix microseconds UTC, the same encoding as datetime_value) and range queries match a document if any of its instants is in range, with constant scoring; a document is reported once even when several instants match. A boolean field accepts BoolArrayValue (repeated bool) and a term query matches a document if any element equals the queried value; each element is indexed as its own true / false term posting — there are no BKD points for booleans — so repeated elements raise the term frequency (and hence the BM25 score), not the hit count. A text field accepts TextArrayValue (repeated string) and a term query matches a document if any element contains the term; position_increment_gap (field 7, optional uint32) is the number of positions inserted between consecutive elements, which keeps a phrase query from spanning two elements unless its slop reaches the gap. It is optional for the same reason term_vectors / doc_values are: an omitted value means the engine default (100), not 0. A bytes field accepts BytesArrayValue (repeated bytes); since a Bytes value is never lexically indexed, this has no “any match” query semantics at all — it only governs the stored/wire shape and ingestion arity — and, like the scalar bytes_value, the per-element MIME type carried by DataValue::BytesArray is not represented on the wire.
Multi-vector fields: MultiVectorOption (FieldOption field 12, Issue #1177) declares a field that holds each document’s token vectors (for example ColBERT-style per-token embeddings) for late-interaction rescoring. It has no ANN index and is not a vector-search target: a query_vectors entry naming it is rejected. distance must be COSINE (vectors are L2-normalized when written) or DOT_PRODUCT. Values are VectorArrayValues (see the Value table below), and the field is not stored, so documents returned by the server never include it. embedder (field 3, Issue #1349) names a token-level embedder ("candle_colbert") from Schema.embedders; when set, a document may give the field a text_value, which is embedded into token vectors. A MultiVector field accepts only a "candle_colbert" or "precomputed" embedder, and other vector fields do not accept "candle_colbert".
Multi-vector storage: the optional storage field (field 4, enum MultiVectorStorage, Issue #1346) picks the on-disk element kind of every token vector: UNSPECIFIED = same as F32, F32 (default, 4 bytes/element, exact), F16 (2 bytes/element, ~2⁻¹¹ relative error per element), or INT8 (~1 byte/element plus a small per-vector scale overhead — dimension + 2 bytes/row total — using a per-vector scale max(abs(vector)) / 127, not trained per-segment or over the corpus). At 300 tokens × 128 dimensions (~150 KB/document in F32), F16 is ~75 KB/document and INT8 is ~38 KB/document. Unset keeps F32.
Distance metrics: COSINE, EUCLIDEAN, MANHATTAN, DOT_PRODUCT, ANGULAR
Quantization methods: SCALAR_8BIT (default), PRODUCT_QUANTIZATION (Issue #481 Stage 3; supported by the HNSW index — Flat / IVF reject it at write time).
NONE (no quantization) was removed in Issue #481 Stage 1. The proto enum value 0 (QUANTIZATION_METHOD_NONE) is kept as a wire-compat reservation; if the server receives it, it falls back to SCALAR_8BIT via Default::default().
Rerank storage: the optional rerank_storage field (enum RerankStorageKind: UNSPECIFIED = no sidecar, F32) enables the Stage-2 rerank sidecar (Issue #481 / #793). When set to F32 on an HNSW field, commit writes an extra full-precision .hnsw.f32 sidecar so searches that set rerank_factor rescore int8 candidates against the original vectors. Omitting the field (or UNSPECIFIED) keeps Stage-1 int8-only ranking. Since #932 the sidecar is emitted and consumed by all three vector index types (HNSW / Flat / IVF); on Flat/IVF the rescoring applies to field-routed queries.
Shared PQ codebook: the optional pq_codebook_path field on HnswOption (Issue #631) names a storage-relative shared PQ codebook file, trained once via the laurus train pq-codebook CLI command. Segments are then encoded against the pre-trained codebook instead of re-training k-means on every commit and merge. Only meaningful with a PRODUCT_QUANTIZATION quantizer; when set but not yet trained, commits fail with an error naming the training command (no silent fallback to per-segment training). Unset keeps per-segment training.
Base weight: base_weight on HnswOption/FlatOption/IvfOption is optional float (Issue #1084): a client that omits it gets the engine’s default (1.0), distinguishable from an explicit 0.0. It sets the field’s relative scoring priority when a query targets it alongside other vector fields — see Vector Search → Weights for what it does and does not affect.
QuantizationConfig structure:
| Field | Type | Description |
|---|---|---|
method | QuantizationMethod | Quantization method (QUANTIZATION_METHOD_SCALAR_8BIT or QUANTIZATION_METHOD_PRODUCT_QUANTIZATION). The reserved value 0 (NONE) is silently coerced to SCALAR_8BIT. |
subvector_count | uint32 | Number of subvectors (only used when method is PRODUCT_QUANTIZATION; must evenly divide dimension) |
Example:
{
"schema": {
"fields": {
"title": {"text": {"indexed": true, "stored": true, "term_vectors": true}},
"embedding": {"hnsw": {"dimension": 384, "distance": "DISTANCE_METRIC_COSINE", "m": 16, "ef_construction": 200}}
},
"default_fields": ["title"]
}
}
GetIndex
Get index statistics.
rpc GetIndex(GetIndexRequest) returns (GetIndexResponse);
Response fields:
| Field | Type | Description |
|---|---|---|
document_count | uint64 | Total number of documents in the index |
vector_fields | map<string, VectorFieldStats> | Per-field vector statistics |
Each VectorFieldStats contains vector_count and dimension.
AddField
Add a new field to the running index at runtime.
rpc AddField(AddFieldRequest) returns (AddFieldResponse);
Request fields:
| Field | Type | Description |
|---|---|---|
name | string | The field name |
field_option | FieldOption | The field configuration |
Response fields:
| Field | Type | Description |
|---|---|---|
schema | Schema | The updated schema after the field has been added |
HTTP gateway: POST /v1/schema/fields
DeleteField
Remove a field from the running index schema.
rpc DeleteField(DeleteFieldRequest) returns (DeleteFieldResponse);
Request fields:
| Field | Type | Description |
|---|---|---|
name | string | The field name to remove |
Response fields:
| Field | Type | Description |
|---|---|---|
schema | Schema | The updated schema after removal |
Existing indexed data for the field remains in storage but becomes inaccessible. Per-field analyzers and embedders are unregistered.
HTTP gateway: DELETE /v1/schema/fields/{name}
GetSchema
Retrieve the current index schema.
rpc GetSchema(GetSchemaRequest) returns (GetSchemaResponse);
Response fields:
| Field | Type | Description |
|---|---|---|
schema | Schema | The index schema |
DocumentService
PutDocument
Insert or replace a document by ID. If a document with the same ID already exists, it is replaced.
rpc PutDocument(PutDocumentRequest) returns (PutDocumentResponse);
Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | External document ID |
document | Document | Yes | Document content |
Document structure:
message Document {
map<string, Value> fields = 1;
}
Each Value is a oneof with these types:
| Type | Proto Field | Description |
|---|---|---|
| Null | null_value | Null value |
| Boolean | bool_value | Boolean value |
| Integer | int64_value | 64-bit signed integer |
| Float | float64_value | 64-bit floating point |
| Text | text_value | UTF-8 string |
| Bytes | bytes_value | Raw bytes |
| Vector | vector_value | VectorValue (list of floats) |
| DateTime | datetime_value | Unix microseconds (UTC) |
| Geo | geo_value | GeoPoint (latitude, longitude) |
| Int64Array | int64_array_value | Int64ArrayValue (multi-valued integers; requires IntegerOption.multi_valued = true) |
| Float64Array | float64_array_value | Float64ArrayValue (multi-valued floats; requires FloatOption.multi_valued = true) |
| Geo3d | geo3d_value | Geo3dPoint (x, y, z meters; ECEF Cartesian) |
| GeoArray | geo_array_value | GeoArrayValue (repeated GeoPoint; multi-valued 2D points; requires GeoOption.multi_valued = true) |
| Geo3dArray | geo3d_array_value | Geo3dArrayValue (repeated Geo3dPoint; multi-valued 3D points; requires Geo3dOption.multi_valued = true) |
| DateTimeArray | datetime_array_value | DatetimeArrayValue (repeated int64 Unix microseconds; multi-valued instants; requires DateTimeOption.multi_valued = true) |
| BoolArray | bool_array_value | BoolArrayValue (repeated bool; multi-valued booleans; requires BooleanOption.multi_valued = true) |
| TextArray | text_array_value | TextArrayValue (repeated string; multi-valued text; requires TextOption.multi_valued = true) |
| BytesArray | bytes_array_value | BytesArrayValue (repeated bytes; multi-valued bytes, MIME not carried on the wire; requires BytesOption.multi_valued = true) |
| VectorArray | vector_array_value | VectorArrayValue (uint32 dimension, repeated float values; token vectors packed row-major, values.len() / dimension vectors; requires a MultiVectorOption field) |
Geo3dPoint:
| Field | Type | Description |
|---|---|---|
x | double | X coordinate in meters (ECEF: equatorial plane, +X toward 0° longitude) |
y | double | Y coordinate in meters (ECEF: equatorial plane, +Y toward 90°E) |
z | double | Z coordinate in meters (ECEF: +Z toward the North Pole) |
See 3D Geographic Search (ECEF) for the full coordinate system description and the wgs84_to_ecef / ecef_to_wgs84 conversion utilities.
AddDocument
Add a document. Unlike PutDocument, this does not replace existing documents with the same ID — multiple documents can share an ID (chunking pattern).
rpc AddDocument(AddDocumentRequest) returns (AddDocumentResponse);
Request fields are the same as PutDocument.
PutDocuments
Batched upsert. Entries are applied sequentially, in input order, with one WAL fsync for the whole batch — much faster than one PutDocument call per document. Duplicate IDs within one batch dedup exactly like the same puts issued one by one (the last occurrence wins).
rpc PutDocuments(PutDocumentsRequest) returns (PutDocumentsResponse);
message DocumentEntry {
string id = 1;
Document document = 2;
}
message PutDocumentsRequest {
repeated DocumentEntry documents = 1;
}
message PutDocumentsResponse {
uint32 applied = 1; // equals the request size on success
}
The call fails fast at the first entry that cannot be applied. Already-applied entries are not rolled back — they are durable at the next commit — and the error status message carries the failing position, its ID, and the applied count, so retrying the batch (or its suffix) is idempotent. A batch that fails on a caller mistake (e.g. a schema violation) returns INVALID_ARGUMENT; storage failures return INTERNAL.
AddDocuments
Batched chunk append. Like PutDocuments but never deletes existing documents, so a batch may legitimately repeat an ID to add multiple chunks of the same logical document.
rpc AddDocuments(AddDocumentsRequest) returns (AddDocumentsResponse);
Request/response fields mirror PutDocuments.
GetDocuments
Retrieve all documents matching the given external ID.
rpc GetDocuments(GetDocumentsRequest) returns (GetDocumentsResponse);
Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | External document ID |
Response fields:
| Field | Type | Description |
|---|---|---|
documents | repeated Document | Matching documents |
DeleteDocuments
Delete all documents matching the given external ID.
rpc DeleteDocuments(DeleteDocumentsRequest) returns (DeleteDocumentsResponse);
Commit
Commit pending changes (additions and deletions) to the index. Changes are not visible to search until committed.
rpc Commit(CommitRequest) returns (CommitResponse);
FlushWal
Force buffered WAL records durable without a full commit. Both messages are empty. This is a near no-op under the default per-record sync policy (every write is already fsync’d); under the group-commit policy it flushes the current partial batch on demand, bounding the crash-loss window. Unlike Commit, it does not materialize segments, so buffered changes remain invisible to search until a subsequent Commit.
rpc FlushWal(FlushWalRequest) returns (FlushWalResponse);
message FlushWalRequest {}
message FlushWalResponse {}
The WAL durability policy is configured server-side via the [index.wal] config section. See Configuration → [index.wal] Section and Persistence & WAL → WAL Durability Policy.
HTTP gateway: POST /v1/flush_wal
SearchService
Search
Execute a search query and return results as a single response.
rpc Search(SearchRequest) returns (SearchResponse);
Response fields:
| Field | Type | Description |
|---|---|---|
results | repeated SearchResult | Search results ordered by relevance |
total_hits | uint64 | Total number of matching documents (before limit/offset) |
SearchStream
Execute a search query and stream results back one at a time.
rpc SearchStream(SearchRequest) returns (stream SearchResult);
SearchRequest Fields
| Field | Type | Required | Description |
|---|---|---|---|
query | string | No | Lexical search query in Query DSL |
query_vectors | repeated QueryVector | No | Vector search queries |
limit | uint32 | No | Maximum number of results (default: engine default) |
offset | uint32 | No | Number of results to skip |
fusion | FusionAlgorithm | No | Fusion algorithm for hybrid search |
lexical_params | LexicalParams | No | Lexical search parameters |
vector_params | VectorParams | No | Vector search parameters |
field_boosts | map<string, float> | No | Per-field score boosting |
highlight | HighlightParams | No | Request highlighted fragments per field (Issue #1134) |
rescore | RescoreParams | No | Rescore the top first-stage results with late interaction (Issue #1351) |
At least one of query or query_vectors must be provided.
3D Geographic Queries
3D ECEF geographic queries are expressed in the lexical DSL string passed via SearchRequest.query. There is no dedicated message type — the same DSL forms used by the core library work over gRPC. Three forms are available (see Query DSL → 3D Geographic Queries for full syntax):
position:geo3d_distance(x, y, z, distance_m)— sphere centered at(x, y, z)with maximum distance in metersposition:geo3d_bbox(min_x, min_y, min_z, max_x, max_y, max_z)— 3D axis-aligned bounding boxposition:geo3d_nearest(x, y, z, k)— k nearest neighbours to(x, y, z)
position is the field name; substitute the actual Geo3d-typed field declared in your schema. All numeric arguments are signed double values; k is an unsigned integer.
QueryVector
| Field | Type | Description |
|---|---|---|
vector | repeated float | Query vector |
weight | float | Weight for this vector (default: 1.0) |
fields | repeated string | Target vector fields (empty = all) |
FusionAlgorithm
A oneof with two options:
- RRF (Reciprocal Rank Fusion):
kparameter (default: 60) - WeightedSum:
lexical_weightandvector_weight
LexicalParams
| Field | Type | Description |
|---|---|---|
min_score | float | Minimum score threshold |
timeout_ms | uint64 | Search timeout in milliseconds |
parallel | bool | Enable parallel search |
sort_by | SortSpec | Sort by a field instead of score |
SortSpec
| Field | Type | Description |
|---|---|---|
field | string | Field name to sort by. Empty string means sort by relevance score |
order | SortOrder | SORT_ORDER_ASC (ascending) or SORT_ORDER_DESC (descending) |
VectorParams
| Field | Type | Description |
|---|---|---|
fields | repeated string | Target vector fields |
score_mode | VectorScoreMode | WEIGHTED_SUM, MAX_SIM, or LATE_INTERACTION |
overfetch | float | Overfetch factor (default: 2.0) |
min_score | float | Minimum score threshold |
rerank_factor | optional uint32 | Stage 2 rerank widening factor (Issue #481). When set on a field whose schema enabled rerank_storage, the server widens the int8/PQ candidate fetch to top_k * rerank_factor and rescores the candidates against the original full-precision vectors before returning the top top_k. Honored on all three vector index types since #932 (HNSW, Flat, IVF — on Flat/IVF this applies to field-routed queries); fields without rerank_storage = "F32" silently fall back to int8 ranking — there is no f32 information to recover. A value of 0 or omitting the field disables rerank. |
ef_search | optional uint32 | Per-query override for the HNSW ef_search candidate-list size (Issue #644). Also gates the PQ → SQ → f32 three-stage rerank chain (Issue #673): on a PQ field with rerank_storage enabled, setting ef_search wider than top_k * rerank_factor activates an extra int8 stage that rescores the graph’s full candidate set — derived from the same rerank_storage sidecar, no extra configuration needed — before the exact stage’s narrower budget is carved out of it. Ignored on non-HNSW fields. |
HighlightParams
Requests highlighted fragments for specific fields (Issue #1134). Highlighting
uses SearchRequest.query (or the lexical clause of a DSL query), the same
query that drives lexical scoring — filter_query never contributes
highlighted terms, and a vector-only request (no lexical query) produces no
highlights. Only stored: true text fields can be highlighted; other names
are silently skipped. See Highlighting for full
semantics.
| Field | Type | Description |
|---|---|---|
fields | repeated string | Stored text fields to highlight. Required — a request with this unset or empty is rejected with INVALID_ARGUMENT |
max_fragments | optional uint32 | Maximum fragments per field (default: 5). 0 is rejected |
fragment_size | optional uint32 | Target fragment length in characters (default: 150). 0 is rejected |
tag | optional string | HTML tag to wrap matches (default: "mark"). An empty string is treated as unset |
css_class | optional string | CSS class added to the tag. An empty string is treated as unset |
require_field_match | optional bool | Only use query terms that target the highlighted field (default: true) |
max_analyzed_chars | optional uint64 | Maximum characters of a field’s text to analyze (default: 1,000,000) |
return_entire_field_if_no_highlight | optional bool | Return the full field value as one fragment when nothing matched (default: false) |
Highlights
| Field | Type | Description |
|---|---|---|
fragments | repeated string | Highlighted fragments for one field, best fragment first |
RescoreParams
Reorders the top window_size first-stage results (lexical, vector or
hybrid) with ColBERT-style late interaction over a MultiVector field
(Issue #1351). See
Late-Interaction Rescore
for the scoring and ordering rules.
| Field | Type | Description |
|---|---|---|
window_size | optional uint32 | How many top first-stage results to rescore, 1 to 10,000. Unset means 100 |
late_interaction | LateInteractionRescore | The rescorer (a oneof; required) |
LateInteractionRescore:
| Field | Type | Description |
|---|---|---|
field | string | A MultiVector field |
vectors | VectorArrayValue | The query’s token vectors, packed row-major like a document value (1 to 1,024 vectors of the field’s dimension). One of vectors and text is required |
text | string | Query text, embedded by the field’s token-level embedder (a candle_colbert one) |
The engine validates the values: an out-of-range window, a field that is not
a MultiVector field, the wrong number or dimension of vectors, text for a
field without a token-level embedder, or a rescore combined with
lexical_params.sort_by is rejected with INVALID_ARGUMENT, as is a
RescoreParams without late_interaction or a query.
SearchResult
| Field | Type | Description |
|---|---|---|
id | string | External document ID |
score | float | Relevance score; for a rescored result, its late-interaction (MaxSim) score |
document | Document | Document content |
highlights | map<string, Highlights> | Highlighted fragments per field named in SearchRequest.highlight.fields. A field with no highlight is absent from this map |
Example
{
"query": "body:rust",
"query_vectors": [
{"vector": [0.1, 0.2, 0.3], "weight": 1.0}
],
"limit": 10,
"fusion": {
"rrf": {"k": 60}
},
"field_boosts": {
"title": 2.0
}
}
Example: Highlighting
{
"query": "body:rust",
"limit": 10,
"highlight": {"fields": ["body"], "max_fragments": 2, "tag": "em"}
}
A matching hit’s SearchResult then carries:
{
"id": "doc1",
"score": 1.2,
"document": {"fields": {"body": {"text_value": "Rust is great"}}},
"highlights": {"body": {"fragments": ["<em>Rust</em> is great"]}}
}
Example: Rescore
A hybrid first stage whose top 100 results are rescored by a ColBERT
embedder on body_colbert:
{
"query": "body:lifetimes body_vec:\"how do lifetimes work\"",
"limit": 10,
"rescore": {
"window_size": 100,
"late_interaction": {"field": "body_colbert", "text": "how do lifetimes work"}
}
}
With pre-computed query token vectors instead of text:
{
"late_interaction": {
"field": "body_colbert",
"vectors": {"dimension": 2, "values": [1.0, 0.0, 0.0, 1.0]}
}
}
Error Handling
gRPC errors are returned as standard Status codes:
| Laurus Error | gRPC Status | When |
|---|---|---|
| Schema / Query / Field / Invalid argument / JSON | INVALID_ARGUMENT | Malformed request or schema, unknown field in a query |
| No index open | FAILED_PRECONDITION | RPC called before CreateIndex |
| Index already exists | ALREADY_EXISTS | CreateIndex called twice |
| Not implemented | UNIMPLEMENTED | Feature not yet supported |
| Internal errors | INTERNAL | I/O, storage, or unexpected errors |