Manticore Search supports the ability to add embeddings generated by Machine Learning models to each document, and then doing a nearest-neighbor search on them. This lets you build features like similarity search, recommendations, semantic search, and relevance ranking based on NLP algorithms, among others, including image, video, and sound searches.
To combine KNN vector search with full-text search for better relevance, see Hybrid search.
An embedding is a method of representing data - such as text, images, or sound - as vectors in a high-dimensional space. These vectors are crafted to ensure that the distance between them reflects the similarity of the data they represent. This process typically employs algorithms like word embeddings (e.g., Word2Vec, BERT) for text or neural networks for images. The high-dimensional nature of the vector space, with many components per vector, allows for the representation of complex and nuanced relationships between items. Their similarity is gauged by the distance between these vectors, often measured using methods like Euclidean distance or cosine similarity.
Manticore Search enables k-nearest neighbor (KNN) vector searches using the HNSW library. This functionality is part of the Manticore Columnar Library.
To run KNN searches, you must first configure your table. Float vectors and KNN search are only supported in real-time tables (not in plain tables). The table needs to have at least one float_vector attribute, which serves as a data vector. You need to specify the following properties:
-
knn_type: A mandatory setting; currently, onlyhnswis supported. -
knn_dims: A mandatory setting that specifies the dimensions of the vectors being indexed. -
hnsw_similarity: A mandatory setting that specifies the distance function used by the HNSW index. Acceptable values are:L2- Squared L2IP- Inner productCOSINE- Cosine similarity
Note: When using
COSINEsimilarity, vectors are automatically normalized upon insertion. This means the stored vector values may differ from the original input values, as they will be converted to unit vectors (vectors with a mathematical length/magnitude of 1.0) to enable efficient cosine similarity calculations. This normalization preserves the direction of the vector while standardizing its length. -
hnsw_m: An optional setting that defines the maximum number of outgoing connections in the graph. The default is 16. -
hnsw_ef_construction: An optional setting that defines a construction time/accuracy trade-off. The default is 200.
NOTE: HNSW graph construction during RT chunk saves,
OPTIMIZE TABLE/ auto-optimize chunk merges, andALTER TABLE ... ADD/DROP/REBUILDKNN rebuilds runs in parallel by default on multi-core hosts; the worker count is controlled by theknn_parallel_buildsearchd setting (set it to1to force the serial path). This affects build-time performance only. Because parallel HNSW construction may insert vectors in a different order, the resulting graph may not be bit-identical to a serial build.
- SQL
- JSON
- Config
create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' );POST /sql?mode=raw -d "create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' )"table test_vec {
type = rt
...
rt_attr_float_vector = image_vector
knn = {"attrs":[{"name":"image_vector","type":"hnsw","dims":4,"hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200}]}
}Note: For auto-embeddings in plain mode, see the example below, which shows how to use model_name and from parameters in the knn configuration.
Query OK, 0 rows affected (0.01 sec)[
{
"total": 0,
"error": "",
"warning": ""
}
]The easiest way to work with vector data is using auto embeddings. With this feature, you create a table with MODEL_NAME and FROM parameters, then simply insert your text data - Manticore automatically generates embeddings for you.
When creating a table for auto embeddings, specify:
MODEL_NAME: The embedding model to useFROM: Which fields to use for embedding generation (empty means all text/string fields)API_KEY: Required for remote models (OpenAI, Voyage, Jina). The API key is validated during table creation by making a real API request.API_URL: Optional. Custom API endpoint URL. If not specified, uses the default provider endpoint (e.g.,https://api.openai.com/v1/embeddingsfor OpenAI).API_TIMEOUT: Optional. HTTP timeout in seconds for API requests. Default is 10 seconds. Set to'0'to use the default timeout. Applies to both validation requests during table creation and embedding generation during INSERT operations.
For remote models, MODEL_NAME can be written in two forms:
- Legacy provider-prefixed form:
openai/text-embedding-ada-002,voyage/voyage-3.5-lite,jina/jina-embeddings-v4 - Explicit provider-signal form for custom endpoints:
openai:text-embedding-ada-002,openai:openai/text-embedding-ada-002,voyage:custom-model,jina:custom-model
When you use the provider:model form together with API_URL, the part before : only selects the request format. The part after : is sent to the remote endpoint unchanged. This is useful for OpenAI-compatible gateways such as OpenRouter or LiteLLM.
Supported embedding models:
| Model Type | Example | API Key Required | Notes |
|---|---|---|---|
| ONNX (recommended) | Xenova/all-MiniLM-L6-v2 |
No | Local models from any Hugging Face repo that ships an .onnx file. Runs on Manticore's fast ONNX Runtime backend. Browse the list: feature-extraction ONNX models. |
| Sentence Transformers | sentence-transformers/all-MiniLM-L6-v2 |
No | Local BERT-based models, auto-downloaded. Still supported — use ONNX above when available. |
| Qwen | Qwen/Qwen3-Embedding-0.6B |
No | Local Qwen family models |
| Llama | TinyLlama/TinyLlama-1.1B-Chat-v1.0 |
No | Local Llama family models |
| Mistral | Locutusque/TinyMistral-248M-v2 |
No | Local Mistral family models |
| Gemma | h2oai/embeddinggemma-300m |
No | Local Gemma family models |
| OpenAI | openai/text-embedding-ada-002 or openai:text-embedding-ada-002 |
Yes | API_KEY='***' |
| Voyage | voyage/voyage-3.5-lite or voyage:voyage-3.5-lite |
Yes | API_KEY='***' |
| Jina | jina/jina-embeddings-v4 or jina:jina-embeddings-v4 |
Yes | API_KEY='***' |
Local model format requirements:
- Must be saved in
safetensorsformat (single-file only) - Supported families: Qwen, Llama, Mistral, Gemma
- Tested models:
TinyLlama/TinyLlama-1.1B-Chat-v1.0,Locutusque/TinyMistral-248M-v2,Qwen/Qwen3-Embedding-0.6B,h2oai/embeddinggemma-300m - Other
safetensorsmodels may also work, but are not guaranteed
More information about setting up a float_vector attribute can be found here.
- SQL
- Config
Using a local ONNX model — recommended (no API key needed)
CREATE TABLE products (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='Xenova/all-MiniLM-L6-v2' FROM='title'
);Using sentence-transformers (no API key needed; runs on the Candle path — use ONNX above when available)
CREATE TABLE products_st (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM='title'
);Using Qwen local embeddings (no API key needed)
CREATE TABLE products_qwen (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='Qwen/Qwen3-Embedding-0.6B' FROM='title' CACHE_PATH='/opt/homebrew/var/manticore/.cache/manticore'
);Using OpenAI (requires API_KEY parameter)
CREATE TABLE products_openai (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='openai/text-embedding-ada-002' FROM='title,description' API_KEY='...'
);Using OpenAI with custom API URL and timeout (optional)
CREATE TABLE products_openai_custom (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='openai:text-embedding-ada-002' FROM='title,description'
API_KEY='***' API_URL='https://custom-api.example.com/v1/embeddings' API_TIMEOUT='30'
);Using an OpenAI-compatible gateway that expects a provider-qualified model ID
CREATE TABLE products_openrouter (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='openai:openai/text-embedding-ada-002' FROM='title,description'
API_KEY='***' API_URL='https://openrouter.ai/api/v1/embeddings' API_TIMEOUT='30'
);Using all text fields for embeddings (FROM is empty)
CREATE TABLE products_all (
title TEXT,
description TEXT,
embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
MODEL_NAME='Xenova/all-MiniLM-L6-v2' FROM=''
);table products {
type = rt
path = /path/to/products
rt_field = title
rt_field = description
rt_attr_float_vector = embedding_vector
knn = {"attrs":[{"name":"embedding_vector","type":"hnsw","hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200,"model_name":"Xenova/all-MiniLM-L6-v2","from":"title"}]}
}Using OpenAI with API key in plain mode:
table products_openai {
type = rt
path = /path/to/products_openai
rt_field = title
rt_field = description
rt_attr_float_vector = embedding_vector
knn = {"attrs":[{"name":"embedding_vector","type":"hnsw","hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200,"model_name":"openai/text-embedding-ada-002","from":"title,description","api_key":"your-api-key-here"}]}
}Using all text fields (empty FROM):
table products_all {
type = rt
path = /path/to/products_all
rt_field = title
rt_field = description
rt_attr_float_vector = embedding_vector
knn = {"attrs":[{"name":"embedding_vector","type":"hnsw","hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200,"model_name":"Xenova/all-MiniLM-L6-v2","from":""}]}
}Important notes for plain mode:
- When using
model_name, you must not specifydims- the model automatically determines the vector dimensions. Thedimsandmodel_nameparameters are mutually exclusive. - When not using
model_name(manual vector insertion), you must specifydimsto indicate the vector dimensions. - The
fromparameter specifies which fields to use for embedding generation (comma-separated list, or empty string for all text/string fields). This parameter is required when usingmodel_name. - For API-based models (OpenAI, Voyage, Jina), include the
api_keyparameter in the knn configuration
When using auto embeddings, you can:
- Omit the vector field and let Manticore generate embeddings from the fields listed in
FROM - Provide your own vector explicitly for a row
- Provide
()to skip generation and store an all-zero vector
If you later run ALTER TABLE ... REBUILD EMBEDDINGS, rows that currently contain zero vectors from () are regenerated too.
- SQL
- JSON
Insert text data only - embeddings generated automatically
INSERT INTO products (title) VALUES
('machine learning artificial intelligence'),
('banana fruit sweet yellow');Insert a user-provided vector
INSERT INTO products (title, embedding_vector) VALUES
('machine learning artificial intelligence', (0.653448,0.192478,0.017971,0.339821));Insert multiple fields - both used for embedding if FROM='title,description'
INSERT INTO products_openai (title, description) VALUES
('smartphone', 'latest mobile device with advanced features'),
('laptop', 'portable computer for work and gaming');Insert empty vector (no auto generation; stores a zero vector)
INSERT INTO products (title, embedding_vector) VALUES
('no embedding item', ());Insert text data only - embeddings generated automatically
POST /sql?mode=raw -d "INSERT INTO products (title) VALUES ('machine learning artificial intelligence'),('banana fruit sweet yellow')"Insert multiple fields - both used for embedding if FROM='title,description'
POST /sql?mode=raw -d "INSERT INTO products_openai (title, description) VALUES ('smartphone', 'latest mobile device with advanced features'), ('laptop', 'portable computer for work and gaming')"Insert empty vector (document excluded from vector search)
POST /sql?mode=raw -d "INSERT INTO products (title, embedding_vector) VALUES ('no embedding item', ())"Search works the same way - provide your query text and Manticore will generate embeddings and find similar documents:
- SQL
- JSON
SELECT id, knn_dist() FROM products WHERE knn(embedding_vector, 'machine learning');Using text query with auto-embeddings
POST /search
{
"table": "products",
"knn": {
"field": "embedding_vector",
"query": "machine learning"
}
}Using vector query directly
POST /search
{
"table": "products",
"knn": {
"field": "embedding_vector",
"query": [0.1, 0.2, 0.3, 0.4]
}
}+------+------------+
| id | knn_dist() |
+------+------------+
| 1 | 0.12345678 |
| 2 | 0.87654321 |
+------+------------+
2 rows in set (0.00 sec){
"took": 0,
"timed_out": false,
"hits": {
"total": 2,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1,
"_knn_dist": 0.12345678,
"_source": {
"title": "machine learning artificial intelligence"
}
},
{
"_id": 2,
"_score": 1,
"_knn_dist": 0.87654321,
"_source": {
"title": "banana fruit sweet yellow"
}
}
]
}
}Alternatively, you can manually insert pre-computed vector data, ensuring it matches the dimensions you specified when creating the table. You can also insert an empty vector; this means that the document will be excluded from vector search results.
Important: When using hnsw_similarity='cosine', vectors are automatically normalized upon insertion to unit vectors (vectors with a mathematical length/magnitude of 1.0). This normalization preserves the direction of the vector while standardizing its length, which is required for efficient cosine similarity calculations. This means the stored values will differ from your original input values.
- SQL
- JSON
insert into test values ( 1, 'yellow bag', (0.653448,0.192478,0.017971,0.339821) ), ( 2, 'white bag', (-0.148894,0.748278,0.091892,-0.095406) );POST /insert
{
"table":"test_vec",
"id":1,
"doc": { "title" : "yellow bag", "image_vector" : [0.653448,0.192478,0.017971,0.339821] }
}
POST /insert
{
"table":"test_vec",
"id":2,
"doc": { "title" : "white bag", "image_vector" : [-0.148894,0.748278,0.091892,-0.095406] }
}Query OK, 2 rows affected (0.00 sec){
"table":"test",
"_id":1,
"created":true,
"result":"created",
"status":201
}
{
"table":"test",
"_id":2,
"created":true,
"result":"created",
"status":201
}Now, you can perform a KNN search using the knn clause in either SQL or JSON format. Both interfaces support the same essential parameters, ensuring a consistent experience regardless of the format you choose:
- SQL:
select ... from <table name> where knn ( <field>, <query vector> [,<options>] ) - JSON:
POST /search { "table": "<table name>", "knn": { "field": "<field>", "query": "<text or vector>", "ef": <ef>, "rescore": <rescore>, "oversampling": <oversampling> } }
The parameters are:
field: This is the name of the float vector attribute containing vector data.k: Deprecated option. Use querylimitinstead. It was used to specify the quantity of documents that a single HNSW index should return. However, the actual number of documents included in the final results may vary. For instance, if the system is dealing with real-time tables divided into disk chunks, each chunk could returnkdocuments, leading to a total that exceeds the specifiedk(as the cumulative count would benum_chunks * k). On the other hand, the final document count might be less thankif, after requestingkdocuments, some are filtered out based on specific attributes. It's important to note that the parameterkdoes not apply to ramchunks. In the context of ramchunks, the retrieval process operates differently, and thus, thekparameter's effect on the number of documents returned is not applicable.query: (Recommended parameter) The search query, which can be either:- Text string: Automatically converted to embeddings if the field has auto-embeddings configured. Will return an error if the field doesn't have auto-embeddings.
- Vector array: Works the same as
query_vector.
query_vector: (Legacy parameter) The search vector as an array of numbers. Still supported for backward compatibility. Note: Use eitherqueryorquery_vector, not both in the same request.ef: optional size of the dynamic list used during the search. A higherefleads to more accurate but slower search. The default is 10.rescore: Enables KNN rescoring (enabled by default). Set to0in SQL orfalsein JSON to disable rescoring. After the KNN search is completed using quantized vectors (with possible oversampling), distances are recalculated with the original (full-precision) vectors and results are re-sorted to improve ranking accuracy.oversampling: Sets a factor (float value) by whichkis multiplied when executing the KNN search, causing more candidates to be retrieved than needed using quantized vectors.oversampling=3.0is applied by default. These candidates can be re-evaluated later if rescoring is enabled. Oversampling also works with non-quantized vectors. Since it increasesk, which affects how the HNSW index works, it may cause a small change in result accuracy.early_termination: Enables or disables adaptive early termination during HNSW graph traversal. Enabled by default. Set to0in SQL orfalsein JSON to disable. See Early termination for details.
Documents are always sorted by their distance to the search vector. Any additional sorting criteria you specify will be applied after this primary sort condition. For retrieving the distance, there is a built-in function called knn_dist().
- SQL
- JSON
select id, knn_dist() from test where knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=2000, oversampling=3.0, rescore=1 } );POST /search
{
"table": "test",
"knn":
{
"field": "image_vector",
"query": [0.286569,-0.031816,0.066684,0.032926],
"ef": 2000,
"rescore": true,
"oversampling": 3.0
}
}+------+------------+
| id | knn_dist() |
+------+------------+
| 1 | 0.28146550 |
| 2 | 0.81527930 |
+------+------------+
2 rows in set (0.00 sec){
"took":0,
"timed_out":false,
"hits":
{
"total":2,
"total_relation":"eq",
"hits":
[
{
"_id": 1,
"_score":1,
"_knn_dist":0.28146550,
"_source":
{
"title":"yellow bag",
"image_vector":[0.653448,0.192478,0.017971,0.339821]
}
},
{
"_id": 2,
"_score":1,
"_knn_dist":0.81527930,
"_source":
{
"title":"white bag",
"image_vector":[-0.148894,0.748278,0.091892,-0.095406]
}
}
]
}
}HNSW indexes need to be fully loaded into memory to perform KNN search, which can lead to significant memory consumption. To reduce memory usage, scalar quantization can be applied - a technique that compresses high-dimensional vectors by representing each component (dimension) with a limited number of discrete values. Manticore supports 8-bit and 1-bit quantization, meaning each vector component is compressed from a 32-bit float to 8 bits or even 1 bit, reducing memory usage by 4x or 32x, respectively. These compressed representations also allow for faster distance calculations, as more vector components can be processed in a single SIMD instruction. Although scalar quantization introduces some approximation error, it is often a worthwhile trade-off between search accuracy and resource efficiency. For even better accuracy, quantization can be combined with rescoring and oversampling: more candidates are retrieved than requested, and distances for these candidates are recalculated using the original 32-bit float vectors.
Supported quantization types include:
8bit: Each vector component is quantized to 8 bits.1bit: Each vector component is quantized to 1 bit. Asymmetric quantization is used, with query vectors quantized to 4 bits and stored vectors to 1 bit. This approach offers greater precision than simpler methods, though with some performance trade-off.1bitsimple: Each vector component is quantized to 1 bit. This method is faster than1bit, but typically less accurate.
- SQL
- JSON
create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit');POST /sql?mode=raw -d "create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit')"Query OK, 0 rows affected (0.01 sec)[
{
"total": 0,
"error": "",
"warning": ""
}
]NOTE: Finding similar documents by id requires Manticore Buddy. If it doesn't work, make sure Buddy is installed.
Finding documents similar to a specific one based on its unique ID is a common task. For instance, when a user views a particular item, Manticore Search can efficiently identify and display a list of items that are most similar to it in the vector space. Here's how you can do it:
- SQL:
select ... from <table name> where knn ( <field>, <k>, <document id> ) - JSON:
POST /search { "table": "<table name>", "knn": { "field": "<field>", "doc_id": <document id>, "k": <k> } }
The parameters are:
field: This is the name of the float vector attribute containing vector data.k: This represents the number of documents to return and is a key parameter for Hierarchical Navigable Small World (HNSW) indexes. It specifies the quantity of documents that a single HNSW index should return. However, the actual number of documents included in the final results may vary. For instance, if the system is dealing with real-time tables divided into disk chunks, each chunk could returnkdocuments, leading to a total that exceeds the specifiedk(as the cumulative count would benum_chunks * k). On the other hand, the final document count might be less thankif, after requestingkdocuments, some are filtered out based on specific attributes. It's important to note that the parameterkdoes not apply to ramchunks. In the context of ramchunks, the retrieval process operates differently, and thus, thekparameter's effect on the number of documents returned is not applicable.document id: Document ID for KNN similarity search.
- SQL
- JSON
select id, knn_dist() from test where knn ( image_vector, 5, 1 );POST /search
{
"table": "test",
"knn":
{
"field": "image_vector",
"doc_id": 1,
"k": 5
}
}+------+------------+
| id | knn_dist() |
+------+------------+
| 2 | 0.81527930 |
+------+------------+
1 row in set (0.00 sec){
"took":0,
"timed_out":false,
"hits":
{
"total":1,
"total_relation":"eq",
"hits":
[
{
"_id": 2,
"_score":1643,
"_knn_dist":0.81527930,
"_source":
{
"title":"white bag",
"image_vector":[-0.148894,0.748278,0.091892,-0.095406]
}
}
]
}
}Manticore also supports additional filtering of documents returned by the KNN search, either by full-text matching, attribute filters, or both.
- SQL
- JSON
select id, knn_dist() from test where knn ( image_vector, 5, (0.286569,-0.031816,0.066684,0.032926) ) and match('white') and id < 10;POST /search
{
"table": "test",
"knn":
{
"field": "image_vector",
"query": [0.286569,-0.031816,0.066684,0.032926],
"k": 5
},
"query":
{
"bool":
{
"must":
[
{ "match": {"_all":"white"} },
{ "range": { "id": { "lt": 10 } } }
]
}
}
}+------+------------+
| id | knn_dist() |
+------+------------+
| 2 | 0.81527930 |
+------+------------+
1 row in set (0.00 sec){
"took":0,
"timed_out":false,
"hits":
{
"total":1,
"total_relation":"eq",
"hits":
[
{
"_id": 2,
"_score":1643,
"_knn_dist":0.81527930,
"_source":
{
"title":"white bag",
"image_vector":[-0.148894,0.748278,0.091892,-0.095406]
}
}
]
}
}When combining KNN vector search with attribute filters, Manticore supports two strategies that differ in when the filter is applied relative to HNSW graph traversal.
-
Prefiltering (default;
prefilter=1(SQL) or"prefilter": true(JSON, default)) passes the filter into the HNSW traversal itself. Each candidate is checked against the filter before being added to the result heap - only matching documents contribute to the finalkresults. This reduces wasted distance computations and guarantees that exactlykmatching documents are returned (assumingkmatching documents exist). -
Postfiltering (
prefilter=0(SQL) or"prefilter": false(JSON)) runs the KNN search first over the full dataset, then applies the filter to the results. This is safe and predictable: the HNSW graph is traversed without interference, and the filter only affects which results are returned to the client. The downside is that the graph may spend effort on candidates that will ultimately be discarded. With a tight filter that matches only a small fraction of documents, the returnedkresults may be significantly fewer than requested, because most KNN candidates fail the filter.
Internally, Manticore uses an ACORN-1-based algorithm for prefiltering. A naive prefilter would simply skip non-matching nodes, which risks losing "bridge" nodes that connect otherwise-separated parts of the HNSW graph, causing recall to collapse as the filter becomes more selective. ACORN-1 avoids this: when a node fails the filter, its neighbors are still added to the exploration queue. This allows the traversal to route around filtered-out nodes and maintain graph connectivity. ACORN-1 exploration is activated automatically when fewer than 60% of the total documents pass the filter.
Automatic brute-force fallback: When prefiltering is enabled, Manticore estimates whether it is cheaper to run a brute-force distance scan over the filtered subset than to traverse the HNSW graph. The estimate compares the expected number of nodes visited by HNSW against the number of documents that pass the filter. If the filtered set is small enough that scanning it directly is faster, Manticore automatically switches to brute-force, skipping HNSW entirely. This ensures correctness and good performance even under extreme selectivity.
- SQL
- JSON
-- prefilter (default): filter applied during HNSW traversal (ACORN-1 used automatically)
SELECT id, knn_dist() FROM test
WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926) )
AND price < 100;
-- postfilter: KNN runs over full dataset, filter applied to results
SELECT id, knn_dist() FROM test
WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { prefilter=0 } )
AND price < 100;// prefilter (default): filter applied during HNSW traversal
POST /search
{
"table": "test",
"knn": {
"field": "image_vector",
"query": [0.286569,-0.031816,0.066684,0.032926]
},
"query": {
"range": { "price": { "lt": 100 } }
}
}
// postfilter: filter applied after KNN search
POST /search
{
"table": "test",
"knn": {
"field": "image_vector",
"query": [0.286569,-0.031816,0.066684,0.032926],
"prefilter": false
},
"query": {
"range": { "price": { "lt": 100 } }
}
}By default, Manticore uses an adaptive early termination algorithm during HNSW graph traversal. Instead of always exploring the full candidate set defined by ef, it monitors the rate at which new candidates improve the result set and stops early when that rate consistently falls below a threshold. This reduces the number of distance computations without significantly affecting result quality.
Early termination is enabled by default and is automatically disabled when k is 10 or fewer, since the overhead of the algorithm is not worthwhile for such small result sets. The performance benefit scales with k — the larger the result set, the more distance computations can be saved by stopping early.
Note that oversampling multiplies the effective k used during HNSW traversal, so early termination also benefits from oversampling: a higher effective k means more candidates to potentially skip.
To explicitly control early termination, use the early_termination option:
- SQL
- JSON
-- disable early termination
SELECT id, knn_dist() FROM test WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=200, early_termination=0 } );
-- enable early termination explicitly (default)
SELECT id, knn_dist() FROM test WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=200, early_termination=1 } );POST /search
{
"table": "test",
"knn":
{
"field": "image_vector",
"query": [0.286569,-0.031816,0.066684,0.032926],
"ef": 200,
"early_termination": false
}
}When to disable early termination:
- When result set precision is critical and you cannot afford any approximation beyond what HNSW already provides.
- When using low
kvalues (around 30 or fewer), where early termination provides little performance benefit but may reduce precision.
Hybrid search combines full-text (BM25) search with KNN vector search in a single query, fusing results using Reciprocal Rank Fusion (RRF). This leverages the strengths of both retrieval methods: keyword precision from BM25 and semantic understanding from vector similarity.
Full-text search excels at exact keyword matching and rare terms but misses conceptually similar content. Vector search captures semantic meaning but can be noisy on ambiguous queries. Hybrid search combines both, so documents that score well on either or both signals are surfaced.
RRF is a rank-based fusion algorithm. It operates on rank positions rather than raw scores, which avoids the need to normalize incompatible score scales (BM25 scores are unbounded; KNN distances have a different scale).
RRF_score(d) = SUM over all result sets r: weight_r / (rank_constant + rank_r(d))
Where:
dis a documentrank_r(d)is the document's 1-based position in result setr(sorted by that retriever's score)rank_constantis a smoothing constant (default: 60, configurable via therank_constantoption)weight_ris an optional per-retriever weight (default: 1.0)
If a document does not appear in a particular result set, its contribution from that set is 0.
rank_constant=60is the default.- Lower values (e.g. 10) amplify differences between top-ranked items.
- Higher values (e.g. 100) distribute influence more evenly across ranks.
Combine MATCH(...) and KNN(...) in the WHERE clause, with OPTION fusion_method='rrf':
- SQL
- JSON
SELECT id, hybrid_score()
FROM t
WHERE match('machine learning')
AND knn(vec, (0.1, 0.1, 0.1, 0.1))
OPTION fusion_method='rrf';POST /search
{
"table": "t",
"knn": {
"field": "vec",
"query_vector": [0.1, 0.1, 0.1, 0.1]
},
"query": { "match": { "title": "machine learning" } },
"options": { "fusion_method": "rrf" }
}This runs the text search and KNN search as independent parallel sub-queries, then fuses the results using RRF. Without fusion_method='rrf', the query runs as a regular KNN search filtered by the text match (pre-hybrid behavior).
In that pre-hybrid mode, KNN ranking still takes precedence. If knn_dist() is available and you do not explicitly sort by it, Manticore prepends knn_dist() ASC to the sort order. In practice, ORDER BY weight() DESC becomes a secondary tie-breaker rather than a global BM25 sort.
hybrid_score()- the RRF fusion score (only available in hybrid queries)weight()- the BM25 text match scoreknn_dist()- the vector distance (minimum across all KNN sub-queries if multiple)
| Option | Type | Default | Description |
|---|---|---|---|
fusion_method |
string | (none) | Set to 'rrf' to enable hybrid search. Required. |
rank_constant |
int | 60 | Smoothing constant in the RRF formula |
window_size |
int | 0 (auto) | How many results each sub-query retrieves before fusion. When 0, auto-computed from KNN k (with oversampling) and query LIMIT |
fusion_weights |
tuple | (all 1.0) | Per-sub-query weights for RRF scoring |
- SQL
- JSON
-- Default rank_constant=60 (gentler ranking)
SELECT id, hybrid_score() FROM t
WHERE match('machine learning') AND knn(vec, (0.1, 0.1, 0.1, 0.1))
OPTION fusion_method='rrf';
-- rank_constant=10 (sharper top-rank differences)
SELECT id, hybrid_score() FROM t
WHERE match('machine learning') AND knn(vec, (0.1, 0.1, 0.1, 0.1))
OPTION fusion_method='rrf', rank_constant=10;POST /search
{
"table": "t",
"knn": { "field": "vec", "query_vector": [0.1, 0.1, 0.1, 0.1] },
"query": { "match": { "title": "machine learning" } },
"options": { "fusion_method": "rrf", "rank_constant": 10 }
}Standard WHERE filters work alongside hybrid search. Filters are applied to both the text and KNN sub-queries:
- SQL
- JSON
SELECT id, category, hybrid_score()
FROM t
WHERE match('machine learning')
AND knn(vec, (0.1, 0.1, 0.1, 0.1))
AND category = 1
OPTION fusion_method='rrf';POST /search
{
"table": "t",
"knn": { "field": "vec", "query_vector": [0.1, 0.1, 0.1, 0.1] },
"query": {
"bool": {
"must": [
{ "match": { "title": "machine learning" } },
{ "equals": { "category": 1 } }
]
}
},
"options": { "fusion_method": "rrf" }
}By default, results are sorted by hybrid_score() DESC. You can override this:
This section applies to true hybrid queries, i.e. queries using OPTION fusion_method='rrf'. Without fusion_method='rrf', a query that includes KNN(...) is not fused and remains KNN-first, so ORDER BY weight() DESC does not produce a globally weight-sorted result set.
- SQL
-- Sort by hybrid score ascending
SELECT id, hybrid_score() FROM t
WHERE match('machine learning') AND knn(vec, (0.1, 0.1, 0.1, 0.1))
ORDER BY hybrid_score() ASC
OPTION fusion_method='rrf';
-- Sort by text weight
SELECT id, weight() FROM t
WHERE match('machine learning') AND knn(vec, (0.1, 0.1, 0.1, 0.1))
ORDER BY weight() DESC, id ASC
OPTION fusion_method='rrf';
-- Sort by KNN distance
SELECT id, knn_dist() FROM t
WHERE match('machine learning') AND knn(vec, (0.1, 0.1, 0.1, 0.1))
ORDER BY knn_dist() ASC
OPTION fusion_method='rrf';If the text query matches no documents, only KNN results contribute to the RRF score:
SELECT id, hybrid_score() FROM t
WHERE match('xyznonexistent') AND knn(vec, (0.1, 0.1, 0.1, 0.1))
OPTION fusion_method='rrf';
-- Returns results ranked purely by KNN rank
A single hybrid query can combine text search with multiple KNN searches on different vector attributes. All are fused together via RRF:
- SQL
- JSON
-- Three-way fusion: text + vec1 KNN + vec2 KNN
SELECT id, hybrid_score()
FROM t
WHERE match('machine learning')
AND knn(vec1, (0.1, 0.1, 0.1, 0.1))
AND knn(vec2, (1.0, 0.0, 0.0, 0.0))
OPTION fusion_method='rrf';
-- KNN-only fusion (no text), two vector searches
SELECT id, hybrid_score()
FROM t
WHERE knn(vec1, (0.1, 0.1, 0.1, 0.1))
AND knn(vec2, (1.0, 0.0, 0.0, 0.0))
OPTION fusion_method='rrf';POST /search
{
"table": "t",
"knn": [
{ "field": "vec1", "query_vector": [0.1, 0.1, 0.1, 0.1] },
{ "field": "vec2", "query_vector": [1.0, 0.0, 0.0, 0.0] }
],
"query": { "match": { "title": "machine learning" } },
"options": { "fusion_method": "rrf" }
}Multiple KNN searches without fusion_method produce an error.
By default, all sub-queries contribute equally (weight 1.0). To give different importance to text vs KNN searches, use fusion_weights with explicit aliases:
- SQL
- JSON
SELECT id, hybrid_score()
FROM t
WHERE match('machine learning') AS text
AND knn(vec1, (0.1, 0.1, 0.1, 0.1)) AS dense1
AND knn(vec2, (1.0, 0.0, 0.0, 0.0)) AS dense2
OPTION fusion_method='rrf',
fusion_weights=(text=0.7, dense1=0.2, dense2=0.1);POST /search
{
"table": "t",
"knn": [
{ "field": "vec1", "query_vector": [0.1, 0.1, 0.1, 0.1], "name": "dense1" },
{ "field": "vec2", "query_vector": [1.0, 0.0, 0.0, 0.0], "name": "dense2" }
],
"query": { "match": { "title": "machine learning" } },
"options": {
"fusion_method": "rrf",
"fusion_weights": { "query": 0.7, "dense1": 0.2, "dense2": 0.1 }
}
}SQL:
- Use
AS aliasonMATCH(...)andKNN(...)to name them. There are no implicit/default aliases. - Omitted aliases default to weight 1.0.
- Referencing a non-existent alias produces an error.
JSON:
"query"is the fixed alias for the full-text sub-query.- KNN aliases are set via the
"name"property on each KNN entry. - A KNN entry named
"query"collides with the text alias and produces an error. - Implicit aliases (field names without explicit
"name") are not supported infusion_weights.
You can specify weights for only some sub-queries; the rest default to 1.0:
-- Only boost text, KNN searches default to weight 1.0
SELECT id, hybrid_score()
FROM t
WHERE match('machine learning') AS text
AND knn(vec1, (0.1, 0.1, 0.1, 0.1)) AS dense1
AND knn(vec2, (1.0, 0.0, 0.0, 0.0)) AS dense2
OPTION fusion_method='rrf', fusion_weights=(text=2.0);
For tables with auto-embeddings configured on a float_vector attribute, hybrid_match() provides a shorthand that automatically runs both text and KNN searches from a single query string:
- SQL
-- Explicit vector field
SELECT id, hybrid_score() FROM t WHERE hybrid_match('machine learning', vec);
-- Auto-detect vector field (requires exactly one auto-embedding attribute)
SELECT id, hybrid_score() FROM t WHERE hybrid_match('machine learning');
-- With custom k and rank_constant
SELECT id, hybrid_score() FROM t
WHERE hybrid_match('machine learning', vec, {k=3})
OPTION rank_constant=10;
-- With attribute filter
SELECT id, hybrid_score() FROM t
WHERE hybrid_match('machine learning', vec) AND category=1;hybrid_match() automatically:
- Runs the text query as a BM25 full-text search
- Generates an embedding from the same text string
- Runs a KNN search using that embedding
- Fuses results via RRF
Requirement: The vector attribute must have model_name and from configured for auto-embeddings. Without them, hybrid_match() returns an error.
For tables with auto-embeddings, a "hybrid" property provides a shorthand in JSON:
- JSON
POST /search
{
"table": "hj",
"hybrid": { "query": "machine learning" }
}
POST /search
{
"table": "hj",
"hybrid": { "query": "machine learning", "field": "vec" }
}
POST /search
{
"table": "hj",
"hybrid": { "query": "machine learning" },
"options": { "rank_constant": 10 }
}The "hybrid" property cannot be used together with "knn".
When the vector attribute has auto-embeddings, you can use "query" (string) instead of "query_vector" (array) in the knn object:
POST /search
{
"table": "ht",
"knn": { "field": "vec", "query": "machine learning", "k": 5 },
"query": { "match": { "title": "machine learning" } },
"options": { "fusion_method": "rrf" }
}
The string is automatically embedded at query time. Without auto-embeddings configured, this returns an error.
Internally, a hybrid query is split into N+1 parallel sub-queries:
- Job 0: Full-text (BM25) sub-query (skipped if text query is empty, to avoid polluting RRF with fullscan results)
- Jobs 1..N: One KNN sub-query per
knn(...)entry
All sub-queries run concurrently. After all complete, the RRF fusion:
- Collects ranked results from each sub-query
- For each document, accumulates RRF score contributions from every sub-query it appears in
- Sorts by fused RRF score descending
- Sets
knn_dist()to the minimum distance across all KNN sub-queries for each document - Preserves
weight()from the text sub-query
Conversational search lets Manticore Buddy answer questions over an existing vectorized table. Buddy retrieves the most relevant rows with KNN search, turns those rows into context, and sends the context plus the conversation history to an LLM.
It is managed from SQL with:
CREATE CHAT MODELSHOW CHAT MODELSDESCRIBE CHAT MODELDROP CHAT MODELCALL CHAT
You need a vectorized table and an LLM provider. The table requirements are covered below. Provider credentials can be set in CREATE CHAT MODEL with api_key, or supplied through the matching environment variable, such as OPENAI_API_KEY.
When CALL CHAT runs, Buddy builds a retrieval-augmented answer in this order:
- Buddy loads the chat model.
- Buddy loads the conversation history for the supplied conversation UUID. If no UUID is supplied, it creates one.
- Buddy inspects the target table and chooses a
FLOAT_VECTORfield. - The LLM decides how to handle the message: search again, answer from the previous search context, or answer without retrieval.
- Buddy runs KNN search with the selected vector field when retrieval is needed.
- Buddy builds the LLM context from the vector field's
from='...'source fields. - The configured LLM generates the answer.
- Buddy saves the user message and the assistant reply in the conversation history.
The fifth argument of CALL CHAT is called fields internally, but for conversational search it means the vector field used by knn(...). It is not a list of fields to return. Buddy selects rows with SELECT *, then removes vector columns from the sources payload so the response does not include large embedding values.
The table must have at least one FLOAT_VECTOR field configured for auto embeddings. The vector field must include from='...', because Buddy uses those source fields as LLM context.
The examples below use onnx-models/all-MiniLM-L12-v2-onnx, which runs through the recommended ONNX path and does not require an embedding API key.
- SQL
CREATE TABLE docs (
id BIGINT,
title TEXT,
content TEXT,
embedding FLOAT_VECTOR
knn_type='hnsw'
hnsw_similarity='cosine'
model_name='onnx-models/all-MiniLM-L12-v2-onnx'
from='title,content'
) TYPE='rt';
INSERT INTO docs(id, title, content) VALUES
(1, 'Vector search', 'Vector search compares embeddings to find semantically similar documents.'),
(2, 'Full-text search', 'Full-text search matches terms and phrases in indexed text.');If CALL CHAT does not specify a vector field, Buddy uses the first FLOAT_VECTOR field found in the table definition.
Use CREATE CHAT MODEL to store the LLM provider, model id, and retrieval settings.
- SQL
CREATE CHAT MODEL assistant (
model='openai:gpt-4o-mini'
);You can also set provider options and retrieval limits:
- SQL
CREATE CHAT MODEL support_assistant (
model='openai:gpt-4o-mini',
api_key='your-provider-api-key',
base_url='http://host.docker.internal:8787/v1',
timeout=60,
retrieval_limit=5,
max_document_length=3000
);Common options:
| Option | Required | Description |
|---|---|---|
model |
Yes | LLM model id in provider:model format. |
description |
No | Stored description. |
api_key |
No | Provider API key passed to the llm extension. |
base_url |
No | Provider or proxy base URL. |
timeout |
No | LLM request timeout, 1..65536. |
retrieval_limit |
No | Number of documents requested from KNN, 1..50; default is 5. |
max_document_length |
No | Per-document context limit. 0 disables truncation; 100..65536 truncates; default is 2000. |
Chat model names may contain only letters, numbers, and underscores.
The model option must use provider:model format:
model='openai:gpt-4o-mini'
Provider api_key is optional if the provider key is already available in Buddy's environment. For example, a Docker Compose service can pass provider keys like this:
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
If api_key is not set in CREATE CHAT MODEL, the llm extension can use the matching provider environment variable. Set api_key in the chat model only when you need this model to use a different key.
CALL CHAT(
'query',
'table',
'model_name',
'conversation_uuid',
'vector_field'
);
Arguments are positional only:
| Position | Argument | Required | Description |
|---|---|---|---|
| 1 | query |
Yes | User question. |
| 2 | table |
Yes | Table to search. |
| 3 | model_name |
Yes | Chat model name. |
| 4 | conversation_uuid |
No | Existing conversation id, or an empty string. |
| 5 | fields / vector field |
No | FLOAT_VECTOR field used in knn(...). |
The table argument must be a plain table identifier, optionally qualified as database.table. The vector field argument must be a plain field identifier.
Use CALL CHAT with a query, a table, and a chat model.
- SQL
CALL CHAT(
'What is vector search?',
'docs',
'assistant'
);To continue a conversation, pass the same conversation UUID:
- SQL
CALL CHAT(
'Can you explain it with an example?',
'docs',
'assistant',
'docs-chat-001'
);To search a specific vector field, pass it as the fifth argument:
- SQL
CALL CHAT(
'Find documents where the title is about vector search',
'docs',
'assistant',
'',
'title_embedding'
);When the fifth argument is present, Buddy checks that the field exists and is a FLOAT_VECTOR. If the argument is omitted, Buddy detects the first FLOAT_VECTOR field from SHOW CREATE TABLE.
When Buddy needs retrieval, it runs KNN search on the selected vector field and returns up to retrieval_limit rows. The default distance threshold is 0.8.
Buddy uses the retrieved rows as LLM context. The same rows are returned in sources, with knn_dist included and FLOAT_VECTOR columns removed.
max_document_length limits how much text from each source row can be sent to the LLM. Use 0 to disable truncation; otherwise use a value from 100 to 65536.
CALL CHAT returns one row:
| Column | Description |
|---|---|
conversation_uuid |
Existing or generated conversation id. |
user_query |
Original user query. |
search_query |
Standalone search query used for retrieval. |
response |
LLM answer. |
sources |
JSON string containing retrieved source rows. |
Example response shape:
{
"conversation_uuid": "docs-chat-001",
"user_query": "What is vector search?",
"search_query": "vector search, embeddings, similarity search",
"response": "Vector search finds similar items by comparing embeddings...",
"sources": "[{\"id\":1,\"title\":\"Vector Search\",\"content\":\"...\",\"knn_dist\":0.12}]"
}
Vector fields are not included in sources.
List models:
- SQL
SHOW CHAT MODELS;Describe a model:
- SQL
DESCRIBE CHAT MODEL assistant;Drop a model:
- SQL
DROP CHAT MODEL assistant;Drop safely:
- SQL
DROP CHAT MODEL IF EXISTS assistant;SHOW CHAT MODELS returns name, model, and created_at. DESCRIBE CHAT MODEL returns property and value; stored API keys are shown as HIDDEN.
Dropping a chat model also drops that model's conversation history table. Conversation history is stored per model and written with a 30-day TTL.
ALTER TABLE table ADD COLUMN column_name [{INTEGER|INT|BIGINT|FLOAT|BOOL|MULTI|MULTI64|JSON [secondary_index='1']|STRING|TEXT [INDEXED [ATTRIBUTE]]|TIMESTAMP|FLOAT_VECTOR [KNN options]}] [engine='columnar']
ALTER TABLE table DROP COLUMN column_name
ALTER TABLE table MODIFY COLUMN column_name bigint
This feature only supports adding one field at a time for RT tables or the expansion of an int column to bigint. The supported data types are:
int- integer attributetimestamp- timestamp attributebigint- big integer attributefloat- float attributebool- boolean attributemulti- multi-valued integer attributemulti64- multi-valued bigint attributejson- json attribute; usesecondary_index='1'to create a secondary index on the JSONstring/text attribute/string attribute- string attributetext/text indexed stored/string indexed stored- full-text indexed field with original value stored in docstoretext indexed/string indexed- full-text indexed field, indexed only (the original value is not stored in docstore)text indexed attribute/string indexed attribute- full text indexed field + string attribute (not storing the original value in docstore)text stored/string stored- the value will be only stored in docstore, not full-text indexed, not a string attributefloat_vector- vector attribute. You can use the same KNN and auto-embedding options as inCREATE TABLE- adding
engine='columnar'to any attribute (except for json) will make it stored in the columnar storage
- ❗It's recommended to backup table files before
ALTERing it to avoid data corruption in case of a sudden power interruption or other similar issues. - Querying a table is impossible while a column is being added.
- Newly created scalar attributes are set to
0. - Newly added
float_vectorcolumns withoutMODEL_NAMEare initialized with zero vectors. - If you add a
float_vectorcolumn withMODEL_NAMEandFROM, existing rows are embedded automatically duringALTER TABLE ... ADD COLUMN. - When
MODEL_NAMEis specified,FROMis required. UseFROM=''to embed from alltextfields andstringattributes. ALTERwill not work for distributed tables and tables without any attributes.- You can't delete the
idcolumn. - When dropping a field which is both a full-text field and a string attribute the first
ALTER DROPdrops the attribute, the second one drops the full-text field. - Adding/dropping full-text field is only supported in the RT mode.
- Example
mysql> desc rt;
+------------+-----------+
| Field | Type |
+------------+-----------+
| id | bigint |
| text | field |
| group_id | uint |
| date_added | timestamp |
+------------+-----------+
mysql> alter table rt add column test integer;
mysql> desc rt;
+------------+-----------+
| Field | Type |
+------------+-----------+
| id | bigint |
| text | field |
| group_id | uint |
| date_added | timestamp |
| test | uint |
+------------+-----------+
mysql> alter table rt drop column group_id;
mysql> desc rt;
+------------+-----------+
| Field | Type |
+------------+-----------+
| id | bigint |
| text | field |
| date_added | timestamp |
| test | uint |
+------------+-----------+
mysql> alter table rt add column title text indexed;
mysql> desc rt;
+------------+-----------+------------+
| Field | Type | Properties |
+------------+-----------+------------+
| id | bigint | |
| text | text | indexed |
| title | text | indexed |
| date_added | timestamp | |
| test | uint | |
+------------+-----------+------------+
mysql> alter table rt add column title text attribute;
mysql> desc rt;
+------------+-----------+------------+
| Field | Type | Properties |
+------------+-----------+------------+
| id | bigint | |
| text | text | indexed |
| title | text | indexed |
| date_added | timestamp | |
| test | uint | |
| title | string | |
+------------+-----------+------------+
mysql> alter table rt drop column title;
mysql> desc rt;
+------------+-----------+------------+
| Field | Type | Properties |
+------------+-----------+------------+
| id | bigint | |
| text | text | indexed |
| title | text | indexed |
| date_added | timestamp | |
| test | uint | |
+------------+-----------+------------+
mysql> alter table rt drop column title;
mysql> desc rt;
+------------+-----------+------------+
| Field | Type | Properties |
+------------+-----------+------------+
| id | bigint | |
| text | text | indexed |
| date_added | timestamp | |
| test | uint | |
+------------+-----------+------------+ALTER TABLE table ft_setting='value'[, ft_setting2='value']
You can use ALTER to modify the full-text settings of your table in RT mode. However, it only affects new documents and not existing ones.
Example:
- create a table with a full-text field and
charset_tablethat allows only 3 searchable characters:a,bandc. - then we insert document 'abcd' and find it by query
abcd, thedjust gets ignored since it's not in thecharset_tablearray - then we understand, that we want
dto be searchable too, so we add it with help ofALTER - but the same query
where match('abcd')still says it searched byabc, because the existing document remembers previous contents ofcharset_table - then we add another document
abcdand search byabcdagain - now it finds the both documents and
show metasays it used two keywords:abc(to find the old document) andabcd(for the new one).
- Example
mysql> create table rt(title text) charset_table='a,b,c';
mysql> insert into rt(title) values('abcd');
mysql> select * from rt where match('abcd');
+---------------------+-------+
| id | title |
+---------------------+-------+
| 1514630637682688054 | abcd |
+---------------------+-------+
mysql> show meta;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| total | 1 |
| total_found | 1 |
| time | 0.000 |
| keyword[0] | abc |
| docs[0] | 1 |
| hits[0] | 1 |
+---------------+-------+
mysql> alter table rt charset_table='a,b,c,d';
mysql> select * from rt where match('abcd');
+---------------------+-------+
| id | title |
+---------------------+-------+
| 1514630637682688054 | abcd |
+---------------------+-------+
mysql> show meta
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| total | 1 |
| total_found | 1 |
| time | 0.000 |
| keyword[0] | abc |
| docs[0] | 1 |
| hits[0] | 1 |
+---------------+-------+
mysql> insert into rt(title) values('abcd');
mysql> select * from rt where match('abcd');
+---------------------+-------+
| id | title |
+---------------------+-------+
| 1514630637682688055 | abcd |
| 1514630637682688054 | abcd |
+---------------------+-------+
mysql> show meta;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| total | 2 |
| total_found | 2 |
| time | 0.000 |
| keyword[0] | abc |
| docs[0] | 1 |
| hits[0] | 1 |
| keyword[1] | abcd |
| docs[1] | 1 |
| hits[1] | 1 |
+---------------+-------+You can change the name of a real-time table in RT mode.
ALTER TABLE table_name RENAME new_table_name;
NOTE: Renaming a real-time table requires Manticore Buddy. If it doesn't work, make sure Buddy is installed.
- Example
ALTER TABLE table_name RENAME new_table_name;Query OK, 0 rows affected (0.00 sec)ALTER TABLE table RECONFIGURE
ALTER can also reconfigure an RT table in the plain mode, so that new tokenization, morphology and other text processing settings from the configuration file take effect for new documents. Note, that the existing document will be left intact. Internally, it forcibly saves the current RAM chunk as a new disk chunk and adjusts the table header, so that new documents are tokenized using the updated full-text settings.
- Example
mysql> show table rt settings;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| settings | |
+---------------+-------+
1 row in set (0.00 sec)
mysql> alter table rt reconfigure;
Query OK, 0 rows affected (0.00 sec)
mysql> show table rt settings;
+---------------+----------------------+
| Variable_name | Value |
+---------------+----------------------+
| settings | morphology = stem_en |
+---------------+----------------------+
1 row in set (0.00 sec)ALTER TABLE table REBUILD SECONDARY
You can also use ALTER to rebuild secondary indexes in a given table. Sometimes, a secondary index can be disabled for the entire table or for one or multiple attributes within the table:
- When an attribute is updated, its secondary index gets disabled.
- If Manticore loads a table with an old version of secondary indexes that is no longer supported, the secondary indexes will be disabled for the entire table.
ALTER TABLE table REBUILD SECONDARY rebuilds secondary indexes from attribute data and enables them again.
Additionally, an old version of secondary indexes may be supported but will lack certain features. REBUILD SECONDARY can be used to update secondary indexes.
- Example
ALTER TABLE rt REBUILD SECONDARY;Query OK, 0 rows affected (0.00 sec)ALTER TABLE table REBUILD KNN
The command reprocesses all vector data in the table and rebuilds the KNN index from scratch.
- Example
ALTER TABLE rt REBUILD KNN;Query OK, 0 rows affected (0.00 sec)ALTER TABLE table REBUILD EMBEDDINGS column_name
This command regenerates embeddings for one target float_vector column that has MODEL_NAME and FROM configured.
Use it when you want to rebuild vectors for an existing embedding column, for example when you want to reprocess rows after adding the column later with ALTER TABLE ... ADD COLUMN, or when you want to force regeneration for all rows.
Important behavior:
- The column name is mandatory. The command rebuilds one embedding column at a time.
- It regenerates embeddings for all rows in that column, not only rows with zero vectors.
- It also overwrites rows whose vectors were inserted manually, and rows where
()was used to skip generation and store a zero vector. - The target column must be an indexed
float_vectorwith an embedding model configured. FROM=''is allowed and means "use alltextfields andstringattributes".
Manticore does not persist whether the current vector in that column was generated automatically, provided explicitly by the user, or created from (). If you run REBUILD EMBEDDINGS, the stored values are regenerated from the configured FROM source for every row in the column, including rows whose current value is an all-zero vector.
- Example
ALTER TABLE products ADD COLUMN embedding FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2' MODEL_NAME='Xenova/all-MiniLM-L6-v2' FROM='title';
ALTER TABLE products REBUILD EMBEDDINGS embedding;Query OK, 0 rows affected (0.00 sec)ALTER can be used to modify API parameters when a remote model is used for auto-embeddings:
ALTER TABLE table_name MODIFY COLUMN column_name API_KEY='key';
ALTER TABLE table_name MODIFY COLUMN column_name API_URL='url';
ALTER TABLE table_name MODIFY COLUMN column_name API_TIMEOUT='seconds';
- Example
ALTER TABLE rt MODIFY COLUMN vector API_KEY='new-key';
ALTER TABLE rt MODIFY COLUMN vector API_URL='https://custom-api.example.com/v1/embeddings';
ALTER TABLE rt MODIFY COLUMN vector API_TIMEOUT='30';Notes:
API_KEY: The new API key is validated during the ALTER operation by making a real API request.API_URL: Set to an empty string ('') to revert to the default provider endpoint.API_TIMEOUT: Set to'0'to use the default timeout (10 seconds). Must be a non-negative integer.
To change the list of local or remote nodes in a distributed table, follow the same syntax you used to create the table. Just replace CREATE with ALTER in the command and remove type='distributed':
ALTER TABLE `distr_table_name` [[local='local_table_name'], [agent='host:port:remote_table'] ... ]
NOTE: Changing the schema of a distributed table online requires Manticore Buddy. If it doesn't work, make sure Buddy is installed.
- Example
ALTER TABLE local_dist local='index1' local='index2' agent='127.0.0.1:9312:remote_table';