Skip to content
Storage

Querying Vectors

Perform similarity search and retrieve vectors using JavaScript SDK or Postgres.

Vector similarity search finds vectors most similar to a query vector using distance metrics. You can query vectors using the JavaScript SDK or directly from Postgres using SQL.

import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project-id.supabase.co', 'your-service-key')
const index = supabase.storage.vectors.from('embeddings').index('documents-openai')
// Query with a vector embedding
const { data, error } = await index.queryVectors({
queryVector: {
float32: [0.1, 0.2, 0.3 /* ... embedding of 1536 dimensions ... */],
},
topK: 5,
returnDistance: true,
returnMetadata: true,
})
if (error) {
console.error('Query failed:', error)
} else {
// Results are ranked by similarity (lowest distance = most similar)
data.vectors.forEach((result, rank) => {
console.log(`${rank + 1}. ${result.metadata?.title}`)
console.log(` Similarity score: ${result.distance.toFixed(4)}`)
})
}

Retrieve more than 100 results#

Hosted vector buckets return at most 100 query results per response. To retrieve more nearest-neighbor results, set topK to the total number of results you need, up to 10,000, and follow nextToken until the response omits it. Keep the other query parameters unchanged between requests.

import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project-id.supabase.co', 'your-service-key')
const index = supabase.storage.vectors.from('embeddings').index('documents-openai')
const query = {
queryVector: {
float32: [0.1, 0.2, 0.3 /* ... embedding of 1536 dimensions ... */],
},
topK: 1000,
returnDistance: true,
returnMetadata: true,
}
const matches = []
let nextToken: string | undefined
do {
const { data, error } = await index.queryVectors({
...query,
...(nextToken ? { nextToken } : {}),
})
if (error) {
throw error
}
matches.push(...data.vectors)
nextToken = data.nextToken
} while (nextToken)
console.log(`Retrieved ${matches.length} matches`)

Find documents similar to a query by embedding the query text:

import { createClient } from '@supabase/supabase-js'
import OpenAI from 'openai'
const supabase = createClient(...)
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
async function semanticSearch(query, topK = 5) {
// Embed the query
const queryEmbedding = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: query
})
const queryVector = queryEmbedding.data[0].embedding
// Search for similar vectors
const { data, error } = await supabase.storage.vectors
.from('embeddings')
.index('documents-openai')
.queryVectors({
queryVector: { float32: queryVector },
topK,
returnDistance: true,
returnMetadata: true
})
if (error) {
throw error
}
return data.vectors.map((result) => ({
id: result.key,
title: result.metadata?.title,
similarity: 1 - result.distance, // Convert distance to similarity (0-1)
metadata: result.metadata
}))
}
// Usage
const results = await semanticSearch('How do I use vector search?')
results.forEach((result) => {
console.log(`${result.title} (${(result.similarity * 100).toFixed(1)}% similar)`)
})
const index = supabase.storage.vectors
.from('embeddings')
.index('documents-openai')
// Search with metadata filter
const { data } = await index.queryVectors({
queryVector: { float32: [...embedding...] },
topK: 10,
filter: {
// Filter by metadata fields
category: 'electronics',
in_stock: true,
price: { $lte: 500 } // Less than or equal to 500
},
returnDistance: true,
returnMetadata: true
})

Retrieving specific vectors#

const index = supabase.storage.vectors.from('embeddings').index('documents-openai')
const { data, error } = await index.getVectors({
keys: ['doc-1', 'doc-2', 'doc-3'],
returnData: true,
returnMetadata: true,
})
if (!error) {
data.vectors.forEach((vector) => {
console.log(`${vector.key}: ${vector.metadata?.title}`)
})
}

Listing vectors#

const index = supabase.storage.vectors.from('embeddings').index('documents-openai')
let nextToken = undefined
let pageCount = 0
do {
const { data, error } = await index.listVectors({
maxResults: 100,
nextToken,
returnData: false, // Don't return embeddings for faster response
returnMetadata: true,
})
if (error) break
pageCount++
console.log(`Page ${pageCount}: ${data.vectors.length} vectors`)
data.vectors.forEach((vector) => {
console.log(` - ${vector.key}: ${vector.metadata?.title}`)
})
nextToken = data.nextToken
} while (nextToken)

Hybrid search: Vectors + relational data#

Combine similarity search with SQL filtering and joins:

async function hybridSearch(queryVector, filters) {
const index = supabase.storage.vectors.from('embeddings').index('documents-openai')
// Get similar vectors with filters
const { data: vectorResults } = await index.queryVectors({
queryVector: { float32: queryVector },
topK: 100,
filter: filters,
returnDistance: true,
returnMetadata: true,
})
// Get additional details from relational database
const { data: details } = await supabase
.from('documents')
.select('*')
.in(
'id',
vectorResults.vectors.map((v) => v.metadata?.doc_id)
)
// Merge results
return vectorResults.vectors.map((vector) => {
const detail = details?.find((d) => d.id === vector.metadata?.doc_id)
return {
...vector,
...detail,
}
})
}

Real-world examples#

RAG (retrieval-augmented generation)#

import OpenAI from 'openai'
import { createClient } from '@supabase/supabase-js'
async function retrieveContextForLLM(userQuery) {
const supabase = createClient(...)
const openai = new OpenAI()
// 1. Embed the user query
const queryEmbedding = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: userQuery
})
// 2. Retrieve relevant documents
const { data: vectorResults } = await supabase.storage.vectors
.from('embeddings')
.index('documents-openai')
.queryVectors({
queryVector: { float32: queryEmbedding.data[0].embedding },
topK: 5,
returnMetadata: true
})
// 3. Use vectors to augment LLM prompt
const context = vectorResults.vectors
.map(v => v.metadata?.content || '')
.join('\n\n')
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: `Use the following context to answer the user's question:\n\n${context}`
},
{
role: 'user',
content: userQuery
}
]
})
return response.choices[0].message.content
}

Product recommendations#

async function recommendProducts(userEmbedding, topK = 5) {
const supabase = createClient(...)
// Find similar products
const { data } = await supabase.storage.vectors
.from('embeddings')
.index('products-openai')
.queryVectors({
queryVector: { float32: userEmbedding },
topK,
filter: {
in_stock: true
},
returnMetadata: true
})
return data.vectors.map((result) => ({
id: result.metadata?.product_id,
name: result.metadata?.name,
price: result.metadata?.price,
similarity: 1 - result.distance
}))
}
// Use metadata filters to reduce search scope
const { data } = await index.queryVectors({
queryVector,
topK: 100,
filter: {
category: 'electronics', // Pre-filter by category
},
})

Next steps#