Semantic Search
UQL supports vector similarity search natively, on PostgreSQL (pgvector), CockroachDB, MariaDB, SQLite (sqlite-vec), and MongoDB Atlas ($vectorSearch). The same $vector query works on all of them. This page is the operator reference; for an end-to-end walkthrough (ingestion, fullstack usage, RAG thresholds), see AI & RAG.
Entity Setup
Section titled “Entity Setup”Define a vector field with type: 'vector' and dimensions. Optionally, add a vector index for efficient approximate nearest-neighbor (ANN) search.
import { Entity, Id, Field, Index } from 'uql-orm';
@Entity()@Index(['embedding'], { type: 'hnsw', distance: 'cosine', m: 16, efConstruction: 64 })export class Article { @Id() id?: number; @Field() title?: string; @Field() category?: string;
@Field({ type: 'vector', dimensions: 1536 }) embedding?: number[];}Query by Similarity
Section titled “Query by Similarity”Use $sort on a vector field with $vector and an optional $distance metric:
const results = await querier.findMany(Article, { $select: { id: true, title: true }, $sort: { embedding: { $vector: queryEmbedding, $distance: 'cosine' } }, $limit: 10,});SELECT "id", "title" FROM "Article"ORDER BY "embedding" <=> $1::vectorLIMIT 10SELECT `id`, `title` FROM `Article`ORDER BY VEC_DISTANCE_COSINE(`embedding`, ?)LIMIT 10SELECT `id`, `title` FROM `Article`ORDER BY vec_distance_cosine(`embedding`, ?)LIMIT 10For MongoDB, UQL translates into a $vectorSearch aggregation pipeline:
[ { "$vectorSearch": { "index": "embedding_index", "path": "embedding", "queryVector": [/* queryEmbedding */], "numCandidates": 100, "limit": 10 } }]Combined with Filtering
Section titled “Combined with Filtering”Vector search composes naturally with $where and regular $sort fields:
const results = await querier.findMany(Article, { $where: { category: 'science' }, $sort: { embedding: { $vector: queryVec, $distance: 'cosine' }, title: 'asc' }, $limit: 10,});SELECT * FROM "Article"WHERE "category" = $1ORDER BY "embedding" <=> $2::vector, "title" ASCLIMIT 10SELECT * FROM `Article`WHERE `category` = ?ORDER BY VEC_DISTANCE_COSINE(`embedding`, ?), `title` ASCLIMIT 10SELECT * FROM `Article`WHERE `category` = ?ORDER BY vec_distance_cosine(`embedding`, ?), `title` ASCLIMIT 10For MongoDB, $where is merged into the $vectorSearch.filter for optimal pre-filtering, and secondary sorts become a separate $sort stage:
[ { "$vectorSearch": { "index": "embedding_index", "path": "embedding", "queryVector": [/* queryVec */], "numCandidates": 100, "limit": 10, "filter": { "category": "science" } } }, { "$sort": { "title": 1 } }]Distance Metrics
Section titled “Distance Metrics”| Metric | Postgres Operator | CockroachDB Operator | MariaDB Function | SQLite Function | MongoDB Atlas | Use Case |
|---|---|---|---|---|---|---|
cosine |
<=> |
<=> |
VEC_DISTANCE_COSINE |
vec_distance_cosine |
✅ (index-defined) | Text embeddings (OpenAI, Cohere) |
l2 |
<-> |
<-> |
VEC_DISTANCE_EUCLIDEAN |
vec_distance_L2 |
✅ (index-defined) | Image search, spatial data |
inner |
<#> |
<#> |
❌ | ❌ | ✅ (index-defined) | Maximum inner product |
l1 |
<+> |
❌ | ❌ | ❌ | ❌ | Manhattan distance |
hamming |
<~> |
❌ | ❌ | vec_distance_hamming |
❌ | Binary embeddings |
If omitted, $distance defaults to 'cosine'. You can also set a default per-field:
@Field({ type: 'vector', dimensions: 1536, distance: 'l2' })embedding?: number[];Queries on this field will use l2 unless overridden with $distance at query time.
Distance Projection
Section titled “Distance Projection”Project the computed distance as a named field in the result with $project:
import type { WithDistance } from 'uql-orm';
const results = (await querier.findMany(Article, { $select: { id: true, title: true }, $sort: { embedding: { $vector: queryVec, $distance: 'cosine', $project: 'distance' } }, $limit: 10,})) as WithDistance<Article, 'distance'>[];
results.forEach((r) => console.log(r.title, r.distance));SELECT "id", "title", "embedding" <=> $1::vector AS "distance" FROM "Article"ORDER BY "distance"LIMIT 10SELECT `id`, `title`, VEC_DISTANCE_COSINE(`embedding`, ?) AS `distance` FROM `Article`ORDER BY `distance`LIMIT 10SELECT `id`, `title`, vec_distance_cosine(`embedding`, ?) AS `distance` FROM `Article`ORDER BY `distance`LIMIT 10For MongoDB, $project adds a $meta: 'vectorSearchScore' projection:
[ { "$vectorSearch": { "index": "embedding_index", "path": "embedding", "queryVector": ["..."], "numCandidates": 100, "limit": 10 } }, { "$project": { "id": true, "title": true, "distance": { "$meta": "vectorSearchScore" } } }]Vector Types
Section titled “Vector Types”UQL supports three vector storage types; use the one that best fits your model and performance needs:
| Type | SQL (Postgres) | SQL (CockroachDB) | Storage | Max Dimensions | Use Case |
|---|---|---|---|---|---|
'vector' |
VECTOR(n) |
VECTOR(n) |
32-bit float | 2,000 | Standard embeddings (OpenAI, etc.) |
'halfvec' |
HALFVEC(n) |
VECTOR(n) |
16-bit float | 4,000 | 50% storage savings, near-identical accuracy |
'sparsevec' |
SPARSEVEC(n) |
VECTOR(n) |
Sparse | 1,000,000 | SPLADE, BM25-style sparse retrieval |
@Field({ type: 'vector', dimensions: 1536 }) // OpenAI ada-002embedding?: number[];
@Field({ type: 'halfvec', dimensions: 1536 }) // Same model, half storageembedding?: number[];
@Field({ type: 'sparsevec', dimensions: 30000 }) // SPLADE sparsesparseEmbedding?: number[];Vector Indexes
Section titled “Vector Indexes”Define vector indexes with @Index() for efficient approximate nearest-neighbor (ANN) search:
| Index Type | Postgres | CockroachDB | MariaDB | MongoDB Atlas | Notes |
|---|---|---|---|---|---|
hnsw |
✅ USING hnsw with operator classes |
❌ | ❌ | ❌ | Best accuracy, higher memory |
ivfflat |
✅ USING ivfflat with lists param |
❌ | ❌ | ❌ | Faster build, large datasets |
vector |
❌ | ✅ Native CREATE VECTOR INDEX |
✅ Inline VECTOR INDEX |
❌ | CockroachDB’s and MariaDB’s own native vector index |
vectorSearch |
❌ | ❌ | ❌ | ✅ Atlas vector search index | MongoDB’s managed ANN index |
@Index(['embedding'], { type: 'hnsw', distance: 'cosine', m: 16, efConstruction: 64 })@Index(['embedding'], { type: 'ivfflat', distance: 'l2', lists: 100 })@Index(['embedding'], { type: 'vector', distance: 'cosine' })@Index(['embedding'], { type: 'vector', distance: 'cosine', m: 8 })@Index(['embedding'], { type: 'vectorSearch', name: 'my_search_index' })