MongoDB实现搜附近功能
创建对应于数据库中的对应的实体类
@Document(collection = "user_location")
@CompoundIndex(name = "location_index", def = "{'location': '2dsphere'}")
public class UserLocation implements Serializable {
private static final long serialVersionUID = 4508868382007529970L;
@Id
private ObjectId id;
@Indexed
private Long userId;
/**
* x:经度 y:纬度
*/
private GeoJsonPoint location;
/**
* 位置描述
*/
private String address;
private Long created;
private Long updated;
/**
* 上次更新时间
*/
private Long lastUpdated;
编写接口
public interface UserLocationApi {
/**
* 更新用户地理位置
*
* @param userId 当前用户
* @param address 位置描述
* @param longitude 经度
* @param latitude 纬度
* @return
*/
String updateUserLocation(Long userId, Double longitude, Double latitude, String address);
/**
* 查询用户地理位置
*
* @param userId 当前用户
* @return
*/
UserLocationVo queryByUserId(Long userId);
/**
* 根据地理位置查询用户
*
* @param longitude
* @param latitude
* @param range 范围 单位 米
* @return
*/
List<UserLocationVo> queryUserFromLocation(Double longitude, Double latitude, Integer range);
}
编写实现类
@Service(version = "1.0")
public class UserLocationApiImpl implements UserLocationApi {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public String updateUserLocation(Long userId, Double longitude, Double latitude, String address) {
UserLocation userLocation = new UserLocation();
userLocation.setAddress(address);
userLocation.setLocation(new GeoJsonPoint(longitude, latitude));
userLocation.setUserId(userId);
Query query = Query.query(Criteria.where("userId").is(userLocation.getUserId()));
UserLocation ul = this.mongoTemplate.findOne(query, UserLocation.class);
if (ul == null) {
// 新增位置信息
userLocation.setId(ObjectId.get());
userLocation.setCreated(System.currentTimeMillis());
userLocation.setUpdated(userLocation.getCreated());
userLocation.setLastUpdated(userLocation.getCreated());
this.mongoTemplate.save(userLocation);
return userLocation.getId().toHexString();
} else {
// 更新位置信息
Update update = Update
.update("location", userLocation.getLocation())
.set("updated", System.currentTimeMillis())
.set("lastUpdated", ul.getUpdated())
.set("address",address);
this.mongoTemplate.updateFirst(query, update, UserLocation.class);
}
return ul.getId().toHexString();
}
@Override
public UserLocationVo queryByUserId(Long userId) {
Query query = Query.query(Criteria.where("userId").is(userId));
UserLocation userLocation = this.mongoTemplate.findOne(query, UserLocation.class);
return UserLocationVo.format(userLocation);
}
@Override
public List<UserLocationVo> queryUserFromLocation(Double longitude, Double latitude, Integer range) {
GeoJsonPoint geoJsonPoint = new GeoJsonPoint(longitude, latitude);
// 转换为2dsphere的距离
Distance distance = new Distance(range / 1000, Metrics.KILOMETERS);
// 画一个圆
Circle circle = new Circle(geoJsonPoint, distance);
Query query = Query.query(Criteria.where("location").withinSphere(circle));
return UserLocationVo.formatToList(this.mongoTemplate.find(query, UserLocation.class));
}
}
UserLocationVo类的编写(由于UserLocation不能序列化,所以要再定义UserLocationVo进行返回数据)
public class UserLocationVo implements Serializable {
private static final long serialVersionUID = 4133419501260037769L;
private String id;
private Long userId;
private Double longitude;
private Double latitude;
private String address;
private Long created;
private Long updated;
private Long lastUpdated;
public static final UserLocationVo format(UserLocation userLocation) {
UserLocationVo userLocationVo = new UserLocationVo();
userLocationVo.setAddress(userLocation.getAddress());
userLocationVo.setCreated(userLocation.getCreated());
userLocationVo.setId(userLocation.getId().toHexString());
userLocationVo.setLastUpdated(userLocation.getLastUpdated());
userLocationVo.setUpdated(userLocation.getUpdated());
userLocationVo.setUserId(userLocation.getUserId());
userLocationVo.setLongitude(userLocation.getLocation().getX());
userLocationVo.setLatitude(userLocation.getLocation().getY());
return userLocationVo;
}
public static final List<UserLocationVo> formatToList(List<UserLocation> userLocations) {
List<UserLocationVo> list = new ArrayList<>();
for (UserLocation userLocation : userLocations) {
list.add(format(userLocation));
}
return list;
}
控制层
@Authorization
@GetMapping("search")
public ResponseEntity<List<NearUserVo>> queryNearUser(@RequestParam(value = "gender", required = false) String gender,
@RequestParam(value = "distance", defaultValue = "2000") String distance) {
try {
List<NearUserVo> list = this.todayBestService.queryNearUser(gender, distance);
return ResponseEntity.ok(list);
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
业务层
public List<NearUserVo> queryNearUser(String gender, String distance) {
User user = UserThreadLocal.get();
// 查询当前用户的位置信息
UserLocationVo userLocationVo = this.userLocationApi.queryByUserId(user.getId());
Double longitude = userLocationVo.getLongitude();
Double latitude = userLocationVo.getLatitude();
// 根据当前用户的位置信息查询附近的好友
List<UserLocationVo> userLocationList = this.userLocationApi
.queryUserFromLocation(longitude, latitude, Integer.valueOf(distance));
if (CollectionUtils.isEmpty(userLocationList)) {
return Collections.emptyList();
}
List<Long> userIds = new ArrayList<>();
for (UserLocationVo locationVo : userLocationList) {
userIds.add(locationVo.getUserId());
}
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.in("user_id", userIds);
if (StringUtils.equalsIgnoreCase(gender, "man")) {
queryWrapper.in("sex", SexEnum.MAN);
} else if (StringUtils.equalsIgnoreCase(gender, "woman")) {
queryWrapper.in("sex", SexEnum.WOMAN);
}
List<UserInfo> userInfoList = this.userInfoMapper.selectList(queryWrapper);
List<NearUserVo> nearUserVoList = new ArrayList<>();
for (UserLocationVo locationVo : userLocationList) {
if (locationVo.getUserId().longValue() == user.getId().longValue()) {
// 排除自己
continue;
}
for (UserInfo userInfo : userInfoList) {
if (locationVo.getUserId().longValue() == userInfo.getUserId().longValue()) {
NearUserVo nearUserVo = new NearUserVo();
nearUserVo.setUserId(userInfo.getUserId());
nearUserVo.setAvatar(userInfo.getLogo());
nearUserVo.setNickname(userInfo.getNickName());
nearUserVoList.add(nearUserVo);
break;
}
}
}
return nearUserVoList;
}