基于Redis實現(xiàn)附近商鋪查詢功能
基于Redis實現(xiàn)-附近查詢
這個功能將使用到Redis中的GEO這種數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)。
1.GEO相關(guān)命令
GEO就是Geolocation的簡寫形式,代表地理坐標(biāo)。Redis在3.2版本中加入到了對GEO的支持,允許存儲地理坐標(biāo)信息,幫助我們根據(jù)經(jīng)緯度來檢索數(shù)據(jù),常見命令如下:
- GEOADD: 添加一個地理空間信息,包含:經(jīng)度(longitude)、緯度(latitude)、值(member)
- GEODIST: 計算指定的兩個點之間的距離并返回
- GEOHASH: 將指定member的坐標(biāo)轉(zhuǎn)為hash字符串形式并返回
- GEOPOS: 返回指定member的坐標(biāo)
- GEORADIUS: 指定圓心、半徑,找到該圓內(nèi)包含的所有member,并按照與圓心之間的距離排序后返回。6.2以后已廢棄
- GEOSEARCH: 在指定范圍內(nèi)搜索member,并按照與指定點之間的距離排序后返回。范圍可以是圓形或矩形。6.2.新功能
- GEOSEARCHSTORE: 與GEOSEARCH功能一致,不過可以把結(jié)果存儲到一個指定的key。6.2.新功能
2.使用GEO來實現(xiàn)以下功能
添加下面幾條數(shù)據(jù):
- 北京南站(116.378248 39.865275)
- 北京站(116.42803 39.903738)
- 北京西站(116.322287 39.893729)
# 1. 添加地理空間數(shù)據(jù) GEOADD stations 116.378248 39.865275 "北京南站" 116.42803 39.903738 "北京站" 116.322287 39.893729 "北京西站"
計算北京西站到北京站的距離
# 2. 計算北京西站到北京站的距離 GEODIST stations "北京西站" "北京站" km
搜索天安門(116.397904 39.909005)附近10km內(nèi)的所有火車站,并按照距離升序排序
# 3. 搜索天安門附近10km內(nèi)的火車站并按距離排序 GEOSEARCH stations FROMLONLAT 116.397904 39.909005 BYRADIUS 10 km ASC
3.使用Java實現(xiàn)簡單的附近商鋪查詢
//在ServiceImpl中(簡單演示) @Autowired private StringRedisTemplate stringRedisTemplate; @Override public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) { // 1. 判斷是否需要基于地理位置查詢 if (x == null || y == null) { // 不需要地理坐標(biāo)查詢時,直接按類型分頁查詢數(shù)據(jù)庫 Page<Shop> page = query() .eq("type_id", typeId) // 按店鋪類型篩選 .page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE)); // 分頁查詢 // 返回查詢結(jié)果 return Result.ok(page.getRecords()); } // 2. 需要地理查詢時,計算分頁參數(shù) int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE; // 起始偏移量 int end = current * SystemConstants.DEFAULT_PAGE_SIZE; // 結(jié)束位置 // 3. 從Redis中查詢附近店鋪ID(GEO查詢) String key = "shop:geo:" + typeId; // GEO數(shù)據(jù)存儲的key // 執(zhí)行GEO搜索:以(x,y)為中心,5000米半徑范圍內(nèi),查詢end數(shù)量的店鋪 GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search( key, GeoReference.fromCoordinate(x, y), // 中心點坐標(biāo) new Distance(5000), // 搜索半徑(5公里) RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs() .includeDistance() // 包含距離信息 .limit(end) // 限制返回數(shù)量 ); // 4. 處理查詢結(jié)果并獲取店鋪詳情 if (results == null) { return Result.ok(Collections.emptyList()); // 無結(jié)果時返回空列表 } List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent(); if (list.size() <= from) { return Result.ok(Collections.emptyList()); // 結(jié)果不足時分頁返回空 } // 收集店鋪ID和距離信息 List<Long> ids = new ArrayList<>(list.size()); Map<String, Distance> distanceMap = new HashMap<>(list.size()); // 跳過前from條記錄(分頁處理),然后處理剩余記錄 list.stream().skip(from).forEach(result -> { String id = result.getContent().getName(); // 獲取店鋪ID ids.add(Long.valueOf(id)); distanceMap.put(id, result.getDistance()); // 存儲店鋪距離 }); // 根據(jù)ID批量查詢店鋪詳情(保持ID順序) String strIds = StrUtil.join(",", ids); List<Shop> shops = query() .in("id", ids) // 按ID列表查詢 .last("ORDER BY FIELD(id," + strIds + ")") // 保持Redis返回的順序 .list(); // 為每個店鋪設(shè)置距離信息 for (Shop shop : shops) { shop.setDistance(distanceMap.get(shop.getId().toString()).getValue()); } // 5. 返回帶距離信息的店鋪列表 return Result.ok(shops); }
4.Redis GEO search 方法的參數(shù)
排序方式
.sortAscending() // 按距離升序排序(從近到遠(yuǎn)) .sortDescending() // 按距離降序排序(從遠(yuǎn)到近)
返回內(nèi)容控制
.includeDistance() // 在結(jié)果中包含距離信息 .includeCoordinates() // 在結(jié)果中包含坐標(biāo)信息 .includeName() // 在結(jié)果中包含成員名稱(默認(rèn)包含)
結(jié)果限制
.limit(50) // 限制返回結(jié)果數(shù)量(可用于簡單分頁)
單位設(shè)置
.includeDistance().withDistance(Metrics.KILOMETERS) // 指定距離單位 //支持的單位: //Metrics.KILOMETERS(千米) //Metrics.MILES(英里) //Metrics.FEET(英尺) //Metrics.METERS(米)
GeoReference 的三種主要形式
//1.fromCoordinate(x, y) //作用:通過經(jīng)緯度坐標(biāo)指定中心點 //示例: GeoReference.fromCoordinate(116.404, 39.915) // 北京天安門坐標(biāo) //2.fromMember(memberName) //作用:通過 Redis中已存儲的GEO成員名稱指定中心點 //示例: GeoReference.fromMember("北京站") // 以已存儲的"北京站"坐標(biāo)為中心 //3.fromString("x,y") //作用:通過字符串格式的坐標(biāo)指定中心點 //示例: GeoReference.fromString("116.404,39.915")
如下為完整示例:
GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search( "shop:geo:1", GeoReference.fromCoordinate(116.397904, 39.909005), new Distance(5, Metrics.KILOMETERS), RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs() .includeDistance() // 包含距離 .includeCoordinates() // 包含坐標(biāo) .sortAscending() // 按距離升序 .limit(100) // 最多返回100條 .withDistance(Metrics.KILOMETERS) // 距離單位為千米 );
對于 Redis 6.2 及以上版本,還可以使用:
矩形范圍搜索
.byBox(width, height, Metrics.KILOMETERS) // 矩形范圍搜索
存儲搜索結(jié)果
.store("result-key") // 將結(jié)果存儲到指定key .storeDist("result-key") // 存儲帶距離的結(jié)果
到此這篇關(guān)于基于Redis實現(xiàn)-附近商鋪查詢的文章就介紹到這了,更多相關(guān)Redis附近商鋪查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
redis的key出現(xiàn)的\xac\xed\x00\x05t\x00亂碼問題及解決
這篇文章主要介紹了redis的key出現(xiàn)的\xac\xed\x00\x05t\x00亂碼問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09