This article is included in personal blog: http://www.chengxy-nds.top , technical data sharing and learning together
Yesterday, a buddies of the official account and I discussed an interview question. Personally, it was more meaningful. I arranged it here to share it with you. The interview question is relatively simple: “let you realize the function of a nearby person, what plan do you have?” In fact, this question is mainly to investigate the breadth of technology. This paper introduces several schemes to give you some ideas, so as to avoid affecting the interview result due to the language jam in the interview process. If there is any laxity, please kindly point it out!
"People in the neighborhood"
It is more commonly used in functional life, such as restaurants near takeout app and sharing cars near bike app. Since the probability of being asked in a common interview is very high, the following analysis is based onMysql database
、Redis
、 MongoDB
The function of “people nearby” is realized.
The popular science: to mark a position in the world, the general practice is to use longitude and latitude. The range of longitude is (- 180, 180), and the range of latitude is (- 90, 90). The latitude is bounded by the equator, north is positive and south is negative, and the longitude is bounded by the prime meridian (Greenwich Observatory), positive in the East and negative in the West. For example, the longitude and latitude (116.49141, 40.01229) of the Motorola building in Wangjing are all positive numbers, because China is located in the Northeast hemisphere.
1、 “People nearby” principle
"People in the neighborhood"
That is often saidLBS
Based on the current location services, it provides value-added services for users based on their current location.
The core idea of “people nearby” is as follows:
- Search nearby users with “I” as the center
- Calculate the distance between others and me based on my current geographical location
- According to the distance between “I” and others, the nearest user or store is selected
2、 What is geohash algorithm?
I’m talking about it"People in the neighborhood"
Before the specific implementation of the function, let’s have a look at itGeoHash
Algorithm, because it will always be dealt with later. The best way to locate a position is to useLongitude and latitude
Identification, butLongitude and latitude
It is two-dimensional, and it is very troublesome to calculate the positionLongitude and latitude
It’s much easier to compare data into one-dimensional data, soGeoHash
The algorithm came into being.
GeoHash
The algorithm converts the two-dimensional longitude and latitude into a string, for example: 9 in the figure belowGeoHash
The string represents nine regions, and each string represents a rectangular region. The other points (longitude and latitude) in this rectangular area use the same oneGeoHash
String representation.
such as:WX4ER
Due to the user’s data within this area of the user’s searchGeoHash
All strings areWX4ER
Therefore, we can put theWX4ER
Askey
, restaurant information asvalue
If you do not use theGeoHash
In this algorithm, users in the region request restaurant data, and the longitude and latitude of users are different, so caching is not only troublesome, but also a huge amount of data.
GeoHash
The longer the string is, the more accurate the position is. The longer the string is, the smaller the error in distance is. Belowgeohash
Code precision table:
Length, width and height of geohash codes
——– | —– | —– |
1 | 5,009.4km | 4,992.6km
2 |1,252.3km |624.1km
3 |156.5km |156km
4 |39.1km |19.5km
5 |4.9km |4.9km
6 |1.2km |609.4m
7 |152.9m |152.4m
8 |38.2m| 19m
9 |4.8m |4.8m
10 |1.2m |59.5cm
11 |14.9cm |14.9cm
12 |3.7cm |1.9cm
Moreover, the more similar the string is, the closer the distance is, and the more string prefix matches, the closer the distance is. For example, the longitude and latitude below represent three restaurants close to each other.
Merchant | latitude and longitude | geohash string
——– | —– | —– |
Chuanchuanxiang | 116.402843,39.999375 | wx4er9v
Hot pot | 116.3967,39.99932 | wx4rtk
Barbecue | 116.40382,39.918118 | wx4erfe
Let’s have a simple understanding of what isGeoHash
Easy to expand the content,GeoHash
The content of the algorithm is relatively high and deep, and the interested partners can dig deeply by themselves. It doesn’t take too much space here (in fact, I know too shallow, cry and haw ~).
3、 Based on MySQL
This approach is purely based onmysql
Implemented, not usedGeoHash
Algorithm.
1. Design ideas
Taking the user as the center, assuming a distance of 500 meters as the radius, draw a circle. All users in this circular area are “people nearby” who meet the user’s requirements. However, there is a problem that the circle has radian. It is too difficult to search the circular area directly. It is impossible to search directly with longitude and latitude.
But if there is a square on the circular jacket, it is easy to search the user information in the square by obtaining the maximum and minimum values of user longitude and latitude (longitude, latitude + distance) and then using the maximum and minimum values as the filtering criteria.
So here comes the question,Does more come out area swollen do?
Let’s analyze. The distance from the user in this part of the area to the circle must be larger than the radius of the circle. Then we calculate the distance between the user’s center point and all users in the square, and screen out all users whose distance is less than or equal to the radius, and all users in the circular area meet the requirements"People in the neighborhood"
。
2. Analysis of advantages and disadvantages
Purely based onmysql
realization"People in the neighborhood"
The advantages are obvious, that is, it is simple, as long as you create a table to store the longitude and latitude information of users. The disadvantage is also obvious, which requires a lot of calculation of the distance between two points, which greatly affects the performance.
3. Implementation
Create a simple table to store the latitude and longitude attributes of users.
CREATE TABLE `nearby_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Name ` varchar (255) default null comment 'name',
`Longitude ` double default null comment 'longitude',
`Latitude ` double default null comment 'latitude',
`create_ time` datetime DEFAULT NULL ON UPDATE CURRENT_ Timestamp comment 'creation time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
To calculate the distance between two points, a tripartite library is used. After all, the wheel made by ourselves is not particularly round, and it may be square, ha ha ha ha~
<dependency>
<groupId>com.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>0.5</version>
</dependency>
After the outer square is obtained, the users in the square area are searched by the maximum and minimum longitude and latitude values of the square, and then the users who exceed the specified distance are eliminatedpeople nearby
。
private SpatialContext spatialContext = SpatialContext.GEO;
/**
*Get people in the neighborhood x meters
*
*@ param distance search range unit: km
*@ param userlng longitude of current user
*@ param userlat latitude of current user
*/
@GetMapping("/nearby")
public String nearBySearch(@RequestParam("distance") double distance,
@RequestParam("userLng") double userLng,
@RequestParam("userLat") double userLat) {
//1. Get the circumscribed square
Rectangle rectangle = getRectangle(distance, userLng, userLat);
//2. Get all users in the square
List<User> users = userMapper.selectUser(rectangle.getMinX(), rectangle.getMaxX(), rectangle.getMinY(), rectangle.getMaxY());
//3. Remove redundant users whose radius exceeds the specified distance
users = users.stream()
.filter(a -> getDistance(a.getLongitude(), a.getLatitude(), userLng, userLat) <= distance)
.collect(Collectors.toList());
return JSON.toJSONString(users);
}
private Rectangle getRectangle(double distance, double userLng, double userLat) {
return spatialContext.getDistCalc()
.calcBoxByDistFromPt(spatialContext.makePoint(userLng, userLat),
distance * DistanceUtils.KM_TO_DEG, spatialContext, null);
}
Because the sorting of distances between users is implemented in business code, you can see that SQL statements are also very simple.
<select id="selectUser" resultMap="BaseResultMap">
SELECT * FROM user
WHERE 1=1
and (longitude BETWEEN ${minlng} AND ${maxlng})
and (latitude BETWEEN ${minlat} AND ${maxlat})
</select>
4、 MySQL + geohash
1. Design ideas
The design idea of this method is simpler. When storing the location information of users, the corresponding values are calculated according to the user’s longitude and latitude attributesgeohash
character string.be careful: in calculationgeohash
String, you need to specifygeohash
The precision of the string, that isgeohash
The length of the string,See abovegeohash
Precision table。
When you need to getpeople nearby
, only the current user is requiredgeohash
String, database throughWHERE geohash Like 'geocode%
‘To find outgeohash
Then calculate the distance between the current user and the searched user, and filter out all the users whose distance is less than or equal to the specified distance (500 meters nearby), i.epeople nearby
。
2. Analysis of advantages and disadvantages
utilize GeoHash
Algorithm implementation"People in the neighborhood"
There is a problem becausegeohash
The algorithm divides the map into rectangles and encodes each rectanglegeohash
character string. But my current point is very close to the adjacent point, but we are in two areas respectively. The point clearly in front of me cannot be found. It is dark under the light.
How to solve this problem?
In order to avoid similar two adjacent points in different regions, we need to obtain the current point at the same time(WX4G0
)Near the area8
Regionalgeohash
Code, screening and comparison.
3. Implementation
We also need to design a table to store the longitude and latitude information of users, but the difference is to add one moregeo_code
Field to store the geohash string. This field is calculated by the user’s latitude and longitude attributes. Frequently used fields are recommended to be indexed.
CREATE TABLE `nearby_user_geohash` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Name ` varchar (255) default null comment 'name',
`Longitude ` double default null comment 'longitude',
`Latitude ` double default null comment 'latitude',
`geo_ Code ` varchar (64) default null comment 'geohash code calculated by latitude and longitude',
`create_ time` datetime DEFAULT NULL ON UPDATE CURRENT_ Timestamp comment 'creation time',
PRIMARY KEY (`id`),
KEY `index_geo_hash` (`geo_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Firstly, according to the user’s latitude and longitude information, the user’s coordinates are calculated after the specified precisiongeoHash
Code, and then get 8 directions around the usergeoHash
Code searches for users in the database, and finally filters out users beyond the given distance (within 500 meters).
private SpatialContext spatialContext = SpatialContext.GEO;
/***
*Add user
* @return
*/
@PostMapping("/addUser")
public boolean add(@RequestBody UserGeohash user) {
//The default precision is 12 bits
String geoHashCode = GeohashUtils.encodeLatLon(user.getLatitude(),user.getLongitude());
return userGeohashService.save(user.setGeoCode(geoHashCode).setCreateTime(LocalDateTime.now()));
}
/**
*Get people in the specified range nearby
*
*@ param distance distance (distance of nearby users) unit km
*The precision of @ param len geohash (several bit string)
*@ param userlng longitude of current user
*@ param userlat latitude of current user
* @return json
*/
@GetMapping("/nearby")
public String nearBySearch(@RequestParam("distance") double distance,
@RequestParam("len") int len,
@RequestParam("userLng") double userLng,
@RequestParam("userLat") double userLat) {
//1. According to the required range, determine the accuracy of geohash code, and obtain the geohash code of current user coordinates
GeoHash geoHash = GeoHash.withCharacterPrecision(userLat, userLng, len);
//2. Get the geohash codes of 8 directions around the user
GeoHash[] adjacent = geoHash.getAdjacent();
QueryWrapper<UserGeohash> queryWrapper = new QueryWrapper<UserGeohash>()
.likeRight("geo_code",geoHash.toBase32());
Stream.of(adjacent).forEach(a -> queryWrapper.or().likeRight("geo_code",a.toBase32()));
//3. Match the geohash code with specified precision
List<UserGeohash> users = userGeohashService.list(queryWrapper);
//4. Filter those that exceed the distance
users = users.stream()
.filter(a ->getDistance(a.getLongitude(),a.getLatitude(),userLng,userLat)<= distance)
.collect(Collectors.toList());
return JSON.toJSONString(users);
}
/***
*The distance between two points in a sphere
*@ param longitude 1
*@ param latitude 1
*@ param userlng longitude 2
*@ param userlat latitude 2
*@ return return distance, in KM
*/
private double getDistance(Double longitude, Double latitude, double userLng, double userLat) {
return spatialContext.calcDistance(spatialContext.makePoint(userLng, userLat),
spatialContext.makePoint(longitude, latitude)) * DistanceUtils.DEG_TO_KM;
}
5、 Redis + geohash
Redis 3.2
After version, based ongeohash
And data structureZset
It provides geographic location related functions. Through the top twomysql
The implementation of the discovery,people nearby
The function is obviously read more and write less scenes, so useredis
Performance will be greatly improved.
1. Design ideas
redis
realizationpeople nearby
The function is mainly throughGeo
Six commands for the module.
-
GEOADD
: add the given location object (latitude, longitude, name) to the specified key; -
GEOPOS
: returns the location (longitude and latitude) of all the given location objects from the key; -
GEODIST
: returns the distance between two given positions; -
GEOHASH
: returns the geohash representation of one or more location objects; -
GEORADIUS
: return all position objects in the target set whose distance from the center does not exceed the given maximum distance with the given longitude and latitude as the center; -
GEORADIUSBYMEMBER
: returns all the position objects whose distance does not exceed the given maximum distance with the given position object as the center.
withGEOADD
Command andGEORADIUS
Simple example of command:
GEOADD key longitude latitude member [longitude latitude member ...]
Among them,key
Is the collection name,member
Is the object corresponding to the latitude and longitude.
GEOADD
Add location information of “hot pot shop” of multiple merchants:
Geoadd Hotel 119.98866180732716 30.27465803229662 hot pot restaurant
GEORADIUS
According to the given longitude and latitude as the center, all position objects in the target set whose distance from the center does not exceed the given maximum distance (within 500m) are obtained"People in the neighborhood"
。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count] [STORE key] [STORedisT key]
Scope unit:m
| km
| ft
| mi
–>Meters, kilometers, feet, miles.
-
WITHDIST
: returns the distance between the position object and the center at the same time. The unit of distance is consistent with the range unit given by the user. -
WITHCOORD
: returns the longitude and dimension of the location object. -
WITHHASH
: returns the ordered set score of the location object in the form of a 52 bit signed integer. This option is mainly used for the underlying application or debugging, but it has little effect in practice. -
ASC | DESC
: return position object element from near to far | return position object element from far to near. -
COUNT count
: select the first n matching position object elements. (return all elements if not set) -
STORE key
: save the geographic location information of the returned result to the specified key. -
STORedisT key
: save the distance between the return result and the center point to the specified key.
For example, the following command: get all hotels within 500 meters of the current location.
GEORADIUS hotel 119.98866180732716 30.27465803229662 500 m WITHCOORD
Redis
Internal use of ordered sets(zset
)Save the user’s location information,zset
Each element in the is an object with a locationscore
The value is 52 bits calculated by latitude and longitudegeohash
Value.
2. Analysis of advantages and disadvantages
redis
realizationpeople nearby
It has high efficiency, simple integration, and also supports distance sorting. However, there are some errors in the results. In order to make the results more accurate, it is necessary to manually calculate the distance between the user center position and other user locations, and then screen again.
3. Implementation
Here’s what it looks likeJava
redis
Implementation version, the code is very concise.
@Autowired
private RedisTemplate<String, Object> redisTemplate;
//Key used by geo related commands
private final static String KEY = "user_info";
public boolean save(User user) {
Long flag = redisTemplate.opsForGeo().add(KEY, new RedisGeoCommands.GeoLocation<>(
user.getName(),
new Point(user.getLongitude(), user.getLatitude()))
);
return flag != null && flag > 0;
}
/**
*Get users in the specified range nearby according to the current location
*@ param distance specifies the range unit km, which can be determined according to {@ link org.springframework.data . geo.Metrics }Set it up
*@ param userlng user longitude
*@ param userlat user latitude
* @return
*/
public String nearBySearch(double distance, double userLng, double userLat) {
List<User> users = new ArrayList<>();
//1. Georadius obtains information in the vicinity
GeoResults<RedisGeoCommands.GeoLocation<Object>> reslut =
redisTemplate.opsForGeo().radius(KEY,
new Circle(new Point(userLng, userLat), new Distance(distance, Metrics.KILOMETERS)),
RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.includeCoordinates().sortAscending());
//2. Collect information and save it in the list
List<GeoResult<RedisGeoCommands.GeoLocation<Object>>> content = reslut.getContent();
//3. Filter out the data beyond the distance
content.forEach(a-> users.add(
new User().setDistance(a.getDistance().getValue())
.setLatitude(a.getContent().getPoint().getX())
.setLongitude(a.getContent().getPoint().getY())));
return JSON.toJSONString(users);
}
6、 Mongodb + 2D index
1. Design ideas
MongoDB
The realization of nearby people is mainly through its two kinds of geospatial index2dsphere
and2d
。 The bottom layer of the two indexes is still based onGeohash
To build. But with the international commonGeohash
There are also some differences. Please refer to the official documents for details.
2dsphere
The index only supports geometry query of spherical surface.
2d
The index supports flat geometry and some spherical queries. although2d
Indexes support some spherical queries, but2d
Errors may occur when indexing these spherical queries. Therefore, spherical query should be selected as much as possible2dsphere
Indexes.
Although the methods of the two indexes are different, as long as the coordinate span is not too large, the distance difference between the two indexes can be ignored.
2. Implementation
First insert a batch of location data into theMongoDB
, collection
Name ithotel
, equivalent toMySQL
Table name of. Two fieldsname
name,location
It is a pair of longitude and latitude data.
db.hotel.insertMany([
{'name':'hotel1', location:[115.993121,28.676436]},
{'name':'hotel2', location:[116.000093,28.679402]},
{'name':'hotel3', location:[115.999967,28.679743]},
{'name':'hotel4', location:[115.995593,28.681632]},
{'name':'hotel5', location:[115.975543,28.679509]},
{'name':'hotel6', location:[115.968428,28.669368]},
{'name':'hotel7', location:[116.035262,28.677037]},
{'name':'hotel8', location:[116.024770,28.68667]},
{'name':'hotel9', location:[116.002384,28.683865]},
{'name':'hotel10', location:[116.000821,28.68129]},
])
Next we givelocation
Field creates a2d
Index, index accuracy throughbits
To specify,bits
The larger the index, the more accurate the index is.
db.coll.createIndex({'location':"2d"}, {"bits":11111})
usegeoNear
Order a test,near
Current coordinates (longitude, latitude),spherical
Whether to calculate the spherical distance,distanceMultiplier
Radius of the earth. The unit is meters. The default value is 6378137,maxDistance
Filter conditions (users within the specified distance), open radian must be divided intodistanceMultiplier
,distanceField
The calculated distance between two points, field alias (random name).
db.hotel.aggregate({
$geoNear:{
Near: [115.999567,28.681813], // current coordinates
Spherical: true, // calculate spherical distance
Distancemultiplier: 6378137, // the radius of the earth in meters, then the division record is also meter
Maxdistance: 2000 / 6378137, // within 2000m, radian is required
Distancefield: "distance" // distance field alias
}
})
You can see that there is qualified data in the result, and there is one more fielddistance
The alias you just set represents the distance between two points.
{ "_id" : ObjectId("5e96a5c91b8d4ce765381e58"), "name" : "hotel10", "location" : [ 116.000821, 28.68129 ], "distance" : 135.60095397487655 }
{ "_id" : ObjectId("5e96a5c91b8d4ce765381e51"), "name" : "hotel3", "location" : [ 115.999967, 28.679743 ], "distance" : 233.71915803517447 }
{ "_id" : ObjectId("5e96a5c91b8d4ce765381e50"), "name" : "hotel2", "location" : [ 116.000093, 28.679402 ], "distance" : 273.26317035334176 }
{ "_id" : ObjectId("5e96a5c91b8d4ce765381e57"), "name" : "hotel9", "location" : [ 116.002384, 28.683865 ], "distance" : 357.5791936927476 }
{ "_id" : ObjectId("5e96a5c91b8d4ce765381e52"), "name" : "hotel4", "location" : [ 115.995593, 28.681632 ], "distance" : 388.62555058249967 }
{ "_id" : ObjectId("5e96a5c91b8d4ce765381e4f"), "name" : "hotel1", "location" : [ 115.993121, 28.676436 ], "distance" : 868.6740526419927 }
summary
The focus of this paper is not on the specific implementation, but to provide some design ideas. During the interview, you may not have a deep understanding of a certain technology, but if you have a wide range of knowledge, you can say a variety of design ideas from many aspects, and you can talk with a lot, then you will give the interviewers great favor and the probability of getting an offer will be much higher. and"People in the neighborhood"
There are many scenarios for the use of functions, especially for e-commerce platforms. Therefore, students who want to enter large factories should have some knowledge about this kind of knowledge.
Code implementation refers to an open source project of a big guy. Here are demos of the first three implementation methods. Interested partners can learn about GitHub address:https://github.com/larscheng/larscheng-learning-demo/tree/master/NearbySearch
,。