You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:45:02