目錄
?
如何將地理位置數據保存到 Redis 中以支持范圍查詢
Redis 中的 GEO 類型是什么?
如何保存 GEO 數據到 Redis?
分段解釋:?
RedisKey.POSTS_ANIMALS_LOCATIONS
new Point(longitude, latitude)
?
如何進行范圍搜索
Redis GEO 范圍搜索核心語句
1.redisTemplate.opsForGeo().search(...)
2. RedisKey.POSTS_ANIMALS_LOCATIONS
3. GeoReference.fromCoordinate(x, y)
4. new Distance(5000)
5. RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs()
6. .includeDistance()
搜索結果判空
提取 Redis 搜索結果?
?
如何將地理位置數據保存到 Redis 中以支持范圍查詢
Redis 中的 GEO 類型是什么?
Redis 提供了 GEO
命令支持地理位置信息的存儲和查詢,底層使用 Geohash 編碼 + Sorted Set(有序集合) 實現。你可以對其進行:
-
添加坐標(經緯度);
-
查詢指定位置一定范圍內的成員;
-
獲取兩個成員之間的距離;
-
查詢某個成員的位置等。
如何保存 GEO 數據到 Redis?
redisTemplate.opsForGeo().add(RedisKey.POSTS_ANIMALS_LOCATIONS, // Redis 中的 key,用來存所有帖子的位置new Point(animalsRescueDTO.getLongitude(), animalsRescueDTO.getLatitude()), // 經緯度(Point 構造順序:經度, 緯度)posts.getPostId().toString() // 這個帖子的位置用帖子 ID 作為唯一標識(member)
);
?
👇 實際等價于 Redis 命令:?
GEOADD POSTS_ANIMALS_LOCATIONS 113.1234 23.5678 "123"
-
POSTS_ANIMALS_LOCATIONS
是存儲地理數據的 Redis 鍵 -
113.1234
是經度(longitude) -
23.5678
是緯度(latitude) -
"123"
是帖子 ID(即你posts.getPostId()
)
分段解釋:?
redisTemplate.opsForGeo()
這是 Spring Data Redis 提供的模板方法,用于執行 GEO 類型操作 的入口。
它返回的是一個 GeoOperations<K, V>
對象,可以執行下面這些操作:
方法 | 功能 |
---|---|
add(...) | 添加地理位置點(經度、緯度 + 名稱) |
search(...) | 按照范圍搜索附近的點 |
distance(...) | 計算兩個點之間的距離 |
position(...) | 獲取某個 member 的坐標 |
hash(...) | 獲取某個 member 的 geohash 值 |
RedisKey.POSTS_ANIMALS_LOCATIONS
這是一個 Redis 的 Key,表示你所有帖子位置都保存在這個 key 下的 GEO 類型中。
?
new Point(longitude, latitude)
這是一個 Spring 提供的 org.springframework.data.geo.Point
類型,用于封裝經緯度。
?? 注意順序:
Point(x, y)
中,x 是經度,y 是緯度
?
Redis 是使用底層的 ZSet + Geohash 來實現這個數據結構的:
-
每個點會被編碼成 geohash,對應 ZSet 的
score
-
可以用
ZRANGE
、GEORADIUS
等方式高效查詢范圍
?
如何進行范圍搜索
Redis GEO 范圍搜索核心語句
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().search(RedisKey.POSTS_ANIMALS_LOCATIONS, // Redis 中 GEO 數據所在的 keyGeoReference.fromCoordinate(x, y), // 查詢圓心:經度 x,緯度 ynew Distance(5000), // 查詢半徑:5000 米(5 公里)RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance() // 查詢結果中返回每個點的距離
);
參數 | 類型 | 說明 |
---|
key | String | Redis 中存儲 GEO 數據的 key,也就是你存儲地理數據時使用的那個 key。例如你之前可能使用 RedisKey.POSTS_ANIMALS_LOCATIONS 存儲了所有帖子的地理位置信息。 |
geoReference | GeoReference | 查詢圓心坐標(經緯度)。這代表你要查詢的中心點的位置。 |
distance | Distance | 查詢的范圍半徑,單位是米(m ),表示你想要查詢中心點 geoReference 周圍多少米以內的所有地理位置。 |
args | GeoSearchCommandArgs | 查詢參數的設置,可以設置排序、返回距離等額外選項。 |
?
1.redisTemplate.opsForGeo().search(...)
-
opsForGeo()
:Spring Data Redis 提供的 GEO 操作 API -
search(...)
:封裝了 Redis 6.2+ 的GEOSEARCH
命令,是范圍查找的入口
2. RedisKey.POSTS_ANIMALS_LOCATIONS
-
這是 Redis 中用于保存 GEO 數據的鍵名(key)
-
對應 Redis 的底層命令示例:
?
3. GeoReference.fromCoordinate(x, y)
這是查詢的中心點:
GeoReference.fromCoordinate(double longitude, double latitude)
注意順序是:
經度在前(x),緯度在后(y)
這個代表:
“我現在的位置是 (經度, 緯度),請查我附近的 member”
你傳入的 (x, y)
一般來源于用戶定位(如小程序的 wx.getLocation()
)
?
4. new Distance(5000)
-
Distance
是 Spring 提供的距離對象 -
默認單位是 米(Meters)
-
所以這是一個 以中心點為圓心、半徑為 5000 米(5 公里) 的范圍查找
?
5. RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs()
這是搜索的“附加參數”,你可以在里面設置返回什么信息、是否排序、返回幾個等。
是一個“無參的靜態方法”,作用是:
返回一個新的
GeoSearchCommandArgs
對象,用來配置 Redis GEO 查詢參數。
你得到了一個 GeoSearchCommandArgs
類型的對象,后面可以繼續鏈式調用:
舉例:
RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs()
.includeDistance().limit(5).sortAscending();
?
6. .includeDistance()
這個非常重要!
-
它的作用是:讓返回結果中附帶每個點距離圓心的“實際距離”
-
否則默認只返回哪些 member 在范圍內,不告訴你它們距離多遠
搜索結果判空
if (results == null || results.getContent().isEmpty()) {return nearbyRescues; // 無結果,直接返回空列表
}
?
提取 Redis 搜索結果?
Map<Integer, Distance> distanceMap = new HashMap<>();
List<Integer> ids = new ArrayList<>();for (GeoResult<RedisGeoCommands.GeoLocation<String>> location : results.getContent()) {Integer postId = Integer.valueOf(location.getContent().getName());Distance distance = location.getDistance();ids.add(postId);distanceMap.put(postId, distance);
}
?
?
👉 這里構建:
-
ids
:用于批量查數據庫 -
distanceMap
:用于后續給每個PostsVo
設置距離屬性
GeoResults
└── getContent() → List<GeoResult>└── GeoResult├── getContent() → GeoLocation│ ├── getName() → 帖子ID("123")│ └── getPoint() → 經緯度└── getDistance() → 到查詢中心的距離
🔹 每個 GeoResult
里包含:
-
.getName()
→ Redis GEO 中的 member(這里是帖子 ID),實際就是 Redis GEO 中的 member -
.getDistance()
→ 距離圓心的距離
GeoResults<GeoLocation<String>> results = {List<GeoResult<GeoLocation<String>>> content = [GeoResult {content: GeoLocation {name: "123", // member → 帖子IDpoint: Point(113.2345, 23.4567) // 坐標},distance: Distance(4213.5) // 到查詢點的距離(單位米)},GeoResult {content: GeoLocation {name: "124",point: Point(...)},distance: ...},...]
}
完整范圍搜索的例子
@Override@Transactionalpublic ArrayList<PostsVo> getNearbyRescue(double y, double x) {GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().search(RedisKey.POSTS_ANIMALS_LOCATIONS, // Redis 中存儲地理位置數據的鍵GeoReference.fromCoordinate(x, y), // 查詢的圓心坐標 (經度, 緯度)new Distance(5000), // 查詢的半徑范圍為 5000 米(5 公里)RedisGeoCommands.GeoSearchCommandArgs // 額外的查詢參數.newGeoSearchArgs().includeDistance() // 返回結果中包含距離);//一系列的封裝返回前端的操作ArrayList<PostsVo> nearbyRescues = new ArrayList<>();// 檢查查詢是否有結果if (results == null || results.getContent().isEmpty()) {return nearbyRescues; // 沒有找到附近的帖子,返回空列表}// 用于存儲 postId 和其對應的 DistanceMap<Integer, Distance> distanceMap = new HashMap<>();List<Integer> ids = new ArrayList<>();// 遍歷 Geo 結果,提取 postId 和 distancefor (GeoResult<RedisGeoCommands.GeoLocation<String>> location : results.getContent()) {Integer postId = Integer.valueOf(location.getContent().getName());Distance distance = location.getDistance();ids.add(postId);distanceMap.put(postId, distance);}// 批量查詢 postsList<Posts> postsList = postsMapper.selectBatchIds(ids);// 轉換為 PostsVo 并將距離賦值進去nearbyRescues = postsList.stream().map(post -> {PostsVo postsVo = BeanUtil.copyProperties(post, PostsVo.class);// 假設 posts 對象中有 getPostId() 方法獲取 idDistance d = distanceMap.get(post.getPostId());if(d != null){// 將距離單位轉換為公里(如果需要),這里假設 d.getValue() 返回的是米postsVo.setDistance(d.getValue() / 1000.0);}// 根據animalId查詢動物信息Animals animals = animalsMapper.selectById(post.getAnimalId());AnimalsVo animalsVo = BeanUtil.copyProperties(animals, AnimalsVo.class);String[] photos = animals.getPhotos().split(",");animalsVo.setPhotos(photos);postsVo.setAnimalsVo(animalsVo);return postsVo;}).collect(Collectors.toCollection(ArrayList::new));return nearbyRescues;}
Redis GEO 本質上是在 Redis 中保存地理位置數據(使用經緯度 + key + member 的形式),然后通過 GEORADIUS
/ GEOSEARCH
實現地理范圍搜索。
?