MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Wsparcie geoprzestrzenne w MongoDB

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

  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. mongodb $w limicie

  2. Analizowanie powolnych zapytań w MongoDB

  3. Obiekty zagnieżdżone w schematach mangusty

  4. Grupa sterowników MongoDB .NET według zakresu czasu

  5. Jak uruchomić Mongo DB z systemu Windows?