基于redis实现-附近查询
这个功能将使用到redis中的geo这种数据结构来实现。
1.geo相关命令
geo就是geolocation的简写形式,代表地理坐标。redis在3.2版本中加入到了对geo的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据,常见命令如下:
- geoadd: 添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)
- geodist: 计算指定的两个点之间的距离并返回
- geohash: 将指定member的坐标转为hash字符串形式并返回
- geopos: 返回指定member的坐标
- georadius: 指定圆心、半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.2以后已废弃
- geosearch: 在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2.新功能
- geosearchstore: 与geosearch功能一致,不过可以把结果存储到一个指定的key。6.2.新功能
2.使用geo来实现以下功能
添加下面几条数据:
- 北京南站(116.378248 39.865275)
- 北京站(116.42803 39.903738)
- 北京西站(116.322287 39.893729)
# 1. 添加地理空间数据 geoadd stations 116.378248 39.865275 "北京南站" 116.42803 39.903738 "北京站" 116.322287 39.893729 "北京西站"
计算北京西站到北京站的距离
# 2. 计算北京西站到北京站的距离 geodist stations "北京西站" "北京站" km
搜索天安门(116.397904 39.909005)附近10km内的所有火车站,并按照距离升序排序
# 3. 搜索天安门附近10km内的火车站并按距离排序 geosearch stations fromlonlat 116.397904 39.909005 byradius 10 km asc
3.使用java实现简单的附近商铺查询
//在serviceimpl中(简单演示) @autowired private stringredistemplate stringredistemplate; @override public result queryshopbytype(integer typeid, integer current, double x, double y) { // 1. 判断是否需要基于地理位置查询 if (x == null || y == null) { // 不需要地理坐标查询时,直接按类型分页查询数据库 page<shop> page = query() .eq("type_id", typeid) // 按店铺类型筛选 .page(new page<>(current, systemconstants.default_page_size)); // 分页查询 // 返回查询结果 return result.ok(page.getrecords()); } // 2. 需要地理查询时,计算分页参数 int from = (current - 1) * systemconstants.default_page_size; // 起始偏移量 int end = current * systemconstants.default_page_size; // 结束位置 // 3. 从redis中查询附近店铺id(geo查询) string key = "shop:geo:" + typeid; // geo数据存储的key // 执行geo搜索:以(x,y)为中心,5000米半径范围内,查询end数量的店铺 georesults<redisgeocommands.geolocation<string>> results = stringredistemplate.opsforgeo().search( key, georeference.fromcoordinate(x, y), // 中心点坐标 new distance(5000), // 搜索半径(5公里) redisgeocommands.geosearchcommandargs.newgeosearchargs() .includedistance() // 包含距离信息 .limit(end) // 限制返回数量 ); // 4. 处理查询结果并获取店铺详情 if (results == null) { return result.ok(collections.emptylist()); // 无结果时返回空列表 } list<georesult<redisgeocommands.geolocation<string>>> list = results.getcontent(); if (list.size() <= from) { return result.ok(collections.emptylist()); // 结果不足时分页返回空 } // 收集店铺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()); // 存储店铺距离 }); // 根据id批量查询店铺详情(保持id顺序) string strids = strutil.join(",", ids); list<shop> shops = query() .in("id", ids) // 按id列表查询 .last("order by field(id," + strids + ")") // 保持redis返回的顺序 .list(); // 为每个店铺设置距离信息 for (shop shop : shops) { shop.setdistance(distancemap.get(shop.getid().tostring()).getvalue()); } // 5. 返回带距离信息的店铺列表 return result.ok(shops); }
4.redis geo search 方法的参数
排序方式
.sortascending() // 按距离升序排序(从近到远) .sortdescending() // 按距离降序排序(从远到近)
返回内容控制
.includedistance() // 在结果中包含距离信息 .includecoordinates() // 在结果中包含坐标信息 .includename() // 在结果中包含成员名称(默认包含)
结果限制
.limit(50) // 限制返回结果数量(可用于简单分页)
单位设置
.includedistance().withdistance(metrics.kilometers) // 指定距离单位 //支持的单位: //metrics.kilometers(千米) //metrics.miles(英里) //metrics.feet(英尺) //metrics.meters(米)
georeference 的三种主要形式
//1.fromcoordinate(x, y) //作用:通过经纬度坐标指定中心点 //示例: georeference.fromcoordinate(116.404, 39.915) // 北京天安门坐标 //2.frommember(membername) //作用:通过 redis中已存储的geo成员名称指定中心点 //示例: georeference.frommember("北京站") // 以已存储的"北京站"坐标为中心 //3.fromstring("x,y") //作用:通过字符串格式的坐标指定中心点 //示例: 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() // 包含坐标 .sortascending() // 按距离升序 .limit(100) // 最多返回100条 .withdistance(metrics.kilometers) // 距离单位为千米 );
对于 redis 6.2 及以上版本,还可以使用:
矩形范围搜索
.bybox(width, height, metrics.kilometers) // 矩形范围搜索
存储搜索结果
.store("result-key") // 将结果存储到指定key .storedist("result-key") // 存储带距离的结果
到此这篇关于基于redis实现-附近商铺查询的文章就介绍到这了,更多相关redis附近商铺查询内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论