如何用Node.js查询Redis多边形内地理位置并解决addCommand错误?
问题翻译
我在Redis 7.2.4中通过GEOADD locations -46.63803300 (经度) -23.53523600 (纬度) "270353275" (ID)格式存储了多个地理位置,现在需要在Node.js v13.5.0中查询给定多边形范围内的地理位置ID。输入的多边形顶点格式为:
"bounds": [ { "lat": -23.534717280387206, "lng": -46.602115631103516 }, { "lat": -23.548251460987665, "lng": -46.623573303222656 }, ... ]
我尝试用redis-redisearch(^1.0.1)搭配ioredis(v4.28.5)实现,代码如下:
const redis = await getRedis() // 返回新的Redis客户端实例 redisearch(redis) const results = await redis.ft_search( "announces", `@location:{${polygon_query}}`, "RETURN", "1", "@location" )
但收到错误提示redis.addCommand is not a function,请问该如何实现需求?
解决方案
一、报错原因分析
redis-redisearch库与ioredis v4的API兼容性不佳,该库期望的是旧版官方Redis客户端(node-redis)的命令扩展方式,导致addCommand方法调用失败。推荐改用官方维护的RedisSearch客户端库来解决兼容问题。
二、具体实现步骤
1. 替换依赖库
卸载旧的redis-redisearch,安装官方Redis客户端及Search模块:
npm uninstall redis-redisearch npm install @redis/client @redis/search
2. 重构代码
const { createClient } = require('@redis/client'); const { Search } = require('@redis/search'); // 获取Redis客户端 async function getRedis() { const client = createClient({ // 填入你的Redis连接配置,例如: // host: '127.0.0.1', // port: 6379 }); await client.connect(); return client; } // 查询多边形范围内的地理位置ID async function queryPolygonInLocations(bounds) { const redis = await getRedis(); const search = new Search(redis); // 将多边形顶点转换为RedisSearch要求的格式:POLYGON(经度1 纬度1, 经度2 纬度2, ...) const polygonPoints = bounds.map(coord => `${coord.lng} ${coord.lat}`).join(', '); const polygonQuery = `POLYGON(${polygonPoints})`; try { // 执行搜索,返回文档ID(即存储的地理位置ID) const searchResult = await search.search('announces', `@location:${polygonQuery}`, { RETURN: [] // 不返回字段,只取文档ID;若ID存在于字段中,可改为['@id'] }); // 提取结果中的地理位置ID const locationIds = searchResult.documents.map(doc => doc.id); return locationIds; } catch (err) { console.error('查询失败:', err); throw err; } finally { await redis.disconnect(); } }
3. 确保RedisSearch索引正确创建
RedisSearch需要预先创建对应索引才能实现地理查询,若你是用哈希结构存储地理位置(推荐方式),执行以下Redis命令创建索引:
# 先存储单个地理位置示例(替换为你的数据) HSET location:270353275 lon -46.638033 lat -23.535236 # 创建索引,将lon和lat字段组合为GEO类型的location字段 FT.CREATE announces ON HASH PREFIX 1 "location:" SCHEMA lon NUMERIC lat NUMERIC location GEOFIELD lon lat
三、备选方案(不依赖RedisSearch)
如果数据量较小,可先通过GEOHASH或GEORADIUSBYMEMBER获取候选地理位置,再在Node.js中用点-in-多边形算法过滤:
// 点是否在多边形内的判断函数 function pointInPolygon(point, polygon) { let inside = false; const [x, y] = [point.lng, point.lat]; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const [xi, yi] = [polygon[i].lng, polygon[i].lat]; const [xj, yj] = [polygon[j].lng, polygon[j].lat]; const intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) inside = !inside; } return inside; } // 查询逻辑示例 async function queryWithLocalFilter() { const redis = await getRedis(); // 获取所有地理位置成员和坐标 const allLocations = await redis.zrange('locations', 0, -1, 'WITHSCORES'); const locationCoords = []; for (let i = 0; i < allLocations.length; i += 2) { const id = allLocations[i]; const score = allLocations[i+1]; // 解析GEO分数为经纬度 const [lon, lat] = redis.geoDecode(score); locationCoords.push({ id, lon, lat }); } // 过滤出多边形内的ID const filteredIds = locationCoords.filter(item => pointInPolygon(item, bounds)).map(item => item.id); return filteredIds; }
内容的提问来源于stack exchange,提问作者luccassaliba
相关产品推荐
相关产品推荐

