Sokongan Geospatial di MongoDB

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan meneroka sokongan Geospatial di MongoDB.

Kami akan membincangkan cara menyimpan data geospasial, pengindeksan geo, dan carian geospasial. Kami juga akan menggunakan pelbagai pertanyaan carian geospasial seperti dekat , geoWithin , dan geoIntersects .

2. Menyimpan Data Geospatial

Pertama, mari kita lihat bagaimana menyimpan data geospasial di MongoDB.

MongoDB menyokong pelbagai jenis GeoJSON untuk menyimpan data geospatial. Sepanjang contoh kami, kami akan menggunakan jenis Titik dan Poligon .

2.1. Titik

Ini adalah jenis GeoJSON yang paling asas dan biasa , dan ia digunakan untuk mewakili satu titik tertentu di grid .

Di sini, kami mempunyai objek sederhana, di koleksi tempat kami , yang memiliki lokasi lapangan sebagai Titik :

{ "name": "Big Ben", "location": { "coordinates": [-0.1268194, 51.5007292], "type": "Point" } }

Perhatikan bahawa nilai garis bujur didahulukan, kemudian garis lintang.

2.2. Poligon

Poligon adalah jenis GeoJSON yang agak kompleks .

Kita boleh menggunakan Polygon untuk menentukan kawasan dengan sempadan luarnya dan juga lubang dalaman jika diperlukan.

Mari lihat objek lain yang lokasinya ditakrifkan sebagai Poligon :

{ "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" } }

Dalam contoh ini, kami menentukan susunan titik yang mewakili batas luaran. Kita juga harus menutup batas sehingga titik terakhir sama dengan titik pertama.

Perhatikan bahawa kita perlu menentukan titik batas luaran ke arah berlawanan arah jarum jam dan batas lubang ke arah arah jam.

Selain jenis ini, terdapat juga banyak jenis lain seperti LineString, MultiPoint, MultiPolygon, MultiLineString, dan GeometryCollection.

3. Pengindeksan Geospatial

Untuk melakukan pertanyaan carian pada data geospasial yang kami simpan, kami perlu membuat indeks geospasial di bidang lokasi kami .

Kami pada dasarnya mempunyai dua pilihan: 2d dan 2dsphere .

Tetapi pertama, mari kita menentukan tempat-tempat yang kami c ollection :

MongoClient mongoClient = new MongoClient(); MongoDatabase db = mongoClient.getDatabase("myMongoDb"); collection = db.getCollection("places");

3.1. Indeks Geospatial 2d

The 2d indeks membolehkan kita untuk melaksanakan permintaan carian yang kerja berdasarkan pengiraan pesawat 2d.

Kita dapat membuat indeks 2d di bidang lokasi dalam aplikasi Java kita seperti berikut:

collection.createIndex(Indexes.geo2d("location"));

Sudah tentu, kita boleh melakukan perkara yang sama di cengkerang mongo :

db.places.createIndex({location:"2d"})

3.2. Indeks Geospatial 2dsphere

The 2dsphere indeks menyokong pertanyaan yang kerja berdasarkan pengiraan sfera.

Begitu juga, kita dapat membuat indeks 2dsphere di Java menggunakan kelas Indeks yang sama seperti di atas:

collection.createIndex(Indexes.geo2dsphere("location"));

Atau di cengkerang mongo :

db.places.createIndex({location:"2dsphere"})

4. Mencari Dengan Menggunakan Pertanyaan Geospatial

Sekarang, untuk bahagian yang menarik, mari kita mencari objek berdasarkan lokasinya menggunakan pertanyaan geospasial.

4.1. Pertanyaan Berhampiran

Mari mulakan dengan dekat. Kita boleh menggunakan pertanyaan dekat untuk mencari tempat dalam jarak tertentu.

Yang berhampiran Pertanyaan bekerja dengan kedua-dua 2d dan 2dsphere indeks .

Dalam contoh seterusnya, kami akan mencari tempat yang berjarak kurang dari 1 km dan lebih dari 10 meter dari kedudukan yang diberikan:

