1. Прегледа
В този урок ще разгледаме геопространствената поддръжка в MongoDB.
Ще обсъдим как да съхраняваме геопространствени данни, географско индексиране и геопространствено търсене. Ще използваме също множество заявки за геопространствено търсене като близо , geoWithin и гео пресича .
2. Съхранение на геопространствени данни
Първо, нека видим как да съхраняваме геопространствени данни в MongoDB.
MongoDB поддържа множество GeoJSON типове за съхраняване на геопространствени данни. В нашите примери ще използваме основно Точката и Полигон видове.
2.1. Точка
Това е най-основният и често срещан GeoJSON тип исе използва за представяне на една конкретна точка в решеткатата .
Тук имаме прост обект на нашите места колекция, което има поле местоположение като Точка :
{
"name": "Big Ben",
"location": {
"coordinates": [-0.1268194, 51.5007292],
"type": "Point"
}
}
Обърнете внимание, че първо е стойността на дължината, след това географската ширина.
2.2. Полигон
Полигон е малко по-сложен GeoJSON тип.
Можем да използваме Полигон за дефиниране на зона с нейните външни граници както и вътрешни отвори, ако е необходимо.
Нека видим друг обект, чието местоположение е дефинирано като Полигон :
{
"name": "Hyde Park",
"location": {
"coordinates": [
[
[-0.159381, 51.513126],
[-0.189615, 51.509928],
[-0.187373, 51.502442],
[-0.153019, 51.503464],
[-0.159381, 51.513126]
]
],
"type": "Polygon"
}
}
В този пример ние дефинирахме масив от точки, които представляват външни граници. Също така трябва да затворим границата, така че последната точка да е равна на първата точка.
Имайте предвид, че трябва да дефинираме външните гранични точки в посока обратна на часовниковата стрелка и границите на дупките в посока на часовниковата стрелка.
В допълнение към тези типове има и много други типове като LineString, MultiPoint, Многополигон, MultiLineString, и GeometryCollection.
3. Геопространствена индекса
За да изпълняваме заявки за търсене на геопространствените данни, които съхраняваме, трябва да създадем геопространствен индекс на нашето местоположение поле.
По принцип имаме две опции:2d и 2dsphere .
Но първо, нека дефинираме нашите места сколекция :
MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("myMongoDb");
collection = db.getCollection("places");
3.1. 2d Геопространствен индекса
2d index ни позволява да изпълняваме заявки за търсене, които работят въз основа на изчисления в 2d равнина.
Можем да създадем 2d индекс на местоположението поле в нашето Java приложение, както следва:
collection.createIndex(Indexes.geo2d("location"));
Разбира се, можем да направим същото в mongo обвивка:
db.places.createIndex({location:"2d"})
3.2. 2dsphere Геопространствен индекса
2dsphere index поддържа заявки, които работят въз основа на изчисления на сфера.
По подобен начин можем да създадем 2dsphere индекс в Java, използвайки същите Индекси клас както по-горе:
collection.createIndex(Indexes.geo2dsphere("location"));
Или в монго обвивка:
db.places.createIndex({location:"2dsphere"})
4. Търсене с помощта на геопространствени заявки
Сега, за вълнуващата част, нека търсим обекти въз основа на тяхното местоположение, използвайки геопространствени заявки.
4.1. Близо до заявката
Да започнем с близо. Можем да използваме близо заявка за търсене на места в рамките на дадено разстояние.
Близо заявката работи и с 2d и 2dsphere индекси.
В следващия пример ще търсим места, които са на по-малко от 1 км и на повече от 10 метра от дадената позиция:
@Test
public void givenNearbyLocation_whenSearchNearby_thenFound() {
Point currentLoc = new Point(new Position(-0.126821, 51.495885));
FindIterable<Document> result = collection.find(
Filters.near("location", currentLoc, 1000.0, 10.0));
assertNotNull(result.first());
assertEquals("Big Ben", result.first().get("name"));
}
И съответната заявка в mongo обвивка:
db.places.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [-0.126821, 51.495885]
},
$maxDistance: 1000,
$minDistance: 10
}
}
})
Имайте предвид, че резултатите са сортирани от най-близкия до най-далечния.
По същия начин, ако използваме много далечно местоположение, няма да намерим места в близост:
@Test
public void givenFarLocation_whenSearchNearby_thenNotFound() {
Point currentLoc = new Point(new Position(-0.5243333, 51.4700223));
FindIterable<Document> result = collection.find(
Filters.near("location", currentLoc, 5000.0, 10.0));
assertNull(result.first());
}
Имаме и nearSphere метод, който действа точно като близо, освен че изчислява разстоянието с помощта на сферична геометрия.
4.2. В рамките на заявката
След това ще проучим geoWithin заявка.
geoWithin заявката ни позволява да търсим места, които напълно съществуват в рамките на дадена геометрия , като кръг, кутия или многоъгълник. Това работи и с двете 2d и 2dsphere индекси.
В този пример търсим места, които съществуват в радиус от 5 км от дадената централна позиция:
@Test
public void givenNearbyLocation_whenSearchWithinCircleSphere_thenFound() {
double distanceInRad = 5.0 / 6371;
FindIterable<Document> result = collection.find(
Filters.geoWithinCenterSphere("location", -0.1435083, 51.4990956, distanceInRad));
assertNotNull(result.first());
assertEquals("Big Ben", result.first().get("name"));
}
Имайте предвид, че трябва да преобразуваме разстоянието от км в радиан (просто разделете на радиуса на Земята).
И получената заявка:
db.places.find({
location: {
$geoWithin: {
$centerSphere: [
[-0.1435083, 51.4990956],
0.0007848061528802386
]
}
}
})
След това ще търсим всички места, които съществуват в рамките на правоъгълна „кутия“. Трябва да дефинираме полето по долната му лява позиция и горната дясна позиция:
@Test
public void givenNearbyLocation_whenSearchWithinBox_thenFound() {
double lowerLeftX = -0.1427638;
double lowerLeftY = 51.4991288;
double upperRightX = -0.1256209;
double upperRightY = 51.5030272;
FindIterable<Document> result = collection.find(
Filters.geoWithinBox("location", lowerLeftX, lowerLeftY, upperRightX, upperRightY));
assertNotNull(result.first());
assertEquals("Big Ben", result.first().get("name"));
}
Ето съответната заявка в mongo обвивка:
db.places.find({
location: {
$geoWithin: {
$box: [
[-0.1427638, 51.4991288],
[-0.1256209, 51.5030272]
]
}
}
})
И накрая, ако областта, в която искаме да търсим, не е правоъгълник или кръг, можем да използваме многоъгълник, за да дефинираме по-конкретна област :
@Test
public void givenNearbyLocation_whenSearchWithinPolygon_thenFound() {
ArrayList<List<Double>> points = new ArrayList<List<Double>>();
points.add(Arrays.asList(-0.1439, 51.4952));
points.add(Arrays.asList(-0.1121, 51.4989));
points.add(Arrays.asList(-0.13, 51.5163));
points.add(Arrays.asList(-0.1439, 51.4952));
FindIterable<Document> result = collection.find(
Filters.geoWithinPolygon("location", points));
assertNotNull(result.first());
assertEquals("Big Ben", result.first().get("name"));
}
А ето и съответната заявка:
db.places.find({
location: {
$geoWithin: {
$polygon: [
[-0.1439, 51.4952],
[-0.1121, 51.4989],
[-0.13, 51.5163],
[-0.1439, 51.4952]
]
}
}
})
Ние дефинирахме само многоъгълник с външните му граници, но можем да добавим и дупки към него. Всяка дупка ще бъде Списък на Точка s:
geoWithinPolygon("location", points, hole1, hole2, ...)
4.3. Заявка за пресичанета
И накрая, нека разгледаме geoIntersects заявка.
гео пресичане заявката намира обекти, които поне се пресичат с дадена геометрия. За сравнение, geoWithin намира обекти, които напълно съществуват в рамките на дадена геометрия .
Тази заявка работи с 2dsphere само индекс.
Нека видим това на практика, с пример за търсене на всяко място, което се пресича с Полигон :
@Test
public void givenNearbyLocation_whenSearchUsingIntersect_thenFound() {
ArrayList<Position> positions = new ArrayList<Position>();
positions.add(new Position(-0.1439, 51.4952));
positions.add(new Position(-0.1346, 51.4978));
positions.add(new Position(-0.2177, 51.5135));
positions.add(new Position(-0.1439, 51.4952));
Polygon geometry = new Polygon(positions);
FindIterable<Document> result = collection.find(
Filters.geoIntersects("location", geometry));
assertNotNull(result.first());
assertEquals("Hyde Park", result.first().get("name"));
}
Получената заявка:
db.places.find({
location:{
$geoIntersects:{
$geometry:{
type:"Polygon",
coordinates:[
[
[-0.1439, 51.4952],
[-0.1346, 51.4978],
[-0.2177, 51.5135],
[-0.1439, 51.4952]
]
]
}
}
}
})