如何用JavaScript(ioredis)实现Azure Redis向量余弦相似度搜索?
使用ioredis实现Azure Redis缓存的向量余弦相似度搜索
首先要确保你的Azure Redis缓存实例是Enterprise层级,并且启用了Redis Stack(包含Redisearch和RedisJSON模块)——这是实现向量搜索的基础,因为原生Redis不支持向量索引与相似度计算。
1. 安装依赖
npm install ioredis mathjs
ioredis用于和Redis交互,mathjs可选,用于客户端侧的余弦相似度计算(如果需要)。
2. 连接Azure Redis缓存
const Redis = require('ioredis'); // 从Azure门户获取你的Redis连接字符串 const redis = new Redis({ host: 'your-redis-host.redis.cache.windows.net', port: 6380, password: 'your-redis-password', tls: true // Azure Redis强制要求TLS });
3. 创建向量索引
先创建包含向量字段的搜索索引,这里假设我们存储的是文档+向量数据:
async function createVectorIndex() { // 索引名称:doc_index,定义向量字段为content_vector(维度假设为1536,适配常见大模型输出) await redis.call('FT.CREATE', 'doc_index', 'ON', 'JSON', 'PREFIX', '1', 'doc:', 'SCHEMA', '$.content_vector', 'AS', 'content_vector', 'VECTOR', 'FLAT', '6', 'TYPE', 'FLOAT32', 'DIM', '1536', 'DISTANCE_METRIC', 'COSINE'); console.log('向量索引创建成功'); } createVectorIndex().catch(console.error);
DISTANCE_METRIC设置为COSINE,指定用余弦相似度计算距离DIM要和你实际使用的向量维度一致(比如OpenAI的text-embedding-ada-002是1536)
4. 插入带向量的数据
async function insertDocument(docId, content, vector) { // 存储为JSON格式,键前缀为doc:,对应索引的PREFIX设置 await redis.json.set(`doc:${docId}`, '$', { content: content, content_vector: vector }); console.log(`文档${docId}插入成功`); } // 示例:插入一个测试文档(vector是1536维的float32数组) const testVector = new Float32Array(1536).fill(0.1); // 替换为你的真实向量 insertDocument('1', '测试文档内容', testVector).catch(console.error);
5. 执行余弦相似度搜索(KNN)
用Redis的FT.SEARCH命令执行最近邻搜索,返回与目标向量最相似的Top N结果:
async function searchSimilarVectors(targetVector, topN = 5) { // 将目标向量转换为Redis接受的二进制格式(FLOAT32) const vectorBuffer = Buffer.from(targetVector.buffer); // 执行KNN搜索,使用COSINE距离 const results = await redis.call('FT.SEARCH', 'doc_index', '*=>[KNN $topN @content_vector $query_vector AS distance]', 'PARAMS', '2', 'topN', topN, 'query_vector', vectorBuffer, 'RETURN', '2', '$.content', 'distance', 'SORTBY', 'distance', 'ASC', 'LIMIT', '0', topN); // 格式化结果 const formattedResults = []; for (let i = 1; i < results.length; i += 2) { const docKey = results[i]; const docData = results[i+1]; formattedResults.push({ docId: docKey.split(':')[1], content: docData['$.content'], cosineSimilarity: 1 - parseFloat(docData.distance) // 余弦距离转相似度(距离范围0-2,相似度=1-距离) }); } return formattedResults; } // 示例:搜索与测试向量相似的文档 searchSimilarVectors(testVector, 3).then(results => { console.log('相似文档结果:', results); }).catch(console.error);
注意:Redis返回的是余弦距离(范围0到2),所以相似度需要转换为1 - 距离(范围0到1,值越大越相似)。
备选:客户端侧计算余弦相似度
如果你的Redis实例不支持Redis Stack,也可以先拉取所有向量到客户端,再计算余弦相似度:
const { dot, norm } = require('mathjs'); function cosineSimilarity(vecA, vecB) { return dot(vecA, vecB) / (norm(vecA) * norm(vecB)); } async function searchSimilarClientSide(targetVector) { // 拉取所有文档向量 const docKeys = await redis.keys('doc:*'); const docs = await Promise.all(docKeys.map(key => redis.json.get(key, '$'))); // 计算每个文档与目标向量的相似度并排序 const results = docs.map((doc, index) => ({ docId: docKeys[index].split(':')[1], content: doc[0].content, cosineSimilarity: cosineSimilarity(Array.from(doc[0].content_vector), Array.from(targetVector)) })).sort((a, b) => b.cosineSimilarity - a.cosineSimilarity); return results.slice(0, 5); // 返回Top5 }
这种方法适合数据量小的场景,数据量大时性能很差,推荐优先使用Redis Stack的原生搜索。
内容的提问来源于stack exchange,提问作者MSTr
相关产品推荐
相关产品推荐

