如何在Kysely/PostgreSQL中结合pgvector实现向量嵌入查询?
解决Kysely查询pgvector时的运算符错误及相似度阈值设置问题
修复<=>运算符错误的Kysely写法
你遇到的operator does not exist: double precision <=> unknown错误,核心原因是Kysely未将输入的查询向量识别为pgvector的vector类型,导致运算符匹配失败。只需明确转换向量类型即可解决,以下是两种可靠写法:
写法一:参数化类型转换(推荐,避免SQL注入)
import { sql } from 'kysely' // 你的查询向量数组 const queryEmbedding = [0.123, 0.456, ...]; const matchedDefinitions = await db.selectFrom('dictionary') .select([ 'word', 'definition', sql`embedding <=> $1::vector`.as('similarity') ]) // 这里手动设置相似度阈值(<=>返回的是距离,值越小越相似) .where(sql`embedding <=> $1::vector`, '<', 0.5) .orderBy(sql`embedding <=> $1::vector`) .bind(queryEmbedding) .execute()
写法二:直接构造向量字面量
如果查询向量是完全可控的(无SQL注入风险),也可以直接拼接成pgvector的字面量格式:
const matchedDefinitions = await db.selectFrom('dictionary') .select([ 'word', 'definition', sql`embedding <=> '${JSON.stringify(queryEmbedding)}'::vector`.as('similarity') ]) .where(sql`embedding <=> '${JSON.stringify(queryEmbedding)}'::vector`, '<', 0.5) .orderBy(sql`embedding <=> '${JSON.stringify(queryEmbedding)}'::vector`) .execute()
自动设置相似度阈值
手动设定阈值不够灵活,可根据数据分布动态调整,两种实用方案:
方案一:先取Top K再过滤
先拉取前N个最相似的结果,再基于它们的相似度分布确定阈值:
const topK = 50; // 先获取前50个最相似的结果 const tempResults = await db.selectFrom('dictionary') .select([ 'word', 'definition', sql`embedding <=> $1::vector`.as('similarity') ]) .orderBy(sql`embedding <=> $1::vector`) .limit(topK) .bind(queryEmbedding) .execute() // 计算阈值:比如取前20%结果的最低相似度,只保留最相似的一批 const similarities = tempResults.map(item => parseFloat(item.similarity)); const threshold = similarities[Math.floor(similarities.length * 0.2)]; // 过滤出符合阈值的结果 const finalResults = tempResults.filter(item => parseFloat(item.similarity) <= threshold);
方案二:用SQL统计函数动态计算阈值
直接在SQL中用百分位函数自动生成阈值,无需前端额外处理:
const finalResults = await db.selectFrom('dictionary') .select([ 'word', 'definition', sql`embedding <=> $1::vector`.as('similarity') ]) // 取所有结果中相似度的第20百分位作为阈值,只保留更相似的结果 .where(sql`embedding <=> $1::vector <= PERCENTILE_CONT(0.2) WITHIN GROUP (ORDER BY embedding <=> $1::vector)`) .orderBy(sql`embedding <=> $1::vector`) .bind(queryEmbedding) .execute()
可选:封装向量查询辅助函数
如果频繁用到向量查询,可封装工具函数减少重复代码:
import { sql } from 'kysely' function vectorSimilarity(columnName: string, embedding: number[]) { return sql`${sql.ref(columnName)} <=> $1::vector`; } // 使用示例 const results = await db.selectFrom('dictionary') .select([ 'word', 'definition', vectorSimilarity('embedding', queryEmbedding).as('similarity') ]) .where(vectorSimilarity('embedding', queryEmbedding), '<', 0.5) .orderBy(vectorSimilarity('embedding', queryEmbedding)) .bind(queryEmbedding) .execute()
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

