Querying Vectors
Perform similarity search and retrieve vectors using JavaScript SDK or PostgreSQL.
This feature is in alpha
Expect rapid changes, limited features, and possible breaking updates. Share feedback as we refine the experience and expand access.
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.
Comparison to pgvector
Vector buckets and any Foreign Data Wrappers (FDW) they use only support one similarity search algorithm, the <===> distance operator.
Basic similarity search
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient('https://your-project.supabase.co', 'your-service-key')45const index = supabase.storage.vectors.from('embeddings').index('documents-openai')67// Query with a vector embedding8const { data, error } = await index.queryVectors({9 queryVector: {10 float32: [0.1, 0.2, 0.3 /* ... embedding of 1536 dimensions ... */],11 },12 topK: 5,13 returnDistance: true,14 returnMetadata: true,15})1617if (error) {18 console.error('Query failed:', error)19} else {20 // Results are ranked by similarity (lowest distance = most similar)21 data.vectors.forEach((result, rank) => {22 console.log(`${rank + 1}. ${result.metadata?.title}`)23 console.log(` Similarity score: ${result.distance.toFixed(4)}`)24 })25}Semantic search
Find documents similar to a query by embedding the query text:
1import { createClient } from '@supabase/supabase-js'2import OpenAI from 'openai'34const supabase = createClient(...)5const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })67async function semanticSearch(query, topK = 5) {8 // Embed the query9 const queryEmbedding = await openai.embeddings.create({10 model: 'text-embedding-3-small',11 input: query12 })1314 const queryVector = queryEmbedding.data[0].embedding1516 // Search for similar vectors17 const { data, error } = await supabase.storage.vectors18 .from('embeddings')19 .index('documents-openai')20 .queryVectors({21 queryVector: { float32: queryVector },22 topK,23 returnDistance: true,24 returnMetadata: true25 })2627 if (error) {28 throw error29 }3031 return data.vectors.map((result) => ({32 id: result.key,33 title: result.metadata?.title,34 similarity: 1 - result.distance, // Convert distance to similarity (0-1)35 metadata: result.metadata36 }))37}3839// Usage40const results = await semanticSearch('How do I use vector search?')41results.forEach((result) => {42 console.log(`${result.title} (${(result.similarity * 100).toFixed(1)}% similar)`)43})Filtered similarity search
1const index = supabase.storage.vectors2 .from('embeddings')3 .index('documents-openai')45// Search with metadata filter6const { data } = await index.queryVectors({7 queryVector: { float32: [...embedding...] },8 topK: 10,9 filter: {10 // Filter by metadata fields11 category: 'electronics',12 in_stock: true,13 price: { $lte: 500 } // Less than or equal to 50014 },15 returnDistance: true,16 returnMetadata: true17})Retrieving specific vectors
1const index = supabase.storage.vectors.from('embeddings').index('documents-openai')23const { data, error } = await index.getVectors({4 keys: ['doc-1', 'doc-2', 'doc-3'],5 returnData: true,6 returnMetadata: true,7})89if (!error) {10 data.vectors.forEach((vector) => {11 console.log(`${vector.key}: ${vector.metadata?.title}`)12 })13}Listing vectors
1const index = supabase.storage.vectors.from('embeddings').index('documents-openai')23let nextToken = undefined4let pageCount = 056do {7 const { data, error } = await index.listVectors({8 maxResults: 100,9 nextToken,10 returnData: false, // Don't return embeddings for faster response11 returnMetadata: true,12 })1314 if (error) break1516 pageCount++17 console.log(`Page ${pageCount}: ${data.vectors.length} vectors`)1819 data.vectors.forEach((vector) => {20 console.log(` - ${vector.key}: ${vector.metadata?.title}`)21 })2223 nextToken = data.nextToken24} while (nextToken)Hybrid search: Vectors + relational data
Combine similarity search with SQL filtering and joins:
1async function hybridSearch(queryVector, filters) {2 const index = supabase.storage.vectors.from('embeddings').index('documents-openai')34 // Get similar vectors with filters5 const { data: vectorResults } = await index.queryVectors({6 queryVector: { float32: queryVector },7 topK: 100,8 filter: filters,9 returnDistance: true,10 returnMetadata: true,11 })1213 // Get additional details from relational database14 const { data: details } = await supabase15 .from('documents')16 .select('*')17 .in(18 'id',19 vectorResults.vectors.map((v) => v.metadata?.doc_id)20 )2122 // Merge results23 return vectorResults.vectors.map((vector) => {24 const detail = details?.find((d) => d.id === vector.metadata?.doc_id)25 return {26 ...vector,27 ...detail,28 }29 })30}Real-world examples
RAG (retrieval-augmented generation)
1import OpenAI from 'openai'2import { createClient } from '@supabase/supabase-js'34async function retrieveContextForLLM(userQuery) {5 const supabase = createClient(...)6 const openai = new OpenAI()78 // 1. Embed the user query9 const queryEmbedding = await openai.embeddings.create({10 model: 'text-embedding-3-small',11 input: userQuery12 })1314 // 2. Retrieve relevant documents15 const { data: vectorResults } = await supabase.storage.vectors16 .from('embeddings')17 .index('documents-openai')18 .queryVectors({19 queryVector: { float32: queryEmbedding.data[0].embedding },20 topK: 5,21 returnMetadata: true22 })2324 // 3. Use vectors to augment LLM prompt25 const context = vectorResults.vectors26 .map(v => v.metadata?.content || '')27 .join('\n\n')2829 const response = await openai.chat.completions.create({30 model: 'gpt-4',31 messages: [32 {33 role: 'system',34 content: `Use the following context to answer the user's question:\n\n${context}`35 },36 {37 role: 'user',38 content: userQuery39 }40 ]41 })4243 return response.choices[0].message.content44}Product recommendations
1async function recommendProducts(userEmbedding, topK = 5) {2 const supabase = createClient(...)34 // Find similar products5 const { data } = await supabase.storage.vectors6 .from('embeddings')7 .index('products-openai')8 .queryVectors({9 queryVector: { float32: userEmbedding },10 topK,11 filter: {12 in_stock: true13 },14 returnMetadata: true15 })1617 return data.vectors.map((result) => ({18 id: result.metadata?.product_id,19 name: result.metadata?.name,20 price: result.metadata?.price,21 similarity: 1 - result.distance22 }))23}Filtering before similarity search
1// Use metadata filters to reduce search scope2const { data } = await index.queryVectors({3 queryVector,4 topK: 100,5 filter: {6 category: 'electronics', // Pre-filter by category7 },8})