MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Геопространствена поддръжка в MongoDB

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

  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Вмъкване на елемент във вложени масиви в MongoDB

  2. MongoDB Schema Design - Много малки документи или по-малко големи документи?

  3. Най-добрият междинен софтуер за съхранение на сесии за Express + MongoDB

  4. Mongo DB Java 3.x драйвер - групиране по заявка

  5. Премахване на database.yml при използване на Mongoid в Rails 3.2