diff --git a/cmake/GetColumnar.cmake b/cmake/GetColumnar.cmake index 1f8f04e233..dbdc496f0f 100644 --- a/cmake/GetColumnar.cmake +++ b/cmake/GetColumnar.cmake @@ -7,7 +7,7 @@ include ( update_bundle ) # Versions of API headers we are need to build with. set ( NEED_COLUMNAR_API 28 ) set ( NEED_SECONDARY_API 21 ) -set ( NEED_KNN_API 17 ) +set ( NEED_KNN_API 18 ) if (WIN32) set ( EXTENSION dll ) diff --git a/manual/english/Creating_a_table/Data_types.md b/manual/english/Creating_a_table/Data_types.md index f87cf097a1..512ffb666a 100755 --- a/manual/english/Creating_a_table/Data_types.md +++ b/manual/english/Creating_a_table/Data_types.md @@ -2576,6 +2576,12 @@ When creating a table with `float_vector` attributes for KNN search, you can spe - `HNSW_M`: Maximum connections in the graph (default: 16) - `HNSW_EF_CONSTRUCTION`: Construction time/accuracy trade-off (default: 200) +**Chunking parameters** (when using `MODEL_NAME`, see [Chunking strategies](../Searching/KNN.md#Chunking-strategies)): +- `CHUNK_STRATEGY`: how a document becomes vectors: `'truncate'` (default), `'mean'`, `'fixed'`, `'recursive'` or `'sentence'`. The last three produce several vectors per document and require a [`float_vector_array`](../Creating_a_table/Data_types.md#Float-vector-array) column. +- `MAX_TOKENS`: chunk size in tokens; `0` (default) uses the model's own limit +- `OVERLAP_TOKENS`: tokens shared between consecutive chunks; requires an explicit non-zero `MAX_TOKENS`; a large overlap is reduced so chunks still advance +- `MAX_CHUNKS`: ceiling on vectors per document; `0` (default) means unlimited + **Auto-embeddings parameters** (when using `MODEL_NAME`): - `MODEL_NAME`: The embedding model to use (e.g., `'Xenova/all-MiniLM-L6-v2'` for the fast ONNX path, `'sentence-transformers/all-MiniLM-L6-v2'`, or `'openai/text-embedding-ada-002'`) - `FROM`: Comma-separated list of field names to use for embedding generation, or empty string `''` to use all text/string fields @@ -2957,7 +2963,7 @@ When the attribute is configured for [KNN](../Searching/KNN.md), all vectors of - Currently only supported in real-time tables (not in plain tables) - Not supported in functions or expressions - Cannot be used in regular filters or sorting -- [Auto embeddings](../Searching/KNN.md#Auto-Embeddings-%28Recommended%29) are not available for this type: a model produces one vector per document, so `MODEL_NAME` is rejected. Vectors must be supplied explicitly. +- [Auto embeddings](../Searching/KNN.md#Auto-Embeddings-%28Recommended%29) work, but only with a chunking strategy that produces several vectors per document - see [Chunking strategies](../Searching/KNN.md#Chunking-strategies). Adding a model-backed `float_vector_array` with `ALTER TABLE ... ADD COLUMN`, and `ALTER TABLE ... REBUILD EMBEDDINGS` on one, are not supported yet; declare the column when creating the table. - Not compatible with the [Auto schema](../Data_creation_and_modification/Adding_documents_to_a_table/Adding_documents_to_a_real-time_table.md#Auto-schema) mechanism ### Using float vector arrays with KNN @@ -2965,7 +2971,7 @@ When the attribute is configured for [KNN](../Searching/KNN.md), all vectors of The parameters are the same ones [`float_vector`](../Creating_a_table/Data_types.md#Float-vector) takes: `KNN_TYPE`, `KNN_DIMS`, `HNSW_SIMILARITY`, plus the optional `HNSW_M`, `HNSW_EF_CONSTRUCTION` and [quantization](../Searching/KNN.md#Vector-quantization), with two differences: - `KNN_DIMS` is required, and **every** vector in **every** row must have exactly that many entries. A row whose vectors are a different width is rejected on insert. -- `MODEL_NAME` and `FROM` are not accepted. +- `MODEL_NAME` and `FROM` are accepted, together with a multi-vector `CHUNK_STRATEGY` that fills the array automatically. See [Chunking strategies](../Searching/KNN.md#Chunking-strategies). **What you can do:** - Run KNN searches that match a document on its closest vector diff --git a/manual/english/Creating_a_table/Local_tables/Plain_and_real-time_table_settings.md b/manual/english/Creating_a_table/Local_tables/Plain_and_real-time_table_settings.md index 793457d174..8459968a1a 100755 --- a/manual/english/Creating_a_table/Local_tables/Plain_and_real-time_table_settings.md +++ b/manual/english/Creating_a_table/Local_tables/Plain_and_real-time_table_settings.md @@ -559,8 +559,15 @@ knn = {"attrs":[{"name":"chunk_vectors","type":"hnsw","dims":768,"hnsw_similarit Two differences apply: -- `dims` is **required**, and every vector in every row must have exactly that many entries. -- `model_name` and `from` are **not** accepted — auto embeddings produce one vector per document, so they do not apply to this type. Vectors must be supplied explicitly. +- `dims` is **required** when vectors are supplied explicitly, and every vector in every row must have exactly that many entries. It must be **omitted** when `model_name` is used. +- `model_name`/`from` are accepted together with a multi-vector `chunk_strategy` (`fixed`, `recursive` or `sentence`), which fills the array with one vector per chunk: + +```ini +rt_attr_float_vector_array = chunk_vectors +knn = {"attrs":[{"name":"chunk_vectors","type":"hnsw","hnsw_similarity":"COSINE","model_name":"Xenova/all-MiniLM-L6-v2","from":"title,content","chunk_strategy":"sentence","max_tokens":256,"overlap_tokens":32}]} +``` + + See [Chunking strategies](../../Searching/KNN.md#Chunking-strategies) for the full option list and the `ALTER` limitations. All vectors are indexed together, and a KNN search returns each document once, scored by its closest vector. See [Float vector array](../../Creating_a_table/Data_types.md#Float-vector-array) and [Multiple vectors per document](../../Searching/KNN.md#Multiple-vectors-per-document). diff --git a/manual/english/Searching/KNN.md b/manual/english/Searching/KNN.md index 58d3200539..056d8cbae8 100755 --- a/manual/english/Searching/KNN.md +++ b/manual/english/Searching/KNN.md @@ -604,7 +604,7 @@ The query vector is still a single vector of `KNN_DIMS` entries, exactly as for With `HNSW_SIMILARITY='cosine'`, each stored vector is normalized on its own, so a document's vectors are compared against the query individually rather than as one long concatenated vector. -Everything else on this page applies unchanged: [filtering](../Searching/KNN.md#Filtering-KNN-vector-search-results), [prefilter/postfilter](../Searching/KNN.md#Filtering-strategies:-prefilter-vs.-postfilter), [quantization](../Searching/KNN.md#Vector-quantization), [early termination](../Searching/KNN.md#Early-termination) and rescoring behave the same way. The only capability that is unavailable is [auto embeddings](../Searching/KNN.md#Auto-Embeddings-%28Recommended%29), since a model yields one vector per document. +Everything else on this page applies unchanged: [filtering](../Searching/KNN.md#Filtering-KNN-vector-search-results), [prefilter/postfilter](../Searching/KNN.md#Filtering-strategies:-prefilter-vs.-postfilter), [quantization](../Searching/KNN.md#Vector-quantization), [early termination](../Searching/KNN.md#Early-termination) and rescoring behave the same way. [Auto embeddings](../Searching/KNN.md#Auto-Embeddings-%28Recommended%29) can fill the array for you, one vector per chunk - see [Chunking strategies](../Searching/KNN.md#Chunking-strategies) below. @@ -644,6 +644,77 @@ POST /search +### Chunking strategies + +By default an embedding model reads only as much of a document as fits its input window (typically a few hundred tokens) and the rest is silently dropped. For a title or a short description that is complete. For a long article it is not: nothing written past the cut-off can ever be retrieved, and no error reports it. + +A **chunking strategy** decides how a document becomes vectors. Set it with `CHUNK_STRATEGY` on a model-backed column: + +| Strategy | Vectors per document | What it does | +|---|---|---| +| `truncate` | 1 | Embeds as much as fits the model's window and drops the rest. The default, and the historical behavior. | +| `mean` | 1 | Splits the whole document, embeds every piece, and averages them into one vector. No tail loss, but a document covering several topics collapses to their average. | +| `fixed` | N | Fixed-size windows of `MAX_TOKENS` tokens. | +| `recursive` | N | Splits on a separator hierarchy: paragraph, then line, then sentence, then space; keeping each piece within `MAX_TOKENS`. | +| `sentence` | N | Sentence boundaries, packed up to `MAX_TOKENS`. | + +`truncate` and `mean` produce one vector per document and work on a [`float_vector`](../Creating_a_table/Data_types.md#Float-vector) column. `fixed`, `recursive` and `sentence` produce several, so they require a [`float_vector_array`](../Creating_a_table/Data_types.md#Float-vector-array) column; using one on a plain `float_vector` is rejected. + +The difference is what a match means. With one vector per document, search asks "is this document, as a whole, similar to the query?", and a single relevant paragraph is diluted by everything around it. With one vector per chunk, it asks "does this document *contain* something similar?": each chunk competes on its own, and the document is returned once, scored by its closest chunk (see [Multiple vectors per document](../Searching/KNN.md#Multiple-vectors-per-document)). + +**Options**, all valid only alongside `MODEL_NAME` and `KNN_TYPE='hnsw'`: + +* `CHUNK_STRATEGY`: one of the five above. Default `truncate`. +* `MAX_TOKENS`: chunk size in tokens. `0` (default) means the model's own limit; a larger value is clamped down to it. +* `OVERLAP_TOKENS`: how many tokens consecutive chunks share, so an idea split across a boundary still appears whole in one of them. Requires an explicit non-zero `MAX_TOKENS`. A large overlap is reduced so that chunks still advance through the document — currently anything above half of `MAX_TOKENS` is capped at half. +* `MAX_CHUNKS`: ceiling on vectors per document. `0` (default) means unlimited. + +Important points: + +* **`MAX_CHUNKS` discards text.** On overflow the remainder is merged into the last kept chunk, which then exceeds `MAX_TOKENS` and is truncated when embedded. Nothing is left as a visible gap, but the tail is gone. +* **Local and remote models chunk differently.** Local models split on the model's real tokens. Remote API models (OpenAI, Voyage, Jina) have no local tokenizer and use a conservative byte estimate instead, so the same text and settings will produce a different number of chunks than a local model would. + +`ALTER TABLE ... ADD COLUMN` with a model-backed `float_vector_array`, and `ALTER TABLE ... REBUILD EMBEDDINGS` on one, are not supported yet; existing rows can not be backfilled, so the column would stay empty. Declare such a column when creating the table. Both work normally for a `float_vector` column, including with `mean`. + + + + +##### SQL: + + + +```sql +-- one vector per sentence group, filled automatically from the text +CREATE TABLE articles ( + title text, + content text, + chunks float_vector_array knn_type='hnsw' hnsw_similarity='cosine' + model_name='Xenova/all-MiniLM-L6-v2' from='title,content' + chunk_strategy='sentence' max_tokens='256' overlap_tokens='32' +); + +INSERT INTO articles (id, title, content) VALUES (1, 'Rotating certificates', 'A long guide with many sections ...'); + +SELECT id, knn_dist() FROM articles WHERE knn(chunks, 5, 'how do I rotate a certificate'); +``` + + +##### JSON: + + + +```JSON +POST /cli -d "CREATE TABLE articles (title text, content text, chunks float_vector_array knn_type='hnsw' hnsw_similarity='cosine' model_name='Xenova/all-MiniLM-L6-v2' from='title,content' chunk_strategy='sentence' max_tokens='256')" + +POST /search +{ + "table": "articles", + "knn": { "field": "chunks", "query": "how do I rotate a certificate", "k": 5 } +} +``` + + + ### Vector quantization 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. diff --git a/mcl b/mcl index 5f1b59b5b4..8cb8f1d7f5 160000 --- a/mcl +++ b/mcl @@ -1 +1 @@ -Subproject commit 5f1b59b5b4173c344ff9efecd47a1bc9bc408a93 +Subproject commit 8cb8f1d7f50c08c72448a164d3c208edf2fdac83 diff --git a/src/accumulator.cpp b/src/accumulator.cpp index 974d43230d..53d846ddb1 100644 --- a/src/accumulator.cpp +++ b/src/accumulator.cpp @@ -148,13 +148,55 @@ static bool RepackBlob ( const CSphColumnInfo & tAttr, const CSphColumnInfo & tB } -static bool StoreEmbeddings ( const CSphSchema & tSchema, int iAttr, int iBlobAttr, int iColumnarAttr, DocstoreDoc_t & tDoc, std::unique_ptr & pNewBlobBuilder, std::unique_ptr & pNewColumnarBuilder, const IntVec_t & dDocstoreRemap, const std::vector & dEmbedding, CSphVector & dTmp, CSphString & sError ) +static bool StoreEmbeddings ( const CSphSchema & tSchema, int iAttr, int iBlobAttr, int iColumnarAttr, DocstoreDoc_t & tDoc, std::unique_ptr & pNewBlobBuilder, std::unique_ptr & pNewColumnarBuilder, const IntVec_t & dDocstoreRemap, const std::vector> & dEmbeddings, size_t iFrom, size_t iTo, CSphVector & dTmp, CSphString & sError ) { const CSphColumnInfo & tAttr = tSchema.GetAttr(iAttr); - dTmp.Resize ( dEmbedding.size() ); - ARRAY_FOREACH ( iEmb, dTmp ) - dTmp[iEmb] = sphF2DW ( dEmbedding[iEmb] ); + dTmp.Resize(0); + + const int iExpectedDims = tAttr.m_tKNN.m_iDims; + if ( iExpectedDims>0 ) + for ( size_t i = iFrom; i < iTo; i++ ) + if ( (int)dEmbeddings[i].size()!=iExpectedDims ) + { + sError.SetSprintf ( "attribute '%s': model returned a %d-value vector, index needs %d values", tAttr.m_sName.cstr(), (int)dEmbeddings[i].size(), iExpectedDims ); + return false; + } + + if ( tAttr.m_eAttrType==SPH_ATTR_FLOAT_VECTOR_ARRAY ) + { + // [dims][N*dims floats]. An empty range is a legal "no vectors" value + if ( iTo>iFrom ) + { + const int iDims = (int)dEmbeddings[iFrom].size(); + if ( !iDims ) + { + sError.SetSprintf ( "attribute '%s': model returned an empty vector", tAttr.m_sName.cstr() ); + return false; + } + + dTmp.Add(iDims); + for ( size_t i = iFrom; i < iTo; i++ ) + { + const std::vector & dVec = dEmbeddings[i]; + if ( (int)dVec.size()!=iDims ) + { + sError.SetSprintf ( "attribute '%s': model returned vectors of different widths (%d and %d) for one document", tAttr.m_sName.cstr(), iDims, (int)dVec.size() ); + return false; + } + + for ( float fVal : dVec ) + dTmp.Add ( sphF2DW(fVal) ); + } + } + } + else + { + assert ( iTo-iFrom<=1 ); + if ( iTo>iFrom ) + for ( float fVal : dEmbeddings[iFrom] ) + dTmp.Add ( sphF2DW(fVal) ); + } if ( tAttr.IsColumnar() ) pNewColumnarBuilder->SetAttr ( iColumnarAttr, dTmp.Begin(), dTmp.GetLength() ); @@ -167,11 +209,11 @@ static bool StoreEmbeddings ( const CSphSchema & tSchema, int iAttr, int iBlobAt if ( tAttr.IsStored() ) { int iId = dDocstoreRemap[iAttr]; - tDoc.m_dFields[iId].Resize ( dEmbedding.size()*sizeof(DWORD) ); - const BYTE * pStart = tDoc.m_dFields[iId].Begin(); - for ( auto i : dEmbedding ) + tDoc.m_dFields[iId].Resize ( dTmp.GetLength()*sizeof(DWORD) ); + BYTE * pStart = tDoc.m_dFields[iId].Begin(); + for ( auto i : dTmp ) { - *(DWORD*)pStart = sphF2DW(i); + *(DWORD*)pStart = (DWORD)i; pStart += sizeof(DWORD); } } @@ -180,7 +222,7 @@ static bool StoreEmbeddings ( const CSphSchema & tSchema, int iAttr, int iBlobAt } -bool RtAccum_t::RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRow, const CSphVector & dAttrsWithModels, std::unique_ptr & pNewBlobBuilder, std::unique_ptr & pNewColumnarBuilder, std::unique_ptr & pNewDocstoreBuilder, CSphVector & dAllIterators, const IntVec_t & dDocstoreRemap, const CSphColumnInfo * pBlobLoc, std::vector>> & dAllEmbeddings, CSphVector & dTmp, CSphString & sError ) +bool RtAccum_t::RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRow, const CSphVector & dAttrsWithModels, std::unique_ptr & pNewBlobBuilder, std::unique_ptr & pNewColumnarBuilder, std::unique_ptr & pNewDocstoreBuilder, CSphVector & dAllIterators, const IntVec_t & dDocstoreRemap, const CSphColumnInfo * pBlobLoc, std::vector>> & dAllEmbeddings, std::vector> & dAllOffsets, CSphVector & dTmp, CSphString & sError ) { int iBlobAttr = 0; int iColumnarAttr = 0; @@ -208,7 +250,9 @@ bool RtAccum_t::RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRo if ( bStoreGenerated ) { - if ( !StoreEmbeddings ( tSchema, i, iBlobAttr, iColumnarAttr, tDoc, pNewBlobBuilder, pNewColumnarBuilder, dDocstoreRemap, dAllEmbeddings[i][tRowID], dTmp, sError ) ) + const std::vector & dOffsets = dAllOffsets[i]; + assert ( (size_t)tRowID+1 < dOffsets.size() ); + if ( !StoreEmbeddings ( tSchema, i, iBlobAttr, iColumnarAttr, tDoc, pNewBlobBuilder, pNewColumnarBuilder, dDocstoreRemap, dAllEmbeddings[i], dOffsets[tRowID], dOffsets[tRowID+1], dTmp, sError ) ) return false; } else @@ -250,7 +294,7 @@ bool RtAccum_t::RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRo } -bool RtAccum_t::GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVector & dAttrsWithModels, std::vector>> & dAllEmbeddings, CSphString & sError ) +bool RtAccum_t::GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVector & dAttrsWithModels, std::vector>> & dAllEmbeddings, std::vector> & dAllOffsets, CSphString & sError ) { const AttrWithModel_t & tAttrWithModel = dAttrsWithModels[iAttr]; if ( !tAttrWithModel.m_pModel ) @@ -260,6 +304,7 @@ bool RtAccum_t::GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVe const auto & tAttr = m_pIndex->GetInternalSchema().GetAttr(iAttr); std::vector> & dEmbeddingsForAttr = dAllEmbeddings[iAttr]; + std::vector & dOffsetsForAttr = dAllOffsets[iAttr]; std::vector dTexts; DWORD uNumSkipped = 0; IntVec_t dResultIds(m_uAccumDocs); @@ -287,14 +332,16 @@ bool RtAccum_t::GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVe } std::string sErrorSTL; - std::vector> dEmbeddingsForAttrTmp; + std::vector> dTmpEmbeddings; + std::vector dTmpOffsets; bool bConverted = true; if ( uNumSkipped!=m_uAccumDocs ) { - auto & dEmbeddingsTarget = uNumSkipped ? dEmbeddingsForAttrTmp : dEmbeddingsForAttr; + // a multi-vector strategy returns several vectors per text; the single-vector ones still report + // offsets, they are just the identity. Asking for them always keeps one code path here auto fnConvert = [&] { - return tAttrWithModel.m_pModel->Convert ( dTexts, dEmbeddingsTarget, sErrorSTL, GetEmbeddingsThreadsToUse() ); + return tAttrWithModel.m_pModel->Convert ( dTexts, dTmpEmbeddings, sErrorSTL, GetEmbeddingsThreadsToUse(), &tAttr.m_tKNNChunk, &dTmpOffsets ); }; if ( Threads::IsInsideCoroutine() ) @@ -309,20 +356,32 @@ bool RtAccum_t::GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVe return false; } - if ( uNumSkipped ) + if ( !dTexts.empty() ) { - dEmbeddingsForAttr.resize ( dResultIds.GetLength() ); - ARRAY_FOREACH ( i, dResultIds ) + bool bOk = dTmpOffsets.size()==dTexts.size()+1 && dTmpOffsets.front()==0 && dTmpOffsets.back()==dTmpEmbeddings.size(); + for ( size_t i = 1; bOk && i < dTmpOffsets.size(); i++ ) + bOk = dTmpOffsets[i]>=dTmpOffsets[i-1]; + + if ( !bOk ) { - int iResultId = dResultIds[i]; - if ( iResultId!=-1 ) - dEmbeddingsForAttr[i] = dEmbeddingsForAttrTmp[iResultId]; + sError.SetSprintf ( "Error generating embeddings for attribute '%s': model returned %d vectors for %d input texts, grouped inconsistently", tAttr.m_sName.cstr(), (int)dTmpEmbeddings.size(), (int)dTexts.size() ); + return false; } } - else if ( dEmbeddingsForAttr.size()!=dResultIds.GetLength() ) + + // flatten into row order: row r owns dEmbeddingsForAttr[ dOffsetsForAttr[r] .. dOffsetsForAttr[r+1] ). + // a row that supplied its own vector keeps an empty range + dEmbeddingsForAttr.clear(); + dOffsetsForAttr.resize ( (size_t)m_uAccumDocs+1 ); + dOffsetsForAttr[0] = 0; + ARRAY_FOREACH ( i, dResultIds ) { - sError = "Error generating embeddings"; - return false; + int iResultId = dResultIds[i]; + if ( iResultId!=-1 ) + for ( size_t v = dTmpOffsets[iResultId]; v < dTmpOffsets[iResultId+1]; v++ ) + dEmbeddingsForAttr.push_back ( std::move ( dTmpEmbeddings[v] ) ); + + dOffsetsForAttr[i+1] = dEmbeddingsForAttr.size(); } return true; @@ -356,7 +415,7 @@ bool RtAccum_t::FetchEmbeddings ( TableEmbeddings_c * pEmbeddings, const CSphVec if ( !dAttrsWithModels[i].m_pModel ) continue; - assert ( tAttr.m_eAttrType==SPH_ATTR_FLOAT_VECTOR ); + assert ( tAttr.m_eAttrType==SPH_ATTR_FLOAT_VECTOR || tAttr.m_eAttrType==SPH_ATTR_FLOAT_VECTOR_ARRAY ); bRebuildColumnar |= bColumnar; bRebuildBlobs |= !bColumnar; bRebuildDocstore |= tAttr.IsStored(); @@ -385,10 +444,12 @@ bool RtAccum_t::FetchEmbeddings ( TableEmbeddings_c * pEmbeddings, const CSphVec int iRowSize = tSchema.GetRowSize(); int iAttrWithModel = 0; std::vector>> dAllEmbeddings; + std::vector> dAllOffsets; dAllEmbeddings.resize ( dAttrsWithModels.GetLength() ); + dAllOffsets.resize ( dAttrsWithModels.GetLength() ); ARRAY_FOREACH ( i, dAttrsWithModels ) { - if ( !GenerateEmbeddings( i, iAttrWithModel, dAttrsWithModels, dAllEmbeddings, sError ) ) + if ( !GenerateEmbeddings( i, iAttrWithModel, dAttrsWithModels, dAllEmbeddings, dAllOffsets, sError ) ) return false; if ( dAttrsWithModels[i].m_pModel ) @@ -400,7 +461,7 @@ bool RtAccum_t::FetchEmbeddings ( TableEmbeddings_c * pEmbeddings, const CSphVec CSphVector dTmp; CSphRowitem * pRow = m_dAccumRows.Begin(); for ( RowID_t tRowID = 0; tRowID < m_uAccumDocs; ++tRowID, pRow += iRowSize ) - if ( !RebuildStoragesForEmbeddings ( tRowID, pRow, dAttrsWithModels, pNewBlobBuilder, pNewColumnarBuilder, pNewDocstoreBuilder, dAllIterators, dDocstoreRemap, pBlobLoc, dAllEmbeddings, dTmp, sError ) ) + if ( !RebuildStoragesForEmbeddings ( tRowID, pRow, dAttrsWithModels, pNewBlobBuilder, pNewColumnarBuilder, pNewDocstoreBuilder, dAllIterators, dDocstoreRemap, pBlobLoc, dAllEmbeddings, dAllOffsets, dTmp, sError ) ) return false; if ( bRebuildBlobs ) diff --git a/src/accumulator.h b/src/accumulator.h index 6765a5b189..5fd08bd1c7 100644 --- a/src/accumulator.h +++ b/src/accumulator.h @@ -209,8 +209,10 @@ class RtAccum_t void ResetDict(); void SetupDocstore(); - bool RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRow, const CSphVector & dAttrsWithModels, std::unique_ptr & pNewBlobBuilder, std::unique_ptr & pNewColumnarBuilder, std::unique_ptr & pNewDocstoreBuilder, CSphVector & dAllIterators, const IntVec_t & dDocstoreRemap, const CSphColumnInfo * pBlobLoc, std::vector>> & dAllEmbeddings, CSphVector & dTmp, CSphString & sError ); - bool GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVector & dAttrsWithModels, std::vector>> & dAllEmbeddings, CSphString & sError ); + // dAllEmbeddings[attr] is as every row's vectors are concatenated in row order + // dAllOffsets[attr] groups them: row r owns [ off[r], off[r+1] ). + bool RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRow, const CSphVector & dAttrsWithModels, std::unique_ptr & pNewBlobBuilder, std::unique_ptr & pNewColumnarBuilder, std::unique_ptr & pNewDocstoreBuilder, CSphVector & dAllIterators, const IntVec_t & dDocstoreRemap, const CSphColumnInfo * pBlobLoc, std::vector>> & dAllEmbeddings, std::vector> & dAllOffsets, CSphVector & dTmp, CSphString & sError ); + bool GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVector & dAttrsWithModels, std::vector>> & dAllEmbeddings, std::vector> & dAllOffsets, CSphString & sError ); // defined in sphinxrt.cpp friend RtSegment_t* CreateSegment ( RtAccum_t*, int, ESphHitless, const VecTraits_T&, CSphString& ); diff --git a/src/attr_embedding.cpp b/src/attr_embedding.cpp index 26386a8cdd..8592879921 100644 --- a/src/attr_embedding.cpp +++ b/src/attr_embedding.cpp @@ -83,6 +83,7 @@ void AddAttrToIndex ( const SqlStmt_t & tStmt, CSphIndex * pIdx, CSphString & sE tCtx.m_tKNN = tStmt.m_tAlterKNN; tCtx.m_tKNNModel = tStmt.m_tAlterKNNModel; tCtx.m_sKNNFrom = tStmt.m_sAlterKnnFrom; + tCtx.m_tKNNChunk = tStmt.m_tAlterKNNChunk; if ( bIndexed || bStored ) { diff --git a/src/ddl.l b/src/ddl.l index db4ac49e14..6e11f4e9b7 100644 --- a/src/ddl.l +++ b/src/ddl.l @@ -59,6 +59,7 @@ "BIT" { YYSTOREBOUNDS; return TOK_BIT; } "BOOL" { YYSTOREBOUNDS; return TOK_BOOL; } "CACHE_PATH" { YYSTOREBOUNDS; return TOK_CACHE_PATH; } +"CHUNK_STRATEGY" { YYSTOREBOUNDS; return TOK_CHUNK_STRATEGY; } "CLUSTER" { YYSTOREBOUNDS; return TOK_CLUSTER; } "COLUMN" { YYSTOREBOUNDS; return TOK_COLUMN; } "COLUMNAR" { YYSTOREBOUNDS; return TOK_COLUMNAR; } @@ -90,6 +91,8 @@ "KNN_DIMS" { YYSTOREBOUNDS; return TOK_KNN_DIMS; } "KNN_TYPE" { YYSTOREBOUNDS; return TOK_KNN_TYPE; } "LIKE" { YYSTOREBOUNDS; return TOK_LIKE; } +"MAX_CHUNKS" { YYSTOREBOUNDS; return TOK_MAX_CHUNKS; } +"MAX_TOKENS" { YYSTOREBOUNDS; return TOK_MAX_TOKENS; } "MODEL_NAME" { YYSTOREBOUNDS; return TOK_MODEL_NAME; } "MODIFY" { YYSTOREBOUNDS; return TOK_MODIFY; } "MODIFY"[ \t\n\r]+"COLUMN" { return TOK_MODIFY_COLUMN; } @@ -99,6 +102,7 @@ "MVA64" { YYSTOREBOUNDS; return TOK_MVA64; } "NOT" { YYSTOREBOUNDS; return TOK_NOT; } "OPTION" { YYSTOREBOUNDS; return TOK_OPTION; } +"OVERLAP_TOKENS" { YYSTOREBOUNDS; return TOK_OVERLAP_TOKENS; } "PLUGIN" { YYSTOREBOUNDS; return TOK_PLUGIN; } "QUANTIZATION" { YYSTOREBOUNDS; return TOK_QUANTIZATION; } "REBUILD" { YYSTOREBOUNDS; return TOK_REBUILD; } diff --git a/src/ddl.y b/src/ddl.y index 5080aab15e..5df10f3728 100644 --- a/src/ddl.y +++ b/src/ddl.y @@ -34,6 +34,7 @@ %token TOK_BIT %token TOK_BOOL %token TOK_CACHE_PATH +%token TOK_CHUNK_STRATEGY %token TOK_CLUSTER %token TOK_COLUMN %token TOK_COLUMNAR @@ -65,6 +66,8 @@ %token TOK_KNN_DIMS %token TOK_KNN_TYPE %token TOK_LIKE +%token TOK_MAX_CHUNKS +%token TOK_MAX_TOKENS %token TOK_MODEL_NAME %token TOK_MODIFY %token TOK_MODIFY_COLUMN @@ -74,6 +77,7 @@ %token TOK_MVA64 "mva64" %token TOK_NOT %token TOK_OPTION +%token TOK_OVERLAP_TOKENS %token TOK_PLUGIN %token TOK_QUANTIZATION %token TOK_REBUILD @@ -399,6 +403,38 @@ item_option: YYERROR; } } + | TOK_CHUNK_STRATEGY '=' TOK_QUOTED_STRING + { + if ( !pParser->AddItemOptionChunkStrategy ( $3 ) ) + { + yyerror ( pParser, pParser->GetLastError() ); + YYERROR; + } + } + | TOK_MAX_TOKENS '=' TOK_QUOTED_STRING + { + if ( !pParser->AddItemOptionMaxTokens ( $3 ) ) + { + yyerror ( pParser, pParser->GetLastError() ); + YYERROR; + } + } + | TOK_OVERLAP_TOKENS '=' TOK_QUOTED_STRING + { + if ( !pParser->AddItemOptionOverlapTokens ( $3 ) ) + { + yyerror ( pParser, pParser->GetLastError() ); + YYERROR; + } + } + | TOK_MAX_CHUNKS '=' TOK_QUOTED_STRING + { + if ( !pParser->AddItemOptionMaxChunks ( $3 ) ) + { + yyerror ( pParser, pParser->GetLastError() ); + YYERROR; + } + } | TOK_API_KEY '=' TOK_QUOTED_STRING { if ( !pParser->AddItemOptionAPIKey ( $3 ) ) diff --git a/src/embeddingutils.cpp b/src/embeddingutils.cpp index 915895ed57..09cbde3b6f 100644 --- a/src/embeddingutils.cpp +++ b/src/embeddingutils.cpp @@ -89,7 +89,16 @@ bool ParseEmbeddingSources ( CSphVector> & dFrom, const CSph bool CheckAlterAddEmbedding ( bool bModify, const CSphString & sAttrToAdd, const SqlStmt_t & tStmt, const CSphSchema & tBaseSchema, CSphString & sError ) { - if ( bModify || tStmt.m_eAlterColType!=SPH_ATTR_FLOAT_VECTOR || !( tStmt.m_uAttrFlags & CSphColumnInfo::ATTR_INDEXED_KNN ) ) + if ( bModify || !( tStmt.m_uAttrFlags & CSphColumnInfo::ATTR_INDEXED_KNN ) ) + return true; + + if ( tStmt.m_eAlterColType==SPH_ATTR_FLOAT_VECTOR_ARRAY && !tStmt.m_tAlterKNNModel.m_sModelName.empty() ) + { + sError.SetSprintf ( "attribute '%s': adding a model-backed float_vector_array is not supported yet, since existing rows can not be embedded; create the column with the table instead", sAttrToAdd.cstr() ); + return false; + } + + if ( tStmt.m_eAlterColType!=SPH_ATTR_FLOAT_VECTOR ) return true; CreateTableAttr_t tCreateAttr; @@ -117,15 +126,18 @@ bool CheckAlterAddEmbedding ( bool bModify, const CSphString & sAttrToAdd, const return ParseEmbeddingSources ( dFrom, tStmt.m_sAlterKnnFrom, tProjectedSchema, sError ); } -bool ConvertEmbeddings ( knn::TextToEmbeddings_i * pModel, const CSphString & sAttrName, const CSphVector & dFromTexts, std::vector> & dEmbeddings, CSphString & sError ) + +bool ConvertEmbeddings ( knn::TextToEmbeddings_i * pModel, const CSphString & sAttrName, const CSphVector & dFromTexts, std::vector> & dEmbeddings, const knn::ChunkSettings_t * pChunk, CSphString & sError ) { std::vector dTexts; dTexts.reserve ( dFromTexts.GetLength() ); for ( const auto & sText : dFromTexts ) dTexts.push_back ( { sText.cstr(), (size_t)sText.Length() } ); + assert ( !pChunk || !knn::IsMultiVectorStrategy ( pChunk->m_eStrategy ) ); + std::string sErrStl; - if ( !pModel->Convert ( dTexts, dEmbeddings, sErrStl, GetEmbeddingsThreadsToUse() ) ) + if ( !pModel->Convert ( dTexts, dEmbeddings, sErrStl, GetEmbeddingsThreadsToUse(), pChunk ) ) { sError = sErrStl.c_str(); return false; diff --git a/src/embeddingutils.h b/src/embeddingutils.h index 7733fb16af..ac4e1c534f 100644 --- a/src/embeddingutils.h +++ b/src/embeddingutils.h @@ -23,7 +23,7 @@ namespace knn { class TextToEmbeddings_i; } bool ValidateSettingModel ( const CreateTableAttr_t & tAttr, CSphString & sError ); bool ParseEmbeddingSources ( CSphVector> & dFrom, const CSphString & sFrom, const ISphSchema & tSchema, CSphString & sError ); bool CheckAlterAddEmbedding ( bool bModify, const CSphString & sAttrToAdd, const SqlStmt_t & tStmt, const CSphSchema & tBaseSchema, CSphString & sError ); -bool ConvertEmbeddings ( knn::TextToEmbeddings_i * pModel, const CSphString & sAttrName, const CSphVector & dFromTexts, std::vector> & dEmbeddings, CSphString & sError ); +bool ConvertEmbeddings ( knn::TextToEmbeddings_i * pModel, const CSphString & sAttrName, const CSphVector & dFromTexts, std::vector> & dEmbeddings, const knn::ChunkSettings_t * pChunk, CSphString & sError ); AttrUpdateSharedPtr_t CreateFloatVectorAttrUpdate ( const CSphString & sAttrName, const CSphVector & dDocids, const std::vector> & dEmbeddings, int iEmptyVectorDims ); bool EmbeddingFromNeedsDocstore ( const VecTraits_T> & dFrom ); void GetEmbeddingColumnar ( const ISphSchema & tSchema, const VecTraits_T> & dFrom, columnar::Columnar_i * pColumnar, CSphVector & dIters ); diff --git a/src/indexsettings.cpp b/src/indexsettings.cpp index 91fdbce36e..05c127ee3e 100644 --- a/src/indexsettings.cpp +++ b/src/indexsettings.cpp @@ -1902,6 +1902,7 @@ bool IndexSettingsContainer_c::SetupKNNAttrs ( const CreateTableSettings_t & tCr (knn::ModelSettings_t&)tNamedKNN = i.m_tKNNModel; tNamedKNN.m_sName = i.m_tAttr.m_sName; tNamedKNN.m_sFrom = i.m_sKNNFrom; + tNamedKNN.m_tChunk = i.m_tKNNChunk; if ( !ValidateSettingModel ( i, m_sError ) ) return false; diff --git a/src/indexsettings.h b/src/indexsettings.h index 497be307e5..d9eafa3382 100644 --- a/src/indexsettings.h +++ b/src/indexsettings.h @@ -513,6 +513,7 @@ struct CreateTableAttr_t knn::IndexSettings_t m_tKNN; knn::ModelSettings_t m_tKNNModel; CSphString m_sKNNFrom; + knn::ChunkSettings_t m_tKNNChunk; }; struct NameValueStr_t diff --git a/src/knnmisc.cpp b/src/knnmisc.cpp index a002866699..f547c2e140 100644 --- a/src/knnmisc.cpp +++ b/src/knnmisc.cpp @@ -629,6 +629,38 @@ static const char * Quantization2Str ( knn::Quantization_e eQuant ) } +const char * ChunkStrategy2Str ( knn::ChunkStrategy_e eStrategy ) +{ + switch ( eStrategy ) + { + case knn::ChunkStrategy_e::TRUNCATE: return "truncate"; + case knn::ChunkStrategy_e::MEAN: return "mean"; + case knn::ChunkStrategy_e::FIXED: return "fixed"; + case knn::ChunkStrategy_e::RECURSIVE: return "recursive"; + case knn::ChunkStrategy_e::SENTENCE: return "sentence"; + default: return "unknown"; + } +} + + +bool Str2ChunkStrategy ( const CSphString & sStrategy, knn::ChunkStrategy_e & eStrategy, CSphString * pError ) +{ + CSphString sVal = sStrategy; + sVal.ToUpper(); + + if ( sVal=="TRUNCATE" ) { eStrategy = knn::ChunkStrategy_e::TRUNCATE; return true; } + if ( sVal=="MEAN" ) { eStrategy = knn::ChunkStrategy_e::MEAN; return true; } + if ( sVal=="FIXED" ) { eStrategy = knn::ChunkStrategy_e::FIXED; return true; } + if ( sVal=="RECURSIVE" ) { eStrategy = knn::ChunkStrategy_e::RECURSIVE; return true; } + if ( sVal=="SENTENCE" ) { eStrategy = knn::ChunkStrategy_e::SENTENCE; return true; } + + if ( pError ) + pError->SetSprintf ( "unknown chunk_strategy '%s'; expected truncate, mean, fixed, recursive or sentence", sStrategy.cstr() ); + + return false; +} + + bool Str2HNSWSimilarity ( const CSphString & sSimilarity, knn::HNSWSimilarity_e & eSimilarity, CSphString * pError ) { CSphString sSim = sSimilarity; @@ -745,11 +777,33 @@ void AddKNNSettings ( StringBuilder_c & sRes, const CSphColumnInfo & tAttr ) if ( tKNN.m_eQuantization!=tDefault.m_eQuantization ) sRes << " quantization='" << Quantization2Str ( tKNN.m_eQuantization ) << "'"; + + { + const auto & tChunk = tAttr.m_tKNNChunk; + knn::ChunkSettings_t tDefaultChunk; + + if ( tChunk.m_eStrategy!=tDefaultChunk.m_eStrategy ) + sRes << " chunk_strategy='" << ChunkStrategy2Str ( tChunk.m_eStrategy ) << "'"; + + if ( tChunk.m_uMaxTokens!=tDefaultChunk.m_uMaxTokens ) + sRes << " max_tokens='" << tChunk.m_uMaxTokens << "'"; + + if ( tChunk.m_uOverlapTokens!=tDefaultChunk.m_uOverlapTokens ) + sRes << " overlap_tokens='" << tChunk.m_uOverlapTokens << "'"; + + if ( tChunk.m_uMaxChunks!=tDefaultChunk.m_uMaxChunks ) + sRes << " max_chunks='" << tChunk.m_uMaxChunks << "'"; + } } -void ReadKNNJson ( bson::Bson_c tRoot, knn::IndexSettings_t & tIS, knn::ModelSettings_t & tMS, CSphString & sKNNFrom ) +void ReadKNNJson ( bson::Bson_c tRoot, knn::IndexSettings_t & tIS, knn::ModelSettings_t & tMS, CSphString & sKNNFrom, knn::ChunkSettings_t & tChunk ) { + Str2ChunkStrategy ( bson::String ( tRoot.ChildByName ( "chunk_strategy" ) ), tChunk.m_eStrategy ); + tChunk.m_uMaxTokens = (uint32_t) bson::Int ( tRoot.ChildByName ( "max_tokens" ), tChunk.m_uMaxTokens ); + tChunk.m_uOverlapTokens = (uint32_t) bson::Int ( tRoot.ChildByName ( "overlap_tokens" ), tChunk.m_uOverlapTokens ); + tChunk.m_uMaxChunks = (uint32_t) bson::Int ( tRoot.ChildByName ( "max_chunks" ), tChunk.m_uMaxChunks ); + tIS.m_iDims = (int) bson::Int ( tRoot.ChildByName ( "knn_dims" ) ); Str2HNSWSimilarity ( bson::String ( tRoot.ChildByName ( "hnsw_similarity" ) ), tIS.m_eHNSWSimilarity ); tIS.m_iHNSWM = (int) bson::Int ( tRoot.ChildByName ( "hnsw_m" ), tIS.m_iHNSWM ); @@ -766,7 +820,7 @@ void ReadKNNJson ( bson::Bson_c tRoot, knn::IndexSettings_t & tIS, knn::ModelSet } -void FormatKNNSettings ( JsonEscapedBuilder & tOut, const knn::IndexSettings_t & tIS, const knn::ModelSettings_t & tMS, const CSphString & sKNNFrom ) +void FormatKNNSettings ( JsonEscapedBuilder & tOut, const knn::IndexSettings_t & tIS, const knn::ModelSettings_t & tMS, const CSphString & sKNNFrom, const knn::ChunkSettings_t & tChunk ) { auto _ = tOut.Object(); @@ -792,6 +846,11 @@ void FormatKNNSettings ( JsonEscapedBuilder & tOut, const knn::IndexSettings_t & tOut.NamedVal ( "api_timeout", tMS.m_iAPITimeout ); tOut.NamedVal ( "use_gpu", tMS.m_bUseGPU ); } + + tOut.NamedString ( "chunk_strategy", ChunkStrategy2Str ( tChunk.m_eStrategy ) ); + tOut.NamedVal ( "max_tokens", tChunk.m_uMaxTokens ); + tOut.NamedVal ( "overlap_tokens", tChunk.m_uOverlapTokens ); + tOut.NamedVal ( "max_chunks", tChunk.m_uMaxChunks ); } @@ -825,6 +884,11 @@ CSphString FormatKNNConfigStr ( const CSphVector & dAttrs ) tObj.AddBool ( "use_gpu", i.m_bUseGPU ); } + tObj.AddStr ( "chunk_strategy", ChunkStrategy2Str ( i.m_tChunk.m_eStrategy ) ); + tObj.AddInt ( "max_tokens", i.m_tChunk.m_uMaxTokens ); + tObj.AddInt ( "overlap_tokens", i.m_tChunk.m_uOverlapTokens ); + tObj.AddInt ( "max_chunks", i.m_tChunk.m_uMaxChunks ); + tArray.AddItem(tObj); } @@ -896,6 +960,22 @@ bool ParseKNNConfigStr ( const CSphString & sStr, CSphVector return false; } + JsonObj_c tChunkStrategy = i.GetStrItem ( "chunk_strategy", sError, true ); + if ( !sError.IsEmpty() ) + return false; + + if ( tChunkStrategy && !Str2ChunkStrategy ( tChunkStrategy.StrVal(), tParsed.m_tChunk.m_eStrategy, &sError ) ) + return false; + + int iMaxTokens = 0, iOverlapTokens = 0, iMaxChunks = 0; + if ( !i.FetchIntItem ( iMaxTokens, "max_tokens", sError, true ) ) return false; + if ( !i.FetchIntItem ( iOverlapTokens, "overlap_tokens", sError, true ) ) return false; + if ( !i.FetchIntItem ( iMaxChunks, "max_chunks", sError, true ) ) return false; + + tParsed.m_tChunk.m_uMaxTokens = (uint32_t)iMaxTokens; + tParsed.m_tChunk.m_uOverlapTokens = (uint32_t)iOverlapTokens; + tParsed.m_tChunk.m_uMaxChunks = (uint32_t)iMaxChunks; + if ( !tParsed.m_sModelName.empty() ) { if ( !i.FetchStrItem ( tParsed.m_sFrom, "from", sError, true ) ) return false; diff --git a/src/knnmisc.h b/src/knnmisc.h index 84b035cb32..56927f4411 100644 --- a/src/knnmisc.h +++ b/src/knnmisc.h @@ -64,12 +64,14 @@ ISphExpr * CreateExpr_KNNDist ( const CSphVector & dAnchor, const CS void NormalizeVec ( VecTraits_T & dData ); void AddKNNSettings ( StringBuilder_c & sRes, const CSphColumnInfo & tAttr ); -void ReadKNNJson ( bson::Bson_c tRoot, knn::IndexSettings_t & tIS, knn::ModelSettings_t & tMS, CSphString & sKNNFrom ); +void ReadKNNJson ( bson::Bson_c tRoot, knn::IndexSettings_t & tIS, knn::ModelSettings_t & tMS, CSphString & sKNNFrom, knn::ChunkSettings_t & tChunk ); CSphString FormatKNNConfigStr ( const CSphVector & dAttrs ); bool ParseKNNConfigStr ( const CSphString & sStr, CSphVector & dParsed, CSphString & sError ); -void FormatKNNSettings ( JsonEscapedBuilder & tOut, const knn::IndexSettings_t & tIndexSettings, const knn::ModelSettings_t & tModelSettings, const CSphString & sKNNFrom ); +void FormatKNNSettings ( JsonEscapedBuilder & tOut, const knn::IndexSettings_t & tIndexSettings, const knn::ModelSettings_t & tModelSettings, const CSphString & sKNNFrom, const knn::ChunkSettings_t & tChunk ); bool Str2HNSWSimilarity ( const CSphString & sSimilarity, knn::HNSWSimilarity_e & eSimilarity, CSphString * pError = nullptr ); +bool Str2ChunkStrategy ( const CSphString & sStrategy, knn::ChunkStrategy_e & eStrategy, CSphString * pError = nullptr ); +const char * ChunkStrategy2Str ( knn::ChunkStrategy_e eStrategy ); bool Str2Quantization ( const CSphString & sQuantization, knn::Quantization_e & eQuantization, CSphString * pError = nullptr ); int GetDefaultKNNParallelBuild ( int iThreads ); diff --git a/src/schema/columninfo.h b/src/schema/columninfo.h index 1e5c518656..f421c0dd29 100755 --- a/src/schema/columninfo.h +++ b/src/schema/columninfo.h @@ -27,6 +27,7 @@ struct NamedKNNSettings_t : public knn::IndexSettings_t, public knn::ModelSettin { CSphString m_sName; CSphString m_sFrom; + knn::ChunkSettings_t m_tChunk; }; /// source column info @@ -79,6 +80,7 @@ struct CSphColumnInfo knn::IndexSettings_t m_tKNN; ///< knn index settings knn::ModelSettings_t m_tKNNModel; ///< knn model settings CSphString m_sKNNFrom; ///< fields/attrs used by the model + knn::ChunkSettings_t m_tKNNChunk; ///< how the model splits a document into vectors float m_fTdigestCompression = 200.0f; ///< tdigest compression for extended aggs AggrSettings_t m_tAggrSettings; ///< full settings payload for extended aggs diff --git a/src/schema/schema.cpp b/src/schema/schema.cpp index 82abc776f3..51b29b2c01 100644 --- a/src/schema/schema.cpp +++ b/src/schema/schema.cpp @@ -584,6 +584,7 @@ void CSphSchema::SetupKNNFlags ( const CSphSourceSettings & tSettings ) // Without the cast, the compiler might copy the wrong memory region or fail to compile due to ambiguity. tAttr.m_tKNNModel = static_cast(tKNN); tAttr.m_sKNNFrom = tKNN.m_sFrom; + tAttr.m_tKNNChunk = tKNN.m_tChunk; } } diff --git a/src/searchdddl.cpp b/src/searchdddl.cpp index 2a8421acd7..5e1b4f8102 100644 --- a/src/searchdddl.cpp +++ b/src/searchdddl.cpp @@ -13,6 +13,7 @@ #include "searchdddl.h" #include "knnmisc.h" +#include "conversion.h" class DdlParser_c : public SqlParserTraits_c @@ -53,10 +54,22 @@ class DdlParser_c : public SqlParserTraits_c bool m_bKNNFromSet = false; bool m_bUseGPU = false; + knn::ChunkStrategy_e m_eChunkStrategy = knn::ChunkStrategy_e::TRUNCATE; + uint32_t m_uMaxTokens = 0; + uint32_t m_uOverlapTokens = 0; + uint32_t m_uMaxChunks = 0; + bool m_bChunkStrategySpecified = false; + bool m_bMaxTokensSpecified = false; + bool m_bOverlapTokensSpecified = false; + bool m_bMaxChunksSpecified = false; + + bool HasAnyChunkOption() const { return m_bChunkStrategySpecified || m_bMaxTokensSpecified || m_bOverlapTokensSpecified || m_bMaxChunksSpecified; } + void Reset() { *this = ItemOptions_t(); } DWORD ToFlags() const; knn::IndexSettings_t ToKNN() const; knn::ModelSettings_t ToKNNModel() const; + knn::ChunkSettings_t ToKNNChunk() const; void CopyOptionsTo ( CreateTableAttr_t & tAttr ) const; }; @@ -81,6 +94,10 @@ class DdlParser_c : public SqlParserTraits_c bool AddItemOptionHNSWEfConstruction ( const SqlNode_t & tOption ); bool AddItemOptionModelName ( const SqlNode_t & tOption ); bool AddItemOptionFrom ( const SqlNode_t & tOption ); + bool AddItemOptionChunkStrategy ( const SqlNode_t & tOption ); + bool AddItemOptionMaxTokens ( const SqlNode_t & tOption ); + bool AddItemOptionOverlapTokens ( const SqlNode_t & tOption ); + bool AddItemOptionMaxChunks ( const SqlNode_t & tOption ); bool AddItemOptionCachePath ( const SqlNode_t & tOption ); bool AddItemOptionAPIKey ( const SqlNode_t & tOption ); bool AddItemOptionAPIUrl ( const SqlNode_t & tOption ); @@ -105,6 +122,7 @@ class DdlParser_c : public SqlParserTraits_c void AddField ( const CSphString & sName, DWORD uFlags ); bool ConvertToAttrEngine ( const SqlNode_t & tEngine, AttrEngine_e & eEngine ); static bool CheckFieldFlags ( ESphAttr eAttrType, int iFlags, const CSphString & sName, const ItemOptions_t & tOpts, CSphString & sError ); + static bool CheckChunkOptions ( ESphAttr eAttrType, const ItemOptions_t & tOpts, CSphString & sError ); }; using YYSTYPE = SqlNode_t; @@ -200,6 +218,19 @@ knn::ModelSettings_t DdlParser_c::ItemOptions_t::ToKNNModel() const } +knn::ChunkSettings_t DdlParser_c::ItemOptions_t::ToKNNChunk() const +{ + knn::ChunkSettings_t tChunk; + + tChunk.m_eStrategy = m_eChunkStrategy; + tChunk.m_uMaxTokens = m_uMaxTokens; + tChunk.m_uOverlapTokens = m_uOverlapTokens; + tChunk.m_uMaxChunks = m_uMaxChunks; + + return tChunk; +} + + void DdlParser_c::ItemOptions_t::CopyOptionsTo ( CreateTableAttr_t & tAttr ) const { tAttr.m_tAttr.m_eEngine = m_eEngine; @@ -257,6 +288,49 @@ static DWORD ConvertFlags ( int iFlags ) } +bool DdlParser_c::CheckChunkOptions ( ESphAttr eAttrType, const ItemOptions_t & tOpts, CSphString & sError ) +{ + if ( tOpts.m_sKNNType.IsEmpty() ) + { + sError = "chunk_strategy, max_tokens, overlap_tokens and max_chunks require knn_type='hnsw'"; + return false; + } + + if ( tOpts.m_sModelName.IsEmpty() ) + { + sError = "chunk_strategy, max_tokens, overlap_tokens and max_chunks require model_name; explicitly supplied vectors are never chunked"; + return false; + } + + if ( !tOpts.m_bChunkStrategySpecified ) + { + sError = "max_tokens, overlap_tokens and max_chunks require chunk_strategy"; + return false; + } + + const bool bOtherOptions = tOpts.m_bMaxTokensSpecified || tOpts.m_bOverlapTokensSpecified || tOpts.m_bMaxChunksSpecified; + if ( tOpts.m_eChunkStrategy==knn::ChunkStrategy_e::TRUNCATE && bOtherOptions ) + { + sError = "chunk_strategy='truncate' ignores max_tokens, overlap_tokens and max_chunks"; + return false; + } + + if ( knn::IsMultiVectorStrategy ( tOpts.m_eChunkStrategy ) && eAttrType!=SPH_ATTR_FLOAT_VECTOR_ARRAY ) + { + sError.SetSprintf ( "chunk_strategy='%s' produces several vectors per document and requires a float_vector_array attribute", ChunkStrategy2Str ( tOpts.m_eChunkStrategy ) ); + return false; + } + + if ( tOpts.m_bOverlapTokensSpecified && ( !tOpts.m_bMaxTokensSpecified || !tOpts.m_uMaxTokens ) ) + { + sError = "overlap_tokens requires an explicit non-zero max_tokens"; + return false; + } + + return true; +} + + bool DdlParser_c::CheckFieldFlags ( ESphAttr eAttrType, int iFlags, const CSphString & sName, const ItemOptions_t & tOpts, CSphString & sError ) { if ( eAttrType!=SPH_ATTR_FLOAT_VECTOR && eAttrType!=SPH_ATTR_FLOAT_VECTOR_ARRAY && !tOpts.m_sKNNType.IsEmpty() ) @@ -265,6 +339,12 @@ bool DdlParser_c::CheckFieldFlags ( ESphAttr eAttrType, int iFlags, const CSphSt return false; } + if ( eAttrType!=SPH_ATTR_FLOAT_VECTOR && eAttrType!=SPH_ATTR_FLOAT_VECTOR_ARRAY && tOpts.HasAnyChunkOption() ) + { + sError = "chunk_strategy, max_tokens, overlap_tokens and max_chunks can only be used with float_vector and float_vector_array attributes"; + return false; + } + if ( eAttrType==SPH_ATTR_STRING ) { if ( ( iFlags & FLAG_ATTRIBUTE ) && ( iFlags & FLAG_STORED ) ) @@ -275,12 +355,8 @@ bool DdlParser_c::CheckFieldFlags ( ESphAttr eAttrType, int iFlags, const CSphSt } else if ( eAttrType==SPH_ATTR_FLOAT_VECTOR || eAttrType==SPH_ATTR_FLOAT_VECTOR_ARRAY ) { - // auto-embeddings produce exactly one vector per document, so they are meaningless for an array column - if ( eAttrType==SPH_ATTR_FLOAT_VECTOR_ARRAY && !tOpts.m_sModelName.IsEmpty() ) - { - sError = "model_name can't be used with float_vector_array attributes"; + if ( tOpts.HasAnyChunkOption() && !CheckChunkOptions ( eAttrType, tOpts, sError ) ) return false; - } if ( !tOpts.m_sKNNType.IsEmpty() ) { @@ -339,6 +415,7 @@ bool DdlParser_c::SetupAlterTable ( const SqlNode_t & tAttr, ESphAttr eAttr, int m_pStmt->m_tAlterKNN = m_tItemOptions.ToKNN(); m_pStmt->m_tAlterKNNModel = m_tItemOptions.ToKNNModel(); m_pStmt->m_sAlterKnnFrom = m_tItemOptions.m_sFrom; + m_pStmt->m_tAlterKNNChunk = m_tItemOptions.ToKNNChunk(); m_pStmt->m_bAlterKnnFromSet = m_tItemOptions.m_bKNNFromSet; bool bOk = CheckFieldFlags ( m_pStmt->m_eAlterColType, iFieldFlags, m_pStmt->m_sAlterAttr, m_tItemOptions, m_sError ); @@ -385,6 +462,7 @@ bool DdlParser_c::AddCreateTableCol ( const SqlNode_t & tName, const SqlNode_t & tAttr.m_bKNN = !tOpts.m_sKNNType.IsEmpty(); tAttr.m_tKNN = tOpts.ToKNN(); tAttr.m_tKNNModel = tOpts.ToKNNModel(); + tAttr.m_tKNNChunk = tOpts.ToKNNChunk(); tAttr.m_sKNNFrom = tOpts.m_sFrom; tAttr.m_bKNNFromSet = tOpts.m_bKNNFromSet; @@ -596,6 +674,63 @@ bool DdlParser_c::AddItemOptionFrom ( const SqlNode_t & tOption ) } +static bool ParseChunkUint ( const CSphString & sValue, const char * szOption, uint32_t & uRes, CSphString & sError ) +{ + int64_t iVal = sphToInt64 ( sValue.cstr(), &sError ); + if ( !sError.IsEmpty() ) + return false; + + if ( iVal<0 || iVal>INT_MAX ) + { + sError.SetSprintf ( "%s: '%s' is out of range (0..%d)", szOption, sValue.cstr(), INT_MAX ); + return false; + } + + uRes = (uint32_t)iVal; + return true; +} + + +bool DdlParser_c::AddItemOptionChunkStrategy ( const SqlNode_t & tOption ) +{ + if ( !Str2ChunkStrategy ( ToStringUnescape(tOption), m_tItemOptions.m_eChunkStrategy, &m_sError ) ) + return false; + + m_tItemOptions.m_bChunkStrategySpecified = true; + return true; +} + + +bool DdlParser_c::AddItemOptionMaxTokens ( const SqlNode_t & tOption ) +{ + if ( !ParseChunkUint ( ToStringUnescape(tOption), "max_tokens", m_tItemOptions.m_uMaxTokens, m_sError ) ) + return false; + + m_tItemOptions.m_bMaxTokensSpecified = true; + return true; +} + + +bool DdlParser_c::AddItemOptionOverlapTokens ( const SqlNode_t & tOption ) +{ + if ( !ParseChunkUint ( ToStringUnescape(tOption), "overlap_tokens", m_tItemOptions.m_uOverlapTokens, m_sError ) ) + return false; + + m_tItemOptions.m_bOverlapTokensSpecified = true; + return true; +} + + +bool DdlParser_c::AddItemOptionMaxChunks ( const SqlNode_t & tOption ) +{ + if ( !ParseChunkUint ( ToStringUnescape(tOption), "max_chunks", m_tItemOptions.m_uMaxChunks, m_sError ) ) + return false; + + m_tItemOptions.m_bMaxChunksSpecified = true; + return true; +} + + bool DdlParser_c::AddItemOptionAPIKey ( const SqlNode_t & tOption ) { m_tItemOptions.m_sAPIKey = ToStringUnescape(tOption); diff --git a/src/searchdsql.h b/src/searchdsql.h index 62c5b20c76..2558464a8a 100644 --- a/src/searchdsql.h +++ b/src/searchdsql.h @@ -332,6 +332,7 @@ struct SqlStmt_t knn::ModelSettings_t m_tAlterKNNModel; CSphString m_sAlterKnnFrom; bool m_bAlterKnnFromSet = false; + knn::ChunkSettings_t m_tAlterKNNChunk; // CREATE TABLE specific CreateTableSettings_t m_tCreateTable; diff --git a/src/sphinx.cpp b/src/sphinx.cpp index 9bb6dcd984..2620d4c76d 100755 --- a/src/sphinx.cpp +++ b/src/sphinx.cpp @@ -4258,7 +4258,7 @@ static void ReadSchemaColumnJson ( bson::Bson_c tNode, CSphColumnInfo & tCol ) NodeHandle_t tKNN = tNode.ChildByName ("knn"); if ( tKNN!=nullnode ) - ReadKNNJson ( tKNN, tCol.m_tKNN, tCol.m_tKNNModel, tCol.m_sKNNFrom ); + ReadKNNJson ( tKNN, tCol.m_tKNN, tCol.m_tKNNModel, tCol.m_sKNNFrom, tCol.m_tKNNChunk ); } @@ -4355,7 +4355,7 @@ void DumpAttrToJson ( JsonEscapedBuilder& tOut, const CSphColumnInfo& tCol ) if ( tCol.IsIndexedKNN() ) { tOut.Named ( "knn" ); - FormatKNNSettings ( tOut, tCol.m_tKNN, tCol.m_tKNNModel, tCol.m_sKNNFrom ); + FormatKNNSettings ( tOut, tCol.m_tKNN, tCol.m_tKNNModel, tCol.m_sKNNFrom, tCol.m_tKNNChunk ); } } } // namespace diff --git a/src/sphinx_alter.cpp b/src/sphinx_alter.cpp index 57ae3263a8..7f1f1fa1dd 100644 --- a/src/sphinx_alter.cpp +++ b/src/sphinx_alter.cpp @@ -85,6 +85,7 @@ void AddToSchema ( CSphSchema & tSchema, const AttrAddRemoveCtx_t & tCtx, CSphSt tInfo.m_tKNN = tCtx.m_tKNN; tInfo.m_tKNNModel = tCtx.m_tKNNModel; tInfo.m_sKNNFrom = tCtx.m_sKNNFrom; + tInfo.m_tKNNChunk = tCtx.m_tKNNChunk; auto iIdxExisting = tSchema.GetAttrIndex ( tCtx.m_sName.cstr() ); if ( iIdxExisting >= 0 ) diff --git a/src/sphinx_alter.h b/src/sphinx_alter.h index 8cde1b94ff..8471095569 100644 --- a/src/sphinx_alter.h +++ b/src/sphinx_alter.h @@ -46,6 +46,7 @@ struct AttrAddRemoveCtx_t knn::IndexSettings_t m_tKNN; knn::ModelSettings_t m_tKNNModel; CSphString m_sKNNFrom; + knn::ChunkSettings_t m_tKNNChunk; }; // common add/remove attribute/field code for both RT and plain indexes diff --git a/src/sphinxrt.cpp b/src/sphinxrt.cpp index cfe869ba39..2c69aa8850 100644 --- a/src/sphinxrt.cpp +++ b/src/sphinxrt.cpp @@ -9993,6 +9993,7 @@ bool RtIndex_c::AddRemoveAttribute ( bool bAdd, const AttrAddRemoveCtx_t & tCtx, tNewCtx.m_tKNN = pAttr->m_tKNN; tNewCtx.m_tKNNModel = pAttr->m_tKNNModel; tNewCtx.m_sKNNFrom = pAttr->m_sKNNFrom; + tNewCtx.m_tKNNChunk = pAttr->m_tKNNChunk; } m_tSchema = tNewSchema; @@ -13171,6 +13172,12 @@ bool RtIndex_c::InitUpdateEmbeddingState ( const CSphString & sAttr, EmbeddingPo } const CSphColumnInfo & tAttr = m_tSchema.GetAttr ( iAttrIdx ); + if ( tAttr.m_eAttrType==SPH_ATTR_FLOAT_VECTOR_ARRAY ) + { + sError.SetSprintf ( "attribute '%s' is a float_vector_array; rebuilding embeddings is not supported for it yet, use REPLACE", sAttr.cstr() ); + return false; + } + if ( tAttr.m_eAttrType!=SPH_ATTR_FLOAT_VECTOR || !tAttr.IsIndexedKNN() ) { sError.SetSprintf ( "attribute '%s' is not indexed float_vector", sAttr.cstr() ); @@ -13307,8 +13314,10 @@ bool RtIndex_c::GetUpdateEmbedding ( ExtUpdState_t & tState, AttrUpdateSharedPtr if ( dDocids.IsEmpty() ) return true; + const CSphColumnInfo & tEmbAttr = m_tSchema.GetAttr ( tState.m_iAttrIdx ); // use the column's persisted chunking settings + std::vector> dEmbeddings; - if ( !ConvertEmbeddings ( tAttrWithModel.m_pModel, tState.m_sAttr, dFromTexts, dEmbeddings, sError ) ) + if ( !ConvertEmbeddings ( tAttrWithModel.m_pModel, tState.m_sAttr, dFromTexts, dEmbeddings, &tEmbAttr.m_tKNNChunk, sError ) ) return false; pUpdate = CreateFloatVectorAttrUpdate ( tState.m_sAttr, dDocids, dEmbeddings, tState.m_iDims ); diff --git a/test/test_524/model.bin b/test/test_524/model.bin index ab845a3eda..352d77f223 100644 --- a/test/test_524/model.bin +++ b/test/test_524/model.bin @@ -6,4 +6,4 @@ v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='L2' id bigint, title text, v float_vector_array -)";}}}i:7;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:8;a:3:{s:8:"sphinxql";s:117:"create table t (title text, v float_vector_array knn_type='hnsw' model_name='sentence-transformers/all-MiniLM-L6-v2')";s:5:"errno";i:1064;s:5:"error";s:73:"P03: model_name can't be used with float_vector_array attributes near ')'";}i:9;a:3:{s:8:"sphinxql";s:84:"create table t (title text, v int knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:5:"errno";i:1064;s:5:"error";s:98:"P03: knn_type='hnsw' can only be used with float_vector and float_vector_array attributes near ')'";}i:10;a:2:{s:8:"sphinxql";s:99:"create table t (title text, v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:11;a:2:{s:8:"sphinxql";s:56:"insert into t values (1, 'multi', [[1,0,0,0],[0,1,0,0]])";s:14:"total_affected";i:1;}i:12;a:2:{s:8:"sphinxql";s:47:"insert into t values (2, 'single', [[0,0,1,0]])";s:14:"total_affected";i:1;}i:13;a:2:{s:8:"sphinxql";s:37:"insert into t values (3, 'empty', [])";s:14:"total_affected";i:1;}i:14;a:2:{s:8:"sphinxql";s:46:"insert into t (id,title) values (4, 'omitted')";s:14:"total_affected";i:1;}i:15;a:3:{s:8:"sphinxql";s:31:"select * from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:3:{s:2:"id";s:1:"1";s:5:"title";s:5:"multi";s:1:"v";s:77:"[[1.000000,0.000000,0.000000,0.000000],[0.000000,1.000000,0.000000,0.000000]]";}i:1;a:3:{s:2:"id";s:1:"2";s:5:"title";s:6:"single";s:1:"v";s:39:"[[0.000000,0.000000,1.000000,0.000000]]";}i:2;a:3:{s:2:"id";s:1:"3";s:5:"title";s:5:"empty";s:1:"v";s:2:"[]";}i:3;a:3:{s:2:"id";s:1:"4";s:5:"title";s:7:"omitted";s:1:"v";s:2:"[]";}}}i:16;a:3:{s:8:"sphinxql";s:43:"insert into t values (5, 'flat', (1,0,0,0))";s:5:"errno";i:1064;s:5:"error";s:86:"column 2 at row 0: float_vector_array requires an array of vectors, e.g. [[1,2],[3,4]]";}i:17;a:3:{s:8:"sphinxql";s:45:"insert into t values (5, 'inner empty', [[]])";s:5:"errno";i:1064;s:5:"error";s:75:"column 2 at row 0: vector #1 is empty; vectors must have at least one entry";}i:18;a:3:{s:8:"sphinxql";s:50:"insert into t values (5, 'inner empty 2', [[],[]])";s:5:"errno";i:1064;s:5:"error";s:75:"column 2 at row 0: vector #1 is empty; vectors must have at least one entry";}i:19;a:3:{s:8:"sphinxql";s:55:"insert into t values (5, 'ragged', [[1,2,3,4],[5,6,7]])";s:5:"errno";i:1064;s:5:"error";s:54:"column 2 at row 0: vector #2 has 3 entries, expected 4";}i:20;a:3:{s:8:"sphinxql";s:53:"insert into t values (5, 'wrong dims', [[1,2],[3,4]])";s:5:"errno";i:1064;s:5:"error";s:58:"KNN error: vectors have 2 values, index 'v' needs 4 values";}i:21;a:3:{s:8:"sphinxql";s:39:"select id, title from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:5:"title";s:5:"multi";}i:1;a:2:{s:2:"id";s:1:"2";s:5:"title";s:6:"single";}i:2;a:2:{s:2:"id";s:1:"3";s:5:"title";s:5:"empty";}i:3;a:2:{s:2:"id";s:1:"4";s:5:"title";s:7:"omitted";}}}i:22;a:2:{s:8:"sphinxql";s:55:"insert into t values (5, 'ints', [[1,0,0,0],[0,1,0,0]])";s:14:"total_affected";i:1;}i:23;a:3:{s:8:"sphinxql";s:30:"select id, v from t where id=5";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"5";s:1:"v";s:77:"[[1.000000,0.000000,0.000000,0.000000],[0.000000,1.000000,0.000000,0.000000]]";}}}i:24;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:25;a:3:{s:8:"sphinxql";s:82:"select id, knn_dist() from t where knn(v,10,(1,0,0,0),{rescore=1}) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:26;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:27;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:28;a:3:{s:8:"sphinxql";s:82:"select id, knn_dist() from t where knn(v,10,(1,0,0,0),{rescore=1}) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:29;a:3:{s:8:"sphinxql";s:41:"select id from t where knn(v,1,(1,0,0,0))";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"5";}i:2;a:1:{s:2:"id";s:1:"2";}}}i:30;a:3:{s:8:"sphinxql";s:57:"select id from t where knn(v,2,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"5";}i:2;a:1:{s:2:"id";s:1:"2";}}}i:31;a:3:{s:8:"sphinxql";s:82:"select id, count(*) c from t where knn(v,10,(1,0,0,0)) group by id order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"c";s:1:"1";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"c";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"5";s:1:"c";s:1:"1";}}}i:32;a:3:{s:8:"sphinxql";s:40:"select id from t where knn(v,10,(1,0,0))";s:5:"errno";i:1064;s:5:"error";s:74:"table t: KNN index 'v' requires a vector of 4 entries; 3 entries specified";}i:33;a:3:{s:8:"sphinxql";s:80:"select id, knn_dist() from t where id!=1 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:34;a:3:{s:8:"sphinxql";s:101:"select id, knn_dist() from t where id!=1 and @knn_dist < 1e38 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:35;a:3:{s:8:"sphinxql";s:95:"select id, knn_dist() d from t where id!=1 and d < 1e38 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"5";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"2";}}}i:36;a:3:{s:8:"sphinxql";s:112:"select id, knn_dist() d from t where (id=2 or id=3 or id=4) and d < 1e38 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"2";}}}i:37;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:38;a:2:{s:8:"sphinxql";s:108:"create table t (title text, v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', tag int)";s:14:"total_affected";i:0;}i:39;a:2:{s:8:"sphinxql";s:42:"select id from t where knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:40;a:2:{s:8:"sphinxql";s:52:"select id from t where tag=1 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:41;a:2:{s:8:"sphinxql";s:36:"insert into t values (1, 'a', [], 1)";s:14:"total_affected";i:1;}i:42;a:2:{s:8:"sphinxql";s:36:"insert into t values (2, 'b', [], 2)";s:14:"total_affected";i:1;}i:43;a:2:{s:8:"sphinxql";s:42:"select id from t where knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:44;a:2:{s:8:"sphinxql";s:52:"select id from t where tag=1 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:45;a:2:{s:8:"sphinxql";s:52:"select id from t where tag<9 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:46;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:47;a:2:{s:8:"sphinxql";s:42:"select id from t where knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:48;a:2:{s:8:"sphinxql";s:52:"select id from t where tag=1 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:49;a:2:{s:8:"sphinxql";s:52:"select id from t where tag<9 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:50;a:3:{s:8:"sphinxql";s:31:"select * from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:4:{s:2:"id";s:1:"1";s:5:"title";s:1:"a";s:1:"v";s:2:"[]";s:3:"tag";s:1:"1";}i:1;a:4:{s:2:"id";s:1:"2";s:5:"title";s:1:"b";s:1:"v";s:2:"[]";s:3:"tag";s:1:"2";}}}i:51;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:52;a:2:{s:8:"sphinxql";s:96:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', tag int)";s:14:"total_affected";i:0;}i:53;a:2:{s:8:"sphinxql";s:106:"insert into t values (1, [[1,0,0,0]], 1), (2, [], 1), (3, [[0,1,0,0]], 1), (4, [], 1), (5, [[0,0,1,0]], 1)";s:14:"total_affected";i:5;}i:54;a:3:{s:8:"sphinxql";s:58:"select id from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:55;a:3:{s:8:"sphinxql";s:68:"select id from t where tag=1 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:56;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:57;a:3:{s:8:"sphinxql";s:58:"select id from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:58;a:3:{s:8:"sphinxql";s:68:"select id from t where tag=1 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:59;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:60;a:2:{s:8:"sphinxql";s:87:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:61;a:2:{s:8:"sphinxql";s:66:"insert into t values (1, [[0.1,-0.2,0.3,-0.4],[0.9,0.8,-0.7,0.6]])";s:14:"total_affected";i:1;}i:62;a:2:{s:8:"sphinxql";s:47:"insert into t values (2, [[-0.5,0.5,-0.5,0.5]])";s:14:"total_affected";i:1;}i:63;a:2:{s:8:"sphinxql";s:51:"insert into t values (3, [[0.11,-0.21,0.31,-0.41]])";s:14:"total_affected";i:1;}i:64;a:2:{s:8:"sphinxql";s:55:"insert into t values (4, [[1,-0.5,0,0.25],[-1,2,-3,4]])";s:14:"total_affected";i:1;}i:65;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:80:"[[0.100000,-0.200000,0.300000,-0.400000],[0.900000,0.800000,-0.700000,0.600000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:41:"[[-0.500000,0.500000,-0.500000,0.500000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:41:"[[0.110000,-0.210000,0.310000,-0.410000]]";}i:3;a:2:{s:2:"id";s:1:"4";s:1:"v";s:80:"[[1.000000,-0.500000,0.000000,0.250000],[-1.000000,2.000000,-3.000000,4.000000]]";}}}i:66;a:3:{s:8:"sphinxql";s:81:"select id, knn_dist() d from t where knn(v,10,(0.1,-0.2,0.3,-0.4)) order by d asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"3";s:1:"d";s:6:"0.0004";}i:2;a:2:{s:2:"id";s:1:"4";s:1:"d";s:6:"1.4125";}i:3;a:2:{s:2:"id";s:1:"2";s:1:"d";s:3:"2.3";}}}i:67;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:68;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:80:"[[0.100000,-0.200000,0.300000,-0.400000],[0.900000,0.800000,-0.700000,0.600000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:41:"[[-0.500000,0.500000,-0.500000,0.500000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:41:"[[0.110000,-0.210000,0.310000,-0.410000]]";}i:3;a:2:{s:2:"id";s:1:"4";s:1:"v";s:80:"[[1.000000,-0.500000,0.000000,0.250000],[-1.000000,2.000000,-3.000000,4.000000]]";}}}i:69;a:3:{s:8:"sphinxql";s:81:"select id, knn_dist() d from t where knn(v,10,(0.1,-0.2,0.3,-0.4)) order by d asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"3";s:1:"d";s:6:"0.0004";}i:2;a:2:{s:2:"id";s:1:"4";s:1:"d";s:6:"1.4125";}i:3;a:2:{s:2:"id";s:1:"2";s:1:"d";s:3:"2.3";}}}i:70;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:71;a:2:{s:8:"sphinxql";s:87:"create table sc2 (v float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:72;a:2:{s:8:"sphinxql";s:73:"insert into sc2 values (1, (0.1,-0.2,0.3,-0.4)), (2, (-0.5,0.5,-0.5,0.5))";s:14:"total_affected";i:2;}i:73;a:2:{s:8:"sphinxql";s:93:"create table ar2 (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:74;a:2:{s:8:"sphinxql";s:77:"insert into ar2 values (1, [[0.1,-0.2,0.3,-0.4]]), (2, [[-0.5,0.5,-0.5,0.5]])";s:14:"total_affected";i:2;}i:75;a:3:{s:8:"sphinxql";s:83:"select id, knn_dist() d from sc2 where knn(v,5,(0.2,-0.1,0.4,-0.3)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:7:"0.06667";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:7:"1.91287";}}}i:76;a:3:{s:8:"sphinxql";s:83:"select id, knn_dist() d from ar2 where knn(v,5,(0.2,-0.1,0.4,-0.3)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:7:"0.06667";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:7:"1.91287";}}}i:77;a:2:{s:8:"sphinxql";s:69:"insert into ar2 values (3, [[0.1,-0.2,0.3,-0.4],[-0.5,0.5,-0.5,0.5]])";s:14:"total_affected";i:1;}i:78;a:3:{s:8:"sphinxql";s:83:"select id, knn_dist() d from ar2 where knn(v,5,(0.2,-0.1,0.4,-0.3)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:7:"0.06667";}i:1;a:2:{s:2:"id";s:1:"3";s:1:"d";s:7:"0.06667";}i:2;a:2:{s:2:"id";s:1:"2";s:1:"d";s:7:"1.91287";}}}i:79;a:2:{s:8:"sphinxql";s:14:"drop table sc2";s:14:"total_affected";i:0;}i:80;a:2:{s:8:"sphinxql";s:14:"drop table ar2";s:14:"total_affected";i:0;}i:81;a:2:{s:8:"sphinxql";s:86:"create table sc (v float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:82;a:2:{s:8:"sphinxql";s:52:"insert into sc values (1, (1,2,3,4)), (2, (4,3,2,1))";s:14:"total_affected";i:2;}i:83;a:2:{s:8:"sphinxql";s:92:"create table ar (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:84;a:2:{s:8:"sphinxql";s:56:"insert into ar values (1, [[1,2,3,4]]), (2, [[4,3,2,1]])";s:14:"total_affected";i:2;}i:85;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from sc where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:86;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from ar where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:87;a:2:{s:8:"sphinxql";s:17:"flush ramchunk sc";s:14:"total_affected";i:0;}i:88;a:2:{s:8:"sphinxql";s:17:"flush ramchunk ar";s:14:"total_affected";i:0;}i:89;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from sc where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:90;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from ar where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:91;a:2:{s:8:"sphinxql";s:13:"drop table sc";s:14:"total_affected";i:0;}i:92;a:2:{s:8:"sphinxql";s:13:"drop table ar";s:14:"total_affected";i:0;}i:93;a:2:{s:8:"sphinxql";s:87:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:94;a:2:{s:8:"sphinxql";s:37:"insert into t values (1, [[1,0,0,0]])";s:14:"total_affected";i:1;}i:95;a:3:{s:8:"sphinxql";s:37:"update t set v=[[0,1,0,0]] where id=1";s:5:"errno";i:1064;s:5:"error";s:60:"table t: unable to update attribute 'v' that has a KNN index";}i:96;a:2:{s:8:"sphinxql";s:48:"replace into t values (1, [[0,1,0,0],[0,0,1,0]])";s:14:"total_affected";i:1;}i:97;a:3:{s:8:"sphinxql";s:19:"select id, v from t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[0.000000,1.000000,0.000000,0.000000],[0.000000,0.000000,1.000000,0.000000]]";}}}i:98;a:3:{s:8:"sphinxql";s:53:"select id, knn_dist() from t where knn(v,5,(0,1,0,0))";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}}}i:99;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:100;a:2:{s:8:"sphinxql";s:37:"create table t (v float_vector_array)";s:14:"total_affected";i:0;}i:101;a:2:{s:8:"sphinxql";s:53:"insert into t values (1, [[1,2],[3,4]]), (2, [[5,6]])";s:14:"total_affected";i:2;}i:102;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:41:"[[1.000000,2.000000],[3.000000,4.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:21:"[[5.000000,6.000000]]";}}}i:103;a:2:{s:8:"sphinxql";s:40:"update t set v=[[7,8],[9,10]] where id=1";s:14:"total_affected";i:1;}i:104;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:42:"[[7.000000,8.000000],[9.000000,10.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:21:"[[5.000000,6.000000]]";}}}i:105;a:2:{s:8:"sphinxql";s:28:"update t set v=[] where id=1";s:14:"total_affected";i:1;}i:106;a:2:{s:8:"sphinxql";s:28:"update t set v=() where id=2";s:14:"total_affected";i:1;}i:107;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:2:"[]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:108;a:3:{s:8:"sphinxql";s:35:"update t set v=(1,2,3,4) where id=1";s:5:"errno";i:1064;s:5:"error";s:91:"table t: attribute 'v': float_vector_array requires an array of vectors, e.g. [[1,2],[3,4]]";}i:109;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:2:"[]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:110;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:111;a:2:{s:8:"sphinxql";s:90:"create table t (v float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', m multi)";s:14:"total_affected";i:0;}i:112;a:2:{s:8:"sphinxql";s:44:"insert into t values (1, (1,0,0,0), (1,2,3))";s:14:"total_affected";i:1;}i:113;a:3:{s:8:"sphinxql";s:39:"update t set m=[[1,2],[3,4]] where id=1";s:5:"errno";i:1064;s:5:"error";s:92:"table t: attribute 'm': nested vector syntax is only valid for float_vector_array attributes";}i:114;a:3:{s:8:"sphinxql";s:46:"insert into t values (2, [[1,0,0,0]], (1,2,3))";s:5:"errno";i:1064;s:5:"error";s:87:"column 2 at row 0: nested vector syntax is only valid for float_vector_array attributes";}i:115;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:116;a:2:{s:8:"sphinxql";s:37:"create table t (v float_vector_array)";s:14:"total_affected";i:0;}i:117;a:2:{s:8:"sphinxql";s:66:"insert into t values (1, [[1,2]]), (2, [[1,2,3],[4,5,6]]), (3, [])";s:14:"total_affected";i:3;}i:118;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:21:"[[1.000000,2.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:59:"[[1.000000,2.000000,3.000000],[4.000000,5.000000,6.000000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:2:"[]";}}}i:119;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:120;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:21:"[[1.000000,2.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:59:"[[1.000000,2.000000,3.000000],[4.000000,5.000000,6.000000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:2:"[]";}}}i:121;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:122;a:2:{s:8:"sphinxql";s:87:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:123;a:2:{s:8:"sphinxql";s:56:"insert into t values (1, [[1,2,3,4],[5,6,7,8]]), (2, [])";s:14:"total_affected";i:2;}i:124;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:125;a:3:{s:8:"sphinxql";s:46:"select id, to_string(v) from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:12:"to_string(v)";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:12:"to_string(v)";s:2:"[]";}}}i:126;a:3:{s:8:"sphinxql";s:36:"select id, v from t order by id desc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}i:1;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}}}i:127;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:128;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:129;a:3:{s:8:"sphinxql";s:46:"select id, to_string(v) from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:12:"to_string(v)";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:12:"to_string(v)";s:2:"[]";}}}i:130;a:2:{s:8:"sphinxql";s:61:"create table d type='distributed' agent=':t'";s:14:"total_affected";i:0;}i:131;a:3:{s:8:"sphinxql";s:35:"select id, v from d order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:132;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from d where knn(v,5,(1,2,3,4)) order by id asc";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}}}i:133;a:2:{s:8:"sphinxql";s:12:"drop table d";s:14:"total_affected";i:0;}i:134;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:135;a:2:{s:8:"sphinxql";s:153:"create table t (a float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', b float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:136;a:2:{s:8:"sphinxql";s:58:"insert into t values (1, (1,0,0,0), [[0,1,0,0],[0,0,1,0]])";s:14:"total_affected";i:1;}i:137;a:2:{s:8:"sphinxql";s:48:"insert into t values (2, (0,1,0,0), [[1,0,0,0]])";s:14:"total_affected";i:1;}i:138;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:139;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(a,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:140;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(b,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"2";}}}i:141;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:142;a:2:{s:8:"sphinxql";s:153:"create table t (b float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', a float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:143;a:2:{s:8:"sphinxql";s:58:"insert into t values (1, [[0,1,0,0],[0,0,1,0]], (1,0,0,0))";s:14:"total_affected";i:1;}i:144;a:2:{s:8:"sphinxql";s:48:"insert into t values (2, [[1,0,0,0]], (0,1,0,0))";s:14:"total_affected";i:1;}i:145;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:146;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(a,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:147;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(b,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"2";}}}i:148;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:149;a:2:{s:8:"sphinxql";s:116:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit', tag int)";s:14:"total_affected";i:0;}i:150;a:2:{s:8:"sphinxql";s:43:"insert into t values (1, [], 1), (2, [], 2)";s:14:"total_affected";i:2;}i:151;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:152;a:2:{s:8:"sphinxql";s:41:"select id from t where knn(v,5,(1,0,0,0))";s:10:"total_rows";i:0;}i:153;a:2:{s:8:"sphinxql";s:51:"select id from t where tag=1 and knn(v,5,(1,0,0,0))";s:10:"total_rows";i:0;}i:154;a:2:{s:8:"sphinxql";s:50:"insert into t values (3, [[1,0,0,0],[0,1,0,0]], 3)";s:14:"total_affected";i:1;}i:155;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:156;a:3:{s:8:"sphinxql";s:53:"select id, knn_dist() from t where knn(v,5,(1,0,0,0))";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"3";s:10:"knn_dist()";s:1:"0";}}}i:157;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:158;a:2:{s:8:"sphinxql";s:96:"create table t (v float_vector_array knn_type='hnsw' knn_dims='2' hnsw_similarity='l2', tag int)";s:14:"total_affected";i:0;}i:159;a:2:{s:8:"sphinxql";s:2296:"insert into t values (1,[[1,0],[0,1]],1),(2,[[2,0],[0,2]],2),(3,[[3,0],[0,3]],3),(4,[[4,0],[0,4]],4),(5,[[5,0],[0,5]],5),(6,[[6,0],[0,6]],6),(7,[[7,0],[0,7]],7),(8,[[8,0],[0,8]],8),(9,[[9,0],[0,9]],9),(10,[[10,0],[0,10]],0),(11,[[11,0],[0,11]],1),(12,[[12,0],[0,12]],2),(13,[[13,0],[0,13]],3),(14,[[14,0],[0,14]],4),(15,[[15,0],[0,15]],5),(16,[[16,0],[0,16]],6),(17,[[17,0],[0,17]],7),(18,[[18,0],[0,18]],8),(19,[[19,0],[0,19]],9),(20,[[20,0],[0,20]],0),(21,[[21,0],[0,21]],1),(22,[[22,0],[0,22]],2),(23,[[23,0],[0,23]],3),(24,[[24,0],[0,24]],4),(25,[[25,0],[0,25]],5),(26,[[26,0],[0,26]],6),(27,[[27,0],[0,27]],7),(28,[[28,0],[0,28]],8),(29,[[29,0],[0,29]],9),(30,[[30,0],[0,30]],0),(31,[[31,0],[0,31]],1),(32,[[32,0],[0,32]],2),(33,[[33,0],[0,33]],3),(34,[[34,0],[0,34]],4),(35,[[35,0],[0,35]],5),(36,[[36,0],[0,36]],6),(37,[[37,0],[0,37]],7),(38,[[38,0],[0,38]],8),(39,[[39,0],[0,39]],9),(40,[[40,0],[0,40]],0),(41,[[41,0],[0,41]],1),(42,[[42,0],[0,42]],2),(43,[[43,0],[0,43]],3),(44,[[44,0],[0,44]],4),(45,[[45,0],[0,45]],5),(46,[[46,0],[0,46]],6),(47,[[47,0],[0,47]],7),(48,[[48,0],[0,48]],8),(49,[[49,0],[0,49]],9),(50,[[50,0],[0,50]],0),(51,[[51,0],[0,51]],1),(52,[[52,0],[0,52]],2),(53,[[53,0],[0,53]],3),(54,[[54,0],[0,54]],4),(55,[[55,0],[0,55]],5),(56,[[56,0],[0,56]],6),(57,[[57,0],[0,57]],7),(58,[[58,0],[0,58]],8),(59,[[59,0],[0,59]],9),(60,[[60,0],[0,60]],0),(61,[[61,0],[0,61]],1),(62,[[62,0],[0,62]],2),(63,[[63,0],[0,63]],3),(64,[[64,0],[0,64]],4),(65,[[65,0],[0,65]],5),(66,[[66,0],[0,66]],6),(67,[[67,0],[0,67]],7),(68,[[68,0],[0,68]],8),(69,[[69,0],[0,69]],9),(70,[[70,0],[0,70]],0),(71,[[71,0],[0,71]],1),(72,[[72,0],[0,72]],2),(73,[[73,0],[0,73]],3),(74,[[74,0],[0,74]],4),(75,[[75,0],[0,75]],5),(76,[[76,0],[0,76]],6),(77,[[77,0],[0,77]],7),(78,[[78,0],[0,78]],8),(79,[[79,0],[0,79]],9),(80,[[80,0],[0,80]],0),(81,[[81,0],[0,81]],1),(82,[[82,0],[0,82]],2),(83,[[83,0],[0,83]],3),(84,[[84,0],[0,84]],4),(85,[[85,0],[0,85]],5),(86,[[86,0],[0,86]],6),(87,[[87,0],[0,87]],7),(88,[[88,0],[0,88]],8),(89,[[89,0],[0,89]],9),(90,[[90,0],[0,90]],0),(91,[[91,0],[0,91]],1),(92,[[92,0],[0,92]],2),(93,[[93,0],[0,93]],3),(94,[[94,0],[0,94]],4),(95,[[95,0],[0,95]],5),(96,[[96,0],[0,96]],6),(97,[[97,0],[0,97]],7),(98,[[98,0],[0,98]],8),(99,[[99,0],[0,99]],9),(100,[[100,0],[0,100]],0)";s:14:"total_affected";i:100;}i:160;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:161;a:3:{s:8:"sphinxql";s:84:"select id, knn_dist() d from t where tag<4 and knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"d";s:1:"4";}}}i:162;a:3:{s:8:"sphinxql";s:84:"select id, knn_dist() d from t where tag<7 and knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"d";s:1:"4";}}}i:163;a:3:{s:8:"sphinxql";s:84:"select id, knn_dist() d from t where tag=5 and knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"5";s:1:"d";s:2:"16";}i:1;a:2:{s:2:"id";s:2:"15";s:1:"d";s:3:"196";}i:2;a:2:{s:2:"id";s:2:"25";s:1:"d";s:3:"576";}}}i:164;a:3:{s:8:"sphinxql";s:74:"select id, knn_dist() d from t where knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"d";s:1:"4";}}}i:165;a:3:{s:8:"sphinxql";s:104:"select id, count(*) c from t where tag<4 and knn(v,20,(1,0)) group by id order by c desc, id asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"c";s:1:"1";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"c";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"c";s:1:"1";}}}i:166;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:167;a:2:{s:8:"sphinxql";s:112:"create table t (s float_vector knn_type='hnsw' knn_dims='2' hnsw_similarity='l2', v float_vector_array, tag int)";s:14:"total_affected";i:0;}i:168;a:2:{s:8:"sphinxql";s:43:"insert into t values (1, (1,2), [[1,2]], 7)";s:14:"total_affected";i:1;}i:169;a:2:{s:8:"sphinxql";s:49:"insert into t values (2, (2,3), [[2,3],[1,2]], 8)";s:14:"total_affected";i:1;}i:170;a:2:{s:8:"sphinxql";s:38:"insert into t values (3, (0,0), [], 9)";s:14:"total_affected";i:1;}i:171;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"2";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"1";}}s:8:"sphinxql";s:25:"select id from t facet s;";}i:172;a:3:{s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:1:"s";s:1:"0";s:8:"count(*)";s:1:"1";}i:1;a:2:{s:1:"s";s:10:"1077936128";s:8:"count(*)";s:1:"1";}i:2;a:2:{s:1:"s";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:3;a:2:{s:1:"s";s:10:"1065353216";s:8:"count(*)";s:1:"1";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:173;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"2";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"1";}}s:8:"sphinxql";s:25:"select id from t facet v;";}i:174;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:10:"1077936128";s:8:"count(*)";s:1:"1";}i:1;a:2:{s:1:"v";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:10:"1065353216";s:8:"count(*)";s:1:"2";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:175;a:3:{s:8:"sphinxql";s:51:"select v, count(*) from t group by v order by v asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:41:"[[2.000000,3.000000],[1.000000,2.000000]]";s:8:"count(*)";s:1:"1";}}}i:176;a:3:{s:8:"sphinxql";s:31:"select count(distinct v) from t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:1:{s:17:"count(distinct v)";s:1:"3";}}}i:177;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:178;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}i:2;a:1:{s:2:"id";s:1:"3";}}s:8:"sphinxql";s:25:"select id from t facet s;";}i:179;a:3:{s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:1:"s";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"s";s:10:"1065353216";s:8:"count(*)";s:1:"1";}i:2;a:2:{s:1:"s";s:10:"1077936128";s:8:"count(*)";s:1:"1";}i:3;a:2:{s:1:"s";s:1:"0";s:8:"count(*)";s:1:"1";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:180;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}i:2;a:1:{s:2:"id";s:1:"3";}}s:8:"sphinxql";s:25:"select id from t facet v;";}i:181;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"v";s:10:"1065353216";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:10:"1077936128";s:8:"count(*)";s:1:"1";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:182;a:3:{s:8:"sphinxql";s:51:"select v, count(*) from t group by v order by v asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:41:"[[2.000000,3.000000],[1.000000,2.000000]]";s:8:"count(*)";s:1:"1";}}}i:183;a:3:{s:8:"sphinxql";s:31:"select count(distinct v) from t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:1:{s:17:"count(distinct v)";s:1:"3";}}}i:184;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}}} \ No newline at end of file +)";}}}i:7;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:8;a:3:{s:8:"sphinxql";s:117:"create table t (title text, v float_vector_array knn_type='hnsw' model_name='sentence-transformers/all-MiniLM-L6-v2')";s:5:"errno";i:1064;s:5:"error";s:66:"error adding table 't': 'from' setting empty for KNN attribute 'v'";}i:9;a:3:{s:8:"sphinxql";s:84:"create table t (title text, v int knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:5:"errno";i:1064;s:5:"error";s:98:"P03: knn_type='hnsw' can only be used with float_vector and float_vector_array attributes near ')'";}i:10;a:2:{s:8:"sphinxql";s:99:"create table t (title text, v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:11;a:2:{s:8:"sphinxql";s:56:"insert into t values (1, 'multi', [[1,0,0,0],[0,1,0,0]])";s:14:"total_affected";i:1;}i:12;a:2:{s:8:"sphinxql";s:47:"insert into t values (2, 'single', [[0,0,1,0]])";s:14:"total_affected";i:1;}i:13;a:2:{s:8:"sphinxql";s:37:"insert into t values (3, 'empty', [])";s:14:"total_affected";i:1;}i:14;a:2:{s:8:"sphinxql";s:46:"insert into t (id,title) values (4, 'omitted')";s:14:"total_affected";i:1;}i:15;a:3:{s:8:"sphinxql";s:31:"select * from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:3:{s:2:"id";s:1:"1";s:5:"title";s:5:"multi";s:1:"v";s:77:"[[1.000000,0.000000,0.000000,0.000000],[0.000000,1.000000,0.000000,0.000000]]";}i:1;a:3:{s:2:"id";s:1:"2";s:5:"title";s:6:"single";s:1:"v";s:39:"[[0.000000,0.000000,1.000000,0.000000]]";}i:2;a:3:{s:2:"id";s:1:"3";s:5:"title";s:5:"empty";s:1:"v";s:2:"[]";}i:3;a:3:{s:2:"id";s:1:"4";s:5:"title";s:7:"omitted";s:1:"v";s:2:"[]";}}}i:16;a:3:{s:8:"sphinxql";s:43:"insert into t values (5, 'flat', (1,0,0,0))";s:5:"errno";i:1064;s:5:"error";s:86:"column 2 at row 0: float_vector_array requires an array of vectors, e.g. [[1,2],[3,4]]";}i:17;a:3:{s:8:"sphinxql";s:45:"insert into t values (5, 'inner empty', [[]])";s:5:"errno";i:1064;s:5:"error";s:75:"column 2 at row 0: vector #1 is empty; vectors must have at least one entry";}i:18;a:3:{s:8:"sphinxql";s:50:"insert into t values (5, 'inner empty 2', [[],[]])";s:5:"errno";i:1064;s:5:"error";s:75:"column 2 at row 0: vector #1 is empty; vectors must have at least one entry";}i:19;a:3:{s:8:"sphinxql";s:55:"insert into t values (5, 'ragged', [[1,2,3,4],[5,6,7]])";s:5:"errno";i:1064;s:5:"error";s:54:"column 2 at row 0: vector #2 has 3 entries, expected 4";}i:20;a:3:{s:8:"sphinxql";s:53:"insert into t values (5, 'wrong dims', [[1,2],[3,4]])";s:5:"errno";i:1064;s:5:"error";s:58:"KNN error: vectors have 2 values, index 'v' needs 4 values";}i:21;a:3:{s:8:"sphinxql";s:39:"select id, title from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:5:"title";s:5:"multi";}i:1;a:2:{s:2:"id";s:1:"2";s:5:"title";s:6:"single";}i:2;a:2:{s:2:"id";s:1:"3";s:5:"title";s:5:"empty";}i:3;a:2:{s:2:"id";s:1:"4";s:5:"title";s:7:"omitted";}}}i:22;a:2:{s:8:"sphinxql";s:55:"insert into t values (5, 'ints', [[1,0,0,0],[0,1,0,0]])";s:14:"total_affected";i:1;}i:23;a:3:{s:8:"sphinxql";s:30:"select id, v from t where id=5";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"5";s:1:"v";s:77:"[[1.000000,0.000000,0.000000,0.000000],[0.000000,1.000000,0.000000,0.000000]]";}}}i:24;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:25;a:3:{s:8:"sphinxql";s:82:"select id, knn_dist() from t where knn(v,10,(1,0,0,0),{rescore=1}) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:26;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:27;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:28;a:3:{s:8:"sphinxql";s:82:"select id, knn_dist() from t where knn(v,10,(1,0,0,0),{rescore=1}) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:2;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:29;a:3:{s:8:"sphinxql";s:41:"select id from t where knn(v,1,(1,0,0,0))";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"5";}i:2;a:1:{s:2:"id";s:1:"2";}}}i:30;a:3:{s:8:"sphinxql";s:57:"select id from t where knn(v,2,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"5";}i:2;a:1:{s:2:"id";s:1:"2";}}}i:31;a:3:{s:8:"sphinxql";s:82:"select id, count(*) c from t where knn(v,10,(1,0,0,0)) group by id order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"c";s:1:"1";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"c";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"5";s:1:"c";s:1:"1";}}}i:32;a:3:{s:8:"sphinxql";s:40:"select id from t where knn(v,10,(1,0,0))";s:5:"errno";i:1064;s:5:"error";s:74:"table t: KNN index 'v' requires a vector of 4 entries; 3 entries specified";}i:33;a:3:{s:8:"sphinxql";s:80:"select id, knn_dist() from t where id!=1 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:34;a:3:{s:8:"sphinxql";s:101:"select id, knn_dist() from t where id!=1 and @knn_dist < 1e38 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"5";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:35;a:3:{s:8:"sphinxql";s:95:"select id, knn_dist() d from t where id!=1 and d < 1e38 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"5";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"2";}}}i:36;a:3:{s:8:"sphinxql";s:112:"select id, knn_dist() d from t where (id=2 or id=3 or id=4) and d < 1e38 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"2";}}}i:37;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:38;a:2:{s:8:"sphinxql";s:108:"create table t (title text, v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', tag int)";s:14:"total_affected";i:0;}i:39;a:2:{s:8:"sphinxql";s:42:"select id from t where knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:40;a:2:{s:8:"sphinxql";s:52:"select id from t where tag=1 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:41;a:2:{s:8:"sphinxql";s:36:"insert into t values (1, 'a', [], 1)";s:14:"total_affected";i:1;}i:42;a:2:{s:8:"sphinxql";s:36:"insert into t values (2, 'b', [], 2)";s:14:"total_affected";i:1;}i:43;a:2:{s:8:"sphinxql";s:42:"select id from t where knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:44;a:2:{s:8:"sphinxql";s:52:"select id from t where tag=1 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:45;a:2:{s:8:"sphinxql";s:52:"select id from t where tag<9 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:46;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:47;a:2:{s:8:"sphinxql";s:42:"select id from t where knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:48;a:2:{s:8:"sphinxql";s:52:"select id from t where tag=1 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:49;a:2:{s:8:"sphinxql";s:52:"select id from t where tag<9 and knn(v,10,(1,0,0,0))";s:10:"total_rows";i:0;}i:50;a:3:{s:8:"sphinxql";s:31:"select * from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:4:{s:2:"id";s:1:"1";s:5:"title";s:1:"a";s:1:"v";s:2:"[]";s:3:"tag";s:1:"1";}i:1;a:4:{s:2:"id";s:1:"2";s:5:"title";s:1:"b";s:1:"v";s:2:"[]";s:3:"tag";s:1:"2";}}}i:51;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:52;a:2:{s:8:"sphinxql";s:96:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', tag int)";s:14:"total_affected";i:0;}i:53;a:2:{s:8:"sphinxql";s:106:"insert into t values (1, [[1,0,0,0]], 1), (2, [], 1), (3, [[0,1,0,0]], 1), (4, [], 1), (5, [[0,0,1,0]], 1)";s:14:"total_affected";i:5;}i:54;a:3:{s:8:"sphinxql";s:58:"select id from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:55;a:3:{s:8:"sphinxql";s:68:"select id from t where tag=1 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:56;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:57;a:3:{s:8:"sphinxql";s:58:"select id from t where knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:58;a:3:{s:8:"sphinxql";s:68:"select id from t where tag=1 and knn(v,10,(1,0,0,0)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"5";}}}i:59;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:60;a:2:{s:8:"sphinxql";s:87:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:61;a:2:{s:8:"sphinxql";s:66:"insert into t values (1, [[0.1,-0.2,0.3,-0.4],[0.9,0.8,-0.7,0.6]])";s:14:"total_affected";i:1;}i:62;a:2:{s:8:"sphinxql";s:47:"insert into t values (2, [[-0.5,0.5,-0.5,0.5]])";s:14:"total_affected";i:1;}i:63;a:2:{s:8:"sphinxql";s:51:"insert into t values (3, [[0.11,-0.21,0.31,-0.41]])";s:14:"total_affected";i:1;}i:64;a:2:{s:8:"sphinxql";s:55:"insert into t values (4, [[1,-0.5,0,0.25],[-1,2,-3,4]])";s:14:"total_affected";i:1;}i:65;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:80:"[[0.100000,-0.200000,0.300000,-0.400000],[0.900000,0.800000,-0.700000,0.600000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:41:"[[-0.500000,0.500000,-0.500000,0.500000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:41:"[[0.110000,-0.210000,0.310000,-0.410000]]";}i:3;a:2:{s:2:"id";s:1:"4";s:1:"v";s:80:"[[1.000000,-0.500000,0.000000,0.250000],[-1.000000,2.000000,-3.000000,4.000000]]";}}}i:66;a:3:{s:8:"sphinxql";s:81:"select id, knn_dist() d from t where knn(v,10,(0.1,-0.2,0.3,-0.4)) order by d asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"3";s:1:"d";s:6:"0.0004";}i:2;a:2:{s:2:"id";s:1:"4";s:1:"d";s:6:"1.4125";}i:3;a:2:{s:2:"id";s:1:"2";s:1:"d";s:3:"2.3";}}}i:67;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:68;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:80:"[[0.100000,-0.200000,0.300000,-0.400000],[0.900000,0.800000,-0.700000,0.600000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:41:"[[-0.500000,0.500000,-0.500000,0.500000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:41:"[[0.110000,-0.210000,0.310000,-0.410000]]";}i:3;a:2:{s:2:"id";s:1:"4";s:1:"v";s:80:"[[1.000000,-0.500000,0.000000,0.250000],[-1.000000,2.000000,-3.000000,4.000000]]";}}}i:69;a:3:{s:8:"sphinxql";s:81:"select id, knn_dist() d from t where knn(v,10,(0.1,-0.2,0.3,-0.4)) order by d asc";s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"3";s:1:"d";s:6:"0.0004";}i:2;a:2:{s:2:"id";s:1:"4";s:1:"d";s:6:"1.4125";}i:3;a:2:{s:2:"id";s:1:"2";s:1:"d";s:3:"2.3";}}}i:70;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:71;a:2:{s:8:"sphinxql";s:87:"create table sc2 (v float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:72;a:2:{s:8:"sphinxql";s:73:"insert into sc2 values (1, (0.1,-0.2,0.3,-0.4)), (2, (-0.5,0.5,-0.5,0.5))";s:14:"total_affected";i:2;}i:73;a:2:{s:8:"sphinxql";s:93:"create table ar2 (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:74;a:2:{s:8:"sphinxql";s:77:"insert into ar2 values (1, [[0.1,-0.2,0.3,-0.4]]), (2, [[-0.5,0.5,-0.5,0.5]])";s:14:"total_affected";i:2;}i:75;a:3:{s:8:"sphinxql";s:83:"select id, knn_dist() d from sc2 where knn(v,5,(0.2,-0.1,0.4,-0.3)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:7:"0.06667";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:7:"1.91287";}}}i:76;a:3:{s:8:"sphinxql";s:83:"select id, knn_dist() d from ar2 where knn(v,5,(0.2,-0.1,0.4,-0.3)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:7:"0.06667";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:7:"1.91287";}}}i:77;a:2:{s:8:"sphinxql";s:69:"insert into ar2 values (3, [[0.1,-0.2,0.3,-0.4],[-0.5,0.5,-0.5,0.5]])";s:14:"total_affected";i:1;}i:78;a:3:{s:8:"sphinxql";s:83:"select id, knn_dist() d from ar2 where knn(v,5,(0.2,-0.1,0.4,-0.3)) order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:7:"0.06667";}i:1;a:2:{s:2:"id";s:1:"3";s:1:"d";s:7:"0.06667";}i:2;a:2:{s:2:"id";s:1:"2";s:1:"d";s:7:"1.91287";}}}i:79;a:2:{s:8:"sphinxql";s:14:"drop table sc2";s:14:"total_affected";i:0;}i:80;a:2:{s:8:"sphinxql";s:14:"drop table ar2";s:14:"total_affected";i:0;}i:81;a:2:{s:8:"sphinxql";s:86:"create table sc (v float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:82;a:2:{s:8:"sphinxql";s:52:"insert into sc values (1, (1,2,3,4)), (2, (4,3,2,1))";s:14:"total_affected";i:2;}i:83;a:2:{s:8:"sphinxql";s:92:"create table ar (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='cosine')";s:14:"total_affected";i:0;}i:84;a:2:{s:8:"sphinxql";s:56:"insert into ar values (1, [[1,2,3,4]]), (2, [[4,3,2,1]])";s:14:"total_affected";i:2;}i:85;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from sc where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:86;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from ar where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:87;a:2:{s:8:"sphinxql";s:17:"flush ramchunk sc";s:14:"total_affected";i:0;}i:88;a:2:{s:8:"sphinxql";s:17:"flush ramchunk ar";s:14:"total_affected";i:0;}i:89;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from sc where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:90;a:3:{s:8:"sphinxql";s:70:"select id, knn_dist() from ar where knn(v,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:6:"0.2697";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:7:"0.81743";}}}i:91;a:2:{s:8:"sphinxql";s:13:"drop table sc";s:14:"total_affected";i:0;}i:92;a:2:{s:8:"sphinxql";s:13:"drop table ar";s:14:"total_affected";i:0;}i:93;a:2:{s:8:"sphinxql";s:87:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:94;a:2:{s:8:"sphinxql";s:37:"insert into t values (1, [[1,0,0,0]])";s:14:"total_affected";i:1;}i:95;a:3:{s:8:"sphinxql";s:37:"update t set v=[[0,1,0,0]] where id=1";s:5:"errno";i:1064;s:5:"error";s:60:"table t: unable to update attribute 'v' that has a KNN index";}i:96;a:2:{s:8:"sphinxql";s:48:"replace into t values (1, [[0,1,0,0],[0,0,1,0]])";s:14:"total_affected";i:1;}i:97;a:3:{s:8:"sphinxql";s:19:"select id, v from t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[0.000000,1.000000,0.000000,0.000000],[0.000000,0.000000,1.000000,0.000000]]";}}}i:98;a:3:{s:8:"sphinxql";s:53:"select id, knn_dist() from t where knn(v,5,(0,1,0,0))";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}}}i:99;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:100;a:2:{s:8:"sphinxql";s:37:"create table t (v float_vector_array)";s:14:"total_affected";i:0;}i:101;a:2:{s:8:"sphinxql";s:53:"insert into t values (1, [[1,2],[3,4]]), (2, [[5,6]])";s:14:"total_affected";i:2;}i:102;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:41:"[[1.000000,2.000000],[3.000000,4.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:21:"[[5.000000,6.000000]]";}}}i:103;a:2:{s:8:"sphinxql";s:40:"update t set v=[[7,8],[9,10]] where id=1";s:14:"total_affected";i:1;}i:104;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:42:"[[7.000000,8.000000],[9.000000,10.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:21:"[[5.000000,6.000000]]";}}}i:105;a:2:{s:8:"sphinxql";s:28:"update t set v=[] where id=1";s:14:"total_affected";i:1;}i:106;a:2:{s:8:"sphinxql";s:28:"update t set v=() where id=2";s:14:"total_affected";i:1;}i:107;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:2:"[]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:108;a:3:{s:8:"sphinxql";s:35:"update t set v=(1,2,3,4) where id=1";s:5:"errno";i:1064;s:5:"error";s:91:"table t: attribute 'v': float_vector_array requires an array of vectors, e.g. [[1,2],[3,4]]";}i:109;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:2:"[]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:110;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:111;a:2:{s:8:"sphinxql";s:90:"create table t (v float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', m multi)";s:14:"total_affected";i:0;}i:112;a:2:{s:8:"sphinxql";s:44:"insert into t values (1, (1,0,0,0), (1,2,3))";s:14:"total_affected";i:1;}i:113;a:3:{s:8:"sphinxql";s:39:"update t set m=[[1,2],[3,4]] where id=1";s:5:"errno";i:1064;s:5:"error";s:92:"table t: attribute 'm': nested vector syntax is only valid for float_vector_array attributes";}i:114;a:3:{s:8:"sphinxql";s:46:"insert into t values (2, [[1,0,0,0]], (1,2,3))";s:5:"errno";i:1064;s:5:"error";s:87:"column 2 at row 0: nested vector syntax is only valid for float_vector_array attributes";}i:115;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:116;a:2:{s:8:"sphinxql";s:37:"create table t (v float_vector_array)";s:14:"total_affected";i:0;}i:117;a:2:{s:8:"sphinxql";s:66:"insert into t values (1, [[1,2]]), (2, [[1,2,3],[4,5,6]]), (3, [])";s:14:"total_affected";i:3;}i:118;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:21:"[[1.000000,2.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:59:"[[1.000000,2.000000,3.000000],[4.000000,5.000000,6.000000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:2:"[]";}}}i:119;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:120;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:21:"[[1.000000,2.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:59:"[[1.000000,2.000000,3.000000],[4.000000,5.000000,6.000000]]";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"v";s:2:"[]";}}}i:121;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:122;a:2:{s:8:"sphinxql";s:87:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:123;a:2:{s:8:"sphinxql";s:56:"insert into t values (1, [[1,2,3,4],[5,6,7,8]]), (2, [])";s:14:"total_affected";i:2;}i:124;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:125;a:3:{s:8:"sphinxql";s:46:"select id, to_string(v) from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:12:"to_string(v)";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:12:"to_string(v)";s:2:"[]";}}}i:126;a:3:{s:8:"sphinxql";s:36:"select id, v from t order by id desc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}i:1;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}}}i:127;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:128;a:3:{s:8:"sphinxql";s:35:"select id, v from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:129;a:3:{s:8:"sphinxql";s:46:"select id, to_string(v) from t order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:12:"to_string(v)";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:12:"to_string(v)";s:2:"[]";}}}i:130;a:2:{s:8:"sphinxql";s:61:"create table d type='distributed' agent=':t'";s:14:"total_affected";i:0;}i:131;a:3:{s:8:"sphinxql";s:35:"select id, v from d order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"v";s:77:"[[1.000000,2.000000,3.000000,4.000000],[5.000000,6.000000,7.000000,8.000000]]";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"v";s:2:"[]";}}}i:132;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from d where knn(v,5,(1,2,3,4)) order by id asc";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}}}i:133;a:2:{s:8:"sphinxql";s:12:"drop table d";s:14:"total_affected";i:0;}i:134;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:135;a:2:{s:8:"sphinxql";s:153:"create table t (a float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', b float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:136;a:2:{s:8:"sphinxql";s:58:"insert into t values (1, (1,0,0,0), [[0,1,0,0],[0,0,1,0]])";s:14:"total_affected";i:1;}i:137;a:2:{s:8:"sphinxql";s:48:"insert into t values (2, (0,1,0,0), [[1,0,0,0]])";s:14:"total_affected";i:1;}i:138;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:139;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(a,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:140;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(b,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"2";}}}i:141;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:142;a:2:{s:8:"sphinxql";s:153:"create table t (b float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2', a float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2')";s:14:"total_affected";i:0;}i:143;a:2:{s:8:"sphinxql";s:58:"insert into t values (1, [[0,1,0,0],[0,0,1,0]], (1,0,0,0))";s:14:"total_affected";i:1;}i:144;a:2:{s:8:"sphinxql";s:48:"insert into t values (2, [[1,0,0,0]], (0,1,0,0))";s:14:"total_affected";i:1;}i:145;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:146;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(a,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"2";}}}i:147;a:3:{s:8:"sphinxql";s:69:"select id, knn_dist() from t where knn(b,5,(1,0,0,0)) order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:2:{s:2:"id";s:1:"2";s:10:"knn_dist()";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"1";s:10:"knn_dist()";s:1:"2";}}}i:148;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:149;a:2:{s:8:"sphinxql";s:116:"create table t (v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit', tag int)";s:14:"total_affected";i:0;}i:150;a:2:{s:8:"sphinxql";s:43:"insert into t values (1, [], 1), (2, [], 2)";s:14:"total_affected";i:2;}i:151;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:152;a:2:{s:8:"sphinxql";s:41:"select id from t where knn(v,5,(1,0,0,0))";s:10:"total_rows";i:0;}i:153;a:2:{s:8:"sphinxql";s:51:"select id from t where tag=1 and knn(v,5,(1,0,0,0))";s:10:"total_rows";i:0;}i:154;a:2:{s:8:"sphinxql";s:50:"insert into t values (3, [[1,0,0,0],[0,1,0,0]], 3)";s:14:"total_affected";i:1;}i:155;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:156;a:3:{s:8:"sphinxql";s:53:"select id, knn_dist() from t where knn(v,5,(1,0,0,0))";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:2:"id";s:1:"3";s:10:"knn_dist()";s:1:"0";}}}i:157;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:158;a:2:{s:8:"sphinxql";s:96:"create table t (v float_vector_array knn_type='hnsw' knn_dims='2' hnsw_similarity='l2', tag int)";s:14:"total_affected";i:0;}i:159;a:2:{s:8:"sphinxql";s:2296:"insert into t values (1,[[1,0],[0,1]],1),(2,[[2,0],[0,2]],2),(3,[[3,0],[0,3]],3),(4,[[4,0],[0,4]],4),(5,[[5,0],[0,5]],5),(6,[[6,0],[0,6]],6),(7,[[7,0],[0,7]],7),(8,[[8,0],[0,8]],8),(9,[[9,0],[0,9]],9),(10,[[10,0],[0,10]],0),(11,[[11,0],[0,11]],1),(12,[[12,0],[0,12]],2),(13,[[13,0],[0,13]],3),(14,[[14,0],[0,14]],4),(15,[[15,0],[0,15]],5),(16,[[16,0],[0,16]],6),(17,[[17,0],[0,17]],7),(18,[[18,0],[0,18]],8),(19,[[19,0],[0,19]],9),(20,[[20,0],[0,20]],0),(21,[[21,0],[0,21]],1),(22,[[22,0],[0,22]],2),(23,[[23,0],[0,23]],3),(24,[[24,0],[0,24]],4),(25,[[25,0],[0,25]],5),(26,[[26,0],[0,26]],6),(27,[[27,0],[0,27]],7),(28,[[28,0],[0,28]],8),(29,[[29,0],[0,29]],9),(30,[[30,0],[0,30]],0),(31,[[31,0],[0,31]],1),(32,[[32,0],[0,32]],2),(33,[[33,0],[0,33]],3),(34,[[34,0],[0,34]],4),(35,[[35,0],[0,35]],5),(36,[[36,0],[0,36]],6),(37,[[37,0],[0,37]],7),(38,[[38,0],[0,38]],8),(39,[[39,0],[0,39]],9),(40,[[40,0],[0,40]],0),(41,[[41,0],[0,41]],1),(42,[[42,0],[0,42]],2),(43,[[43,0],[0,43]],3),(44,[[44,0],[0,44]],4),(45,[[45,0],[0,45]],5),(46,[[46,0],[0,46]],6),(47,[[47,0],[0,47]],7),(48,[[48,0],[0,48]],8),(49,[[49,0],[0,49]],9),(50,[[50,0],[0,50]],0),(51,[[51,0],[0,51]],1),(52,[[52,0],[0,52]],2),(53,[[53,0],[0,53]],3),(54,[[54,0],[0,54]],4),(55,[[55,0],[0,55]],5),(56,[[56,0],[0,56]],6),(57,[[57,0],[0,57]],7),(58,[[58,0],[0,58]],8),(59,[[59,0],[0,59]],9),(60,[[60,0],[0,60]],0),(61,[[61,0],[0,61]],1),(62,[[62,0],[0,62]],2),(63,[[63,0],[0,63]],3),(64,[[64,0],[0,64]],4),(65,[[65,0],[0,65]],5),(66,[[66,0],[0,66]],6),(67,[[67,0],[0,67]],7),(68,[[68,0],[0,68]],8),(69,[[69,0],[0,69]],9),(70,[[70,0],[0,70]],0),(71,[[71,0],[0,71]],1),(72,[[72,0],[0,72]],2),(73,[[73,0],[0,73]],3),(74,[[74,0],[0,74]],4),(75,[[75,0],[0,75]],5),(76,[[76,0],[0,76]],6),(77,[[77,0],[0,77]],7),(78,[[78,0],[0,78]],8),(79,[[79,0],[0,79]],9),(80,[[80,0],[0,80]],0),(81,[[81,0],[0,81]],1),(82,[[82,0],[0,82]],2),(83,[[83,0],[0,83]],3),(84,[[84,0],[0,84]],4),(85,[[85,0],[0,85]],5),(86,[[86,0],[0,86]],6),(87,[[87,0],[0,87]],7),(88,[[88,0],[0,88]],8),(89,[[89,0],[0,89]],9),(90,[[90,0],[0,90]],0),(91,[[91,0],[0,91]],1),(92,[[92,0],[0,92]],2),(93,[[93,0],[0,93]],3),(94,[[94,0],[0,94]],4),(95,[[95,0],[0,95]],5),(96,[[96,0],[0,96]],6),(97,[[97,0],[0,97]],7),(98,[[98,0],[0,98]],8),(99,[[99,0],[0,99]],9),(100,[[100,0],[0,100]],0)";s:14:"total_affected";i:100;}i:160;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:161;a:3:{s:8:"sphinxql";s:84:"select id, knn_dist() d from t where tag<4 and knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"d";s:1:"4";}}}i:162;a:3:{s:8:"sphinxql";s:84:"select id, knn_dist() d from t where tag<7 and knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"d";s:1:"4";}}}i:163;a:3:{s:8:"sphinxql";s:84:"select id, knn_dist() d from t where tag=5 and knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"5";s:1:"d";s:2:"16";}i:1;a:2:{s:2:"id";s:2:"15";s:1:"d";s:3:"196";}i:2;a:2:{s:2:"id";s:2:"25";s:1:"d";s:3:"576";}}}i:164;a:3:{s:8:"sphinxql";s:74:"select id, knn_dist() d from t where knn(v,5,(1,0)) order by d asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"d";s:1:"0";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"d";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"d";s:1:"4";}}}i:165;a:3:{s:8:"sphinxql";s:104:"select id, count(*) c from t where tag<4 and knn(v,20,(1,0)) group by id order by c desc, id asc limit 3";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:2:"id";s:1:"1";s:1:"c";s:1:"1";}i:1;a:2:{s:2:"id";s:1:"2";s:1:"c";s:1:"1";}i:2;a:2:{s:2:"id";s:1:"3";s:1:"c";s:1:"1";}}}i:166;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:167;a:2:{s:8:"sphinxql";s:112:"create table t (s float_vector knn_type='hnsw' knn_dims='2' hnsw_similarity='l2', v float_vector_array, tag int)";s:14:"total_affected";i:0;}i:168;a:2:{s:8:"sphinxql";s:43:"insert into t values (1, (1,2), [[1,2]], 7)";s:14:"total_affected";i:1;}i:169;a:2:{s:8:"sphinxql";s:49:"insert into t values (2, (2,3), [[2,3],[1,2]], 8)";s:14:"total_affected";i:1;}i:170;a:2:{s:8:"sphinxql";s:38:"insert into t values (3, (0,0), [], 9)";s:14:"total_affected";i:1;}i:171;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"2";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"1";}}s:8:"sphinxql";s:25:"select id from t facet s;";}i:172;a:3:{s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:1:"s";s:1:"0";s:8:"count(*)";s:1:"1";}i:1;a:2:{s:1:"s";s:10:"1077936128";s:8:"count(*)";s:1:"1";}i:2;a:2:{s:1:"s";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:3;a:2:{s:1:"s";s:10:"1065353216";s:8:"count(*)";s:1:"1";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:173;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"2";}i:1;a:1:{s:2:"id";s:1:"3";}i:2;a:1:{s:2:"id";s:1:"1";}}s:8:"sphinxql";s:25:"select id from t facet v;";}i:174;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:10:"1077936128";s:8:"count(*)";s:1:"1";}i:1;a:2:{s:1:"v";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:10:"1065353216";s:8:"count(*)";s:1:"2";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:175;a:3:{s:8:"sphinxql";s:51:"select v, count(*) from t group by v order by v asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:41:"[[2.000000,3.000000],[1.000000,2.000000]]";s:8:"count(*)";s:1:"1";}}}i:176;a:3:{s:8:"sphinxql";s:31:"select count(distinct v) from t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:1:{s:17:"count(distinct v)";s:1:"3";}}}i:177;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:178;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}i:2;a:1:{s:2:"id";s:1:"3";}}s:8:"sphinxql";s:25:"select id from t facet s;";}i:179;a:3:{s:10:"total_rows";i:4;s:4:"rows";a:4:{i:0;a:2:{s:1:"s";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"s";s:10:"1065353216";s:8:"count(*)";s:1:"1";}i:2;a:2:{s:1:"s";s:10:"1077936128";s:8:"count(*)";s:1:"1";}i:3;a:2:{s:1:"s";s:1:"0";s:8:"count(*)";s:1:"1";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:180;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}i:2;a:1:{s:2:"id";s:1:"3";}}s:8:"sphinxql";s:25:"select id from t facet v;";}i:181;a:3:{s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:10:"1073741824";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"v";s:10:"1065353216";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:10:"1077936128";s:8:"count(*)";s:1:"1";}}s:8:"sphinxql";s:42:" /* result 2 of previous multistatement */";}i:182;a:3:{s:8:"sphinxql";s:51:"select v, count(*) from t group by v order by v asc";s:10:"total_rows";i:3;s:4:"rows";a:3:{i:0;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:1;a:2:{s:1:"v";s:21:"[[1.000000,2.000000]]";s:8:"count(*)";s:1:"2";}i:2;a:2:{s:1:"v";s:41:"[[2.000000,3.000000],[1.000000,2.000000]]";s:8:"count(*)";s:1:"1";}}}i:183;a:3:{s:8:"sphinxql";s:31:"select count(distinct v) from t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:1:{s:17:"count(distinct v)";s:1:"3";}}}i:184;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}}} \ No newline at end of file diff --git a/test/test_524/test.xml b/test/test_524/test.xml index c9b465c31d..c87ecec47c 100644 --- a/test/test_524/test.xml +++ b/test/test_524/test.xml @@ -31,7 +31,8 @@ searchd show create table t; drop table t; - + create table t (title text, v float_vector_array knn_type='hnsw' model_name='sentence-transformers/all-MiniLM-L6-v2'); create table t (title text, v int knn_type='hnsw' knn_dims='4' hnsw_similarity='l2'); diff --git a/test/test_527/model.bin b/test/test_527/model.bin new file mode 100644 index 0000000000..10115428c6 --- /dev/null +++ b/test/test_527/model.bin @@ -0,0 +1,22 @@ +a:1:{i:0;a:42:{i:0;a:3:{s:8:"sphinxql";s:60:"create table t (title text, n int chunk_strategy='sentence')";s:5:"errno";i:1064;s:5:"error";s:140:"P03: chunk_strategy, max_tokens, overlap_tokens and max_chunks can only be used with float_vector and float_vector_array attributes near ')'";}i:1;a:3:{s:8:"sphinxql";s:75:"create table t (title text, v float_vector_array chunk_strategy='sentence')";s:5:"errno";i:1064;s:5:"error";s:95:"P03: chunk_strategy, max_tokens, overlap_tokens and max_chunks require knn_type='hnsw' near ')'";}i:2;a:3:{s:8:"sphinxql";s:125:"create table t (title text, v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' chunk_strategy='sentence')";s:5:"errno";i:1064;s:5:"error";s:137:"P03: chunk_strategy, max_tokens, overlap_tokens and max_chunks require model_name; explicitly supplied vectors are never chunked near ')'";}i:3;a:3:{s:8:"sphinxql";s:168:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' max_tokens='128')";s:5:"errno";i:1064;s:5:"error";s:78:"P03: max_tokens, overlap_tokens and max_chunks require chunk_strategy near ')'";}i:4;a:3:{s:8:"sphinxql";s:194:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='truncate' max_tokens='128')";s:5:"errno";i:1064;s:5:"error";s:89:"P03: chunk_strategy='truncate' ignores max_tokens, overlap_tokens and max_chunks near ')'";}i:5;a:3:{s:8:"sphinxql";s:188:"create table t (title text, v float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='sentence' max_tokens='128')";s:5:"errno";i:1064;s:5:"error";s:121:"P03: chunk_strategy='sentence' produces several vectors per document and requires a float_vector_array attribute near ')'";}i:6;a:3:{s:8:"sphinxql";s:194:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' overlap_tokens='16')";s:5:"errno";i:1064;s:5:"error";s:69:"P03: overlap_tokens requires an explicit non-zero max_tokens near ')'";}i:7;a:3:{s:8:"sphinxql";s:209:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='0' overlap_tokens='16')";s:5:"errno";i:1064;s:5:"error";s:69:"P03: overlap_tokens requires an explicit non-zero max_tokens near ')'";}i:8;a:3:{s:8:"sphinxql";s:178:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='paragraph')";s:5:"errno";i:1064;s:5:"error";s:114:"P03: unknown chunk_strategy 'paragraph'; expected truncate, mean, fixed, recursive or sentence near ''paragraph')'";}i:9;a:3:{s:8:"sphinxql";s:191:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='abc')";s:5:"errno";i:1064;s:5:"error";s:50:"P03: invalid number "abc", 0 assumed near ''abc')'";}i:10;a:3:{s:8:"sphinxql";s:190:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='-1')";s:5:"errno";i:1064;s:5:"error";s:66:"P03: max_tokens: '-1' is out of range (0..2147483647) near ''-1')'";}i:11;a:2:{s:8:"sphinxql";s:211:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='100' overlap_tokens='50')";s:14:"total_affected";i:0;}i:12;a:3:{s:8:"sphinxql";s:19:"show create table t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:5:"Table";s:1:"t";s:12:"Create Table";s:241:"CREATE TABLE t ( +id bigint, +title text, +v float_vector_array knn_type='hnsw' hnsw_similarity='L2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' api_timeout='10' chunk_strategy='fixed' max_tokens='100' overlap_tokens='50' +)";}}}i:13;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:14;a:2:{s:8:"sphinxql";s:167:"create table t (title text, v float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean')";s:14:"total_affected";i:0;}i:15;a:3:{s:8:"sphinxql";s:19:"show create table t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:5:"Table";s:1:"t";s:12:"Create Table";s:197:"CREATE TABLE t ( +id bigint, +title text, +v float_vector knn_type='hnsw' hnsw_similarity='L2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' api_timeout='10' chunk_strategy='mean' +)";}}}i:16;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:17;a:2:{s:8:"sphinxql";s:232:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='cosine' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='sentence' max_tokens='64' overlap_tokens='8' max_chunks='16')";s:14:"total_affected";i:0;}i:18;a:3:{s:8:"sphinxql";s:19:"show create table t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:5:"Table";s:1:"t";s:12:"Create Table";s:262:"CREATE TABLE t ( +id bigint, +title text, +v float_vector_array knn_type='hnsw' hnsw_similarity='COSINE' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' api_timeout='10' chunk_strategy='sentence' max_tokens='64' overlap_tokens='8' max_chunks='16' +)";}}}i:19;a:2:{s:8:"sphinxql";s:120:"insert into t (id, title) values (1, 'The cat sat on the mat. Dogs bark loudly at night. Ships cross the ocean slowly.')";s:14:"total_affected";i:1;}i:20;a:2:{s:8:"sphinxql";s:59:"insert into t (id, title) values (2, 'One short sentence.')";s:14:"total_affected";i:1;}i:21;a:2:{s:8:"sphinxql";s:70:"insert into t (id, title, v) values (3, 'Explicitly empty vector', ())";s:14:"total_affected";i:1;}i:22;a:3:{s:8:"sphinxql";s:65:"select id from t where knn(v, 5, 'a dog barking') order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}}}i:23;a:2:{s:8:"sphinxql";s:16:"flush ramchunk t";s:14:"total_affected";i:0;}i:24;a:3:{s:8:"sphinxql";s:65:"select id from t where knn(v, 5, 'a dog barking') order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}}}i:25;a:1:{s:8:"sphinxql";s:83:"/* restart-daemon-no-warnings => stop=ok, return code=0; start=ok, return code=0 */";}i:26;a:3:{s:8:"sphinxql";s:19:"show create table t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:5:"Table";s:1:"t";s:12:"Create Table";s:262:"CREATE TABLE t ( +id bigint, +title text, +v float_vector_array knn_type='hnsw' hnsw_similarity='COSINE' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' api_timeout='10' chunk_strategy='sentence' max_tokens='64' overlap_tokens='8' max_chunks='16' +)";}}}i:27;a:3:{s:8:"sphinxql";s:65:"select id from t where knn(v, 5, 'a dog barking') order by id asc";s:10:"total_rows";i:2;s:4:"rows";a:2:{i:0;a:1:{s:2:"id";s:1:"1";}i:1;a:1:{s:2:"id";s:1:"2";}}}i:28;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:29;a:2:{s:8:"sphinxql";s:34:"create table t (title text, n int)";s:14:"total_affected";i:0;}i:30;a:2:{s:8:"sphinxql";s:40:"insert into t values (1, 'some text', 7)";s:14:"total_affected";i:1;}i:31;a:3:{s:8:"sphinxql";s:147:"alter table t add column v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title'";s:5:"errno";i:1064;s:5:"error";s:168:"table t: attribute 'v': adding a model-backed float_vector_array is not supported yet, since existing rows can not be embedded; create the column with the table instead";}i:32;a:2:{s:8:"sphinxql";s:163:"alter table t add column s float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean'";s:14:"total_affected";i:0;}i:33;a:3:{s:8:"sphinxql";s:19:"show create table t";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:2:{s:5:"Table";s:1:"t";s:12:"Create Table";s:191:"CREATE TABLE t ( +id bigint, +title text, +n integer, +s float_vector knn_type='hnsw' hnsw_similarity='L2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean' +)";}}}i:34;a:3:{s:8:"sphinxql";s:45:"select id from t where knn(s, 5, 'some text')";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:1:{s:2:"id";s:1:"1";}}}i:35;a:2:{s:8:"sphinxql";s:34:"alter table t rebuild embeddings s";s:14:"total_affected";i:0;}i:36;a:3:{s:8:"sphinxql";s:45:"select id from t where knn(s, 5, 'some text')";s:10:"total_rows";i:1;s:4:"rows";a:1:{i:0;a:1:{s:2:"id";s:1:"1";}}}i:37;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}i:38;a:2:{s:8:"sphinxql";s:173:"create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean')";s:14:"total_affected";i:0;}i:39;a:2:{s:8:"sphinxql";s:41:"insert into t values (1, 'some text', ())";s:14:"total_affected";i:1;}i:40;a:3:{s:8:"sphinxql";s:34:"alter table t rebuild embeddings v";s:5:"errno";i:1064;s:5:"error";s:110:"table t: attribute 'v' is a float_vector_array; rebuilding embeddings is not supported for it yet, use REPLACE";}i:41;a:2:{s:8:"sphinxql";s:12:"drop table t";s:14:"total_affected";i:0;}}} \ No newline at end of file diff --git a/test/test_527/test.xml b/test/test_527/test.xml new file mode 100644 index 0000000000..2c441fc6fa --- /dev/null +++ b/test/test_527/test.xml @@ -0,0 +1,88 @@ + + + +chunked auto-embeddings + + + + + + + + + + + +searchd +{ + + data_dir = +} + + + + + + create table t (title text, n int chunk_strategy='sentence'); + create table t (title text, v float_vector_array chunk_strategy='sentence'); + create table t (title text, v float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' chunk_strategy='sentence'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' max_tokens='128'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='truncate' max_tokens='128'); + create table t (title text, v float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='sentence' max_tokens='128'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' overlap_tokens='16'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='0' overlap_tokens='16'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='paragraph'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='abc'); + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='-1'); + + + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='fixed' max_tokens='100' overlap_tokens='50'); + show create table t; + drop table t; + + + create table t (title text, v float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean'); + show create table t; + drop table t; + + + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='cosine' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='sentence' max_tokens='64' overlap_tokens='8' max_chunks='16'); + show create table t; + insert into t (id, title) values (1, 'The cat sat on the mat. Dogs bark loudly at night. Ships cross the ocean slowly.'); + insert into t (id, title) values (2, 'One short sentence.'); + insert into t (id, title, v) values (3, 'Explicitly empty vector', ()); + select id from t where knn(v, 5, 'a dog barking') order by id asc; + flush ramchunk t; + select id from t where knn(v, 5, 'a dog barking') order by id asc; + + + + + + + show create table t; + select id from t where knn(v, 5, 'a dog barking') order by id asc; + drop table t; + + + create table t (title text, n int); + insert into t values (1, 'some text', 7); + alter table t add column v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title'; + + alter table t add column s float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean'; + show create table t; + select id from t where knn(s, 5, 'some text'); + + alter table t rebuild embeddings s; + select id from t where knn(s, 5, 'some text'); + drop table t; + + + create table t (title text, v float_vector_array knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title' chunk_strategy='mean'); + insert into t values (1, 'some text', ()); + alter table t rebuild embeddings v; + drop table t; + + + +