@Test public void givenNearbyLocation_whenSearchNearby_thenFound() { Point currentLoc = new Point(new Position(-0.126821, 51.495885)); FindIterable result = collection.find( Filters.near("location", currentLoc, 1000.0, 10.0)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }

Dan pertanyaan yang sesuai di shell mongo :

db.places.find({ location: { $near: { $geometry: { type: "Point", coordinates: [-0.126821, 51.495885] }, $maxDistance: 1000, $minDistance: 10 } } })

Perhatikan bahawa hasilnya disusun dari yang terdekat ke yang paling jauh.

Begitu juga, jika kita menggunakan lokasi yang sangat jauh, kita tidak akan menemui tempat yang berdekatan:

@Test public void givenFarLocation_whenSearchNearby_thenNotFound() { Point currentLoc = new Point(new Position(-0.5243333, 51.4700223)); FindIterable result = collection.find( Filters.near("location", currentLoc, 5000.0, 10.0)); assertNull(result.first()); }

We also have the nearSphere method, which acts exactly like near, except it calculates the distance using spherical geometry.

4.2. Within Query

Next, we'll explore the geoWithin query.

The geoWithin query enables us to search for places that fully exist within a given Geometry, like a circle, box, or polygon. This also works with both 2d and 2dsphere indices.

In this example, we're looking for places that exist within a 5 km radius from the given center position:

@Test public void givenNearbyLocation_whenSearchWithinCircleSphere_thenFound() { double distanceInRad = 5.0 / 6371; FindIterable result = collection.find( Filters.geoWithinCenterSphere("location", -0.1435083, 51.4990956, distanceInRad)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }

Note that we need to transform the distance from km to radian (just divide by Earth's radius).

And the resulting query:

db.places.find({ location: { $geoWithin: { $centerSphere: [ [-0.1435083, 51.4990956], 0.0007848061528802386 ] } } })

Next, we'll search for all places that exist within a rectangle “box”. We need to define the box by its lower left position and upper right position:

@Test public void givenNearbyLocation_whenSearchWithinBox_thenFound() { double lowerLeftX = -0.1427638; double lowerLeftY = 51.4991288; double upperRightX = -0.1256209; double upperRightY = 51.5030272; FindIterable result = collection.find( Filters.geoWithinBox("location", lowerLeftX, lowerLeftY, upperRightX, upperRightY)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }

Here's the corresponding query in mongo shell:

db.places.find({ location: { $geoWithin: { $box: [ [-0.1427638, 51.4991288], [-0.1256209, 51.5030272] ] } } })

Finally, if the area we want to search within isn't a rectangle or a circle, we can use a polygon to define a more specific area:

@Test public void givenNearbyLocation_whenSearchWithinPolygon_thenFound() { ArrayList
    
      points = new ArrayList
     
      (); 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 result = collection.find( Filters.geoWithinPolygon("location", points)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }
     
    

And here's the corresponding query:

db.places.find({ location: { $geoWithin: { $polygon: [ [-0.1439, 51.4952], [-0.1121, 51.4989], [-0.13, 51.5163], [-0.1439, 51.4952] ] } } })

We only defined a polygon with its exterior bounds, but we can also add holes to it. Each hole will be a List of Points:

geoWithinPolygon("location", points, hole1, hole2, ...)

4.3. Intersect Query

Finally, let's look at the geoIntersects query.

The geoIntersects Pertanyaan penemuan objek yang sekurang-kurangnya bersilang dengan diberikan Geometry. Sebagai perbandingan, geoWithin menemui objek yang wujud sepenuhnya dalam Geometri tertentu .

Pertanyaan ini hanya berfungsi dengan indeks 2dsphere .

Mari kita lihat ini dalam praktik, dengan contoh mencari tempat yang bersilang dengan Poligon :

@Test public void givenNearbyLocation_whenSearchUsingIntersect_thenFound() { ArrayList positions = new ArrayList(); 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 result = collection.find( Filters.geoIntersects("location", geometry)); assertNotNull(result.first()); assertEquals("Hyde Park", result.first().get("name")); }

Pertanyaan yang dihasilkan:

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] ] ] } } } })

5. Kesimpulan

Dalam artikel ini, kami belajar bagaimana menyimpan data geospasial di MongoDB dan melihat perbezaan antara indeks geospasial 2d dan 2dsphere . Kami juga belajar bagaimana mencari di MongoDB menggunakan pertanyaan geospatial.

Seperti biasa, kod sumber penuh untuk contoh boleh didapati di GitHub.