1. Przegląd
W tym samouczku omówimy obsługę geoprzestrzenną w MongoDB.
Omówimy sposób przechowywania danych geoprzestrzennych, indeksowania geograficznego i wyszukiwania geoprzestrzennego. Będziemy również używać wielu zapytań geoprzestrzennych, takich jak blisko , geoWewnątrz i geoIntersects .
2. Przechowywanie danych geoprzestrzennych
Najpierw zobaczmy, jak przechowywać dane geoprzestrzenne w MongoDB.
MongoDB obsługuje wiele GeoJSON typy do przechowywania danych geoprzestrzennych. W naszych przykładach będziemy głównie używać Point i Wielokąt typy.
2.1. Punkt
To jest najbardziej podstawowy i powszechny GeoJSON typ i służy do reprezentowania jednego konkretnego punktu na siatce .
Tutaj mamy prosty obiekt w naszych miejscach kolekcja, z polem lokalizacja jako punkt :
{
"name": "Big Ben",
"location": {
"coordinates": [-0.1268194, 51.5007292],
"type": "Point"
}
}
Zauważ, że najpierw jest długość geograficzna, a potem szerokość geograficzna.
2.2. Wielokąt
Wielokąt jest nieco bardziej złożona GeoJSON typ.
Możemy użyć wielokąta zdefiniować obszar z jego granicami zewnętrznymi a także w razie potrzeby otwory wewnętrzne.
Zobaczmy inny obiekt, którego położenie jest zdefiniowane jako wielokąt :
{
"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"
}
}
W tym przykładzie zdefiniowaliśmy tablicę punktów reprezentujących granice zewnętrzne. Musimy również zamknąć granicę tak, aby ostatni punkt był równy punktowi pierwszemu.
Zauważ, że musimy zdefiniować zewnętrzne punkty granic w kierunku przeciwnym do ruchu wskazówek zegara i granice otworów w kierunku zgodnym z ruchem wskazówek zegara.
Oprócz tych typów istnieje również wiele innych typów, takich jak LineString, MultiPoint, Wielokąt, MultiLineString, i GeometryCollection.
3. Indeksowanie geoprzestrzenne
Aby wykonywać zapytania dotyczące przechowywanych przez nas danych geoprzestrzennych, musimy utworzyć indeks geoprzestrzenny w naszej lokalizacji pole.
Zasadniczo mamy dwie opcje:2d i 2dsphere .
Ale najpierw zdefiniujmy zbiór naszych miejsc zbiór :
MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("myMongoDb");
collection = db.getCollection("places");
3.1. 2d Indeks geoprzestrzenny
2d indeks umożliwia nam wykonywanie zapytań wyszukiwania, które działają w oparciu o obliczenia płaszczyzny 2d.
Możemy stworzyć 2d indeks lokalizacji w naszej aplikacji Java w następujący sposób:
collection.createIndex(Indexes.geo2d("location"));
Oczywiście możemy zrobić to samo w mongo powłoka:
db.places.createIndex({location:"2d"})
3.2. 2dsfera Indeks geoprzestrzenny
Sfera 2d index obsługuje zapytania, które działają w oparciu o obliczenia kuli.
Podobnie możemy utworzyć 2dsphere indeksuj w Javie przy użyciu tych samych indeksów klasa jak wyżej:
collection.createIndex(Indexes.geo2dsphere("location"));
Lub w mongo powłoka:
db.places.createIndex({location:"2dsphere"})
4. Wyszukiwanie za pomocą zapytań geoprzestrzennych
Teraz, w ekscytującej części, wyszukajmy obiekty na podstawie ich lokalizacji za pomocą zapytań geoprzestrzennych.
4.1. W pobliżu zapytania
Zacznijmy od blisko. Możemy użyć w pobliżu zapytanie, aby wyszukać miejsca w określonej odległości.
w pobliżu zapytanie działa zarówno z 2d i 2dsfera indeksy.
W następnym przykładzie wyszukamy miejsca oddalone o mniej niż 1 km io więcej niż 10 metrów od podanej pozycji:
@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"));
}
I odpowiednie zapytanie w mongo powłoka:
db.places.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [-0.126821, 51.495885]
},
$maxDistance: 1000,
$minDistance: 10
}
}
})
Pamiętaj, że wyniki są sortowane od najbliższego do najdalszego.
Podobnie, jeśli użyjemy bardzo odległej lokalizacji, nie znajdziemy żadnych pobliskich miejsc:
@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());
}
Mamy też nearSphere metoda, która działa dokładnie tak, jak near, poza tym, że oblicza odległość za pomocą geometrii sferycznej.
4.2. W zapytaniu
Następnie przyjrzymy się geoWithin zapytanie.
geoWithin zapytanie umożliwia nam wyszukiwanie miejsc, które w pełni istnieją w danej Geometrii , jak okrąg, prostokąt lub wielokąt. Działa to również w przypadku 2d i 2dsfera indeksy.
W tym przykładzie szukamy miejsc, które istnieją w promieniu 5 km od podanej pozycji środkowej:
@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"));
}
Pamiętaj, że musimy przekształcić odległość z km na radiany (po prostu podziel przez promień Ziemi).
A wynikowe zapytanie:
db.places.find({
location: {
$geoWithin: {
$centerSphere: [
[-0.1435083, 51.4990956],
0.0007848061528802386
]
}
}
})
Następnie przeszukamy wszystkie miejsca znajdujące się w prostokątnym „pudełku”. Musimy zdefiniować pudełko poprzez jego dolną lewą pozycję i górną prawą pozycję:
@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"));
}
Oto odpowiednie zapytanie w języku mongo powłoka:
db.places.find({
location: {
$geoWithin: {
$box: [
[-0.1427638, 51.4991288],
[-0.1256209, 51.5030272]
]
}
}
})
Wreszcie, jeśli obszar, w którym chcemy przeszukać, nie jest prostokątem ani okręgiem, możemy użyć wielokąta, aby zdefiniować bardziej konkretny obszar :
@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"));
}
A oto odpowiednie zapytanie:
db.places.find({
location: {
$geoWithin: {
$polygon: [
[-0.1439, 51.4952],
[-0.1121, 51.4989],
[-0.13, 51.5163],
[-0.1439, 51.4952]
]
}
}
})
Zdefiniowaliśmy tylko wielokąt z jego zewnętrznymi granicami, ale możemy również dodać do niego otwory. Każdy otwór będzie listą z punktu s:
geoWithinPolygon("location", points, hole1, hole2, ...)
4.3. Przecięcie zapytania
Na koniec spójrzmy na geoIntersects zapytanie.
GeoIntersects zapytanie znajduje obiekty, które przynajmniej przecinają się z daną geometrią. Dla porównania, geoWithin znajduje obiekty, które w pełni istnieją w danej Geometrii .
To zapytanie działa z 2dsphere tylko indeks.
Zobaczmy to w praktyce na przykładzie szukania dowolnego miejsca, które przecina wielokąt :
@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"));
}
Wynikowe zapytanie:
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]
]
]
}
}
}
})