sql >> Database >  >> NoSQL >> MongoDB

Geospatiale ondersteuning in MongoDB

1. Overzicht

In deze zelfstudie onderzoeken we de Geospatial-ondersteuning in MongoDB.

We bespreken hoe u geospatiale gegevens, geo-indexering en geospatiaal zoeken kunt opslaan. We zullen ook meerdere geospatiale zoekopdrachten gebruiken, zoals in de buurt van , geoWithin , en geoIntersects .

2. Geospatiale gegevens opslaan

Laten we eerst eens kijken hoe we geospatiale gegevens kunnen opslaan in MongoDB.

MongoDB ondersteunt meerdere GeoJSON typen om geospatiale gegevens op te slaan. In onze voorbeelden gebruiken we voornamelijk het Punt en Polygoon soorten.

2.1. Punt

Dit is de meest eenvoudige en gebruikelijke GeoJSON type, en het wordt gebruikt om één specifiek punt op het raster weer te geven .

Hier hebben we een eenvoudig object, in onze plaatsen collectie, dat veld locatie . heeft als een Punt :

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

Merk op dat de lengtegraad eerst komt, dan de breedtegraad.

2.2. Veelhoek

Veelhoek is een beetje ingewikkelder GeoJSON typ.

We kunnen Polygoon . gebruiken om een ​​gebied met zijn buitengrenzen te definiëren en ook inwendige gaten indien nodig.

Laten we een ander object bekijken waarvan de locatie is gedefinieerd als een Polygoon :

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

In dit voorbeeld hebben we een reeks punten gedefinieerd die buitengrenzen vertegenwoordigen. We moeten ook de grens sluiten zodat het laatste punt gelijk is aan het eerste punt.

Merk op dat we de buitengrenzen moeten definiëren in tegenwijzerzin en hole-grenzen in wijzerzin.

Naast deze typen zijn er ook vele andere typen zoals LineString, MultiPoint, MultiPolygon, MultiLineString, en GeometryCollection.

3. Geospatiale indexering

Om zoekopdrachten uit te voeren op de geospatiale gegevens die we hebben opgeslagen, moeten we een geospatiale index maken op onze locatie veld.

We hebben in principe twee opties:2d en 2dsphere .

Maar laten we eerst onze plaatsen definiëren cverzameling :

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

3.1. 2d Geospatiale index

De 2d index stelt ons in staat om zoekopdrachten uit te voeren die werken op basis van 2D-vlakberekeningen.

We kunnen een 2d . maken index op de locatie veld in onze Java-toepassing als volgt:

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

Natuurlijk kunnen we hetzelfde doen in de mongo schaal:

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

3.2. 2dsphere Geospatiale index

De 2dsphere index ondersteunt zoekopdrachten die werken op basis van bolberekeningen.

Op dezelfde manier kunnen we een 2dsphere . maken index in Java met dezelfde Indexen klasse zoals hierboven:

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

Of in de mongo schaal:

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

4. Zoeken met behulp van ruimtelijke zoekopdrachten

Laten we nu, voor het spannende deel, objecten zoeken op basis van hun locatie met behulp van geospatiale zoekopdrachten.

4.1. Bijna zoekopdracht

Laten we beginnen met in de buurt. We kunnen de nabije . gebruiken zoekopdracht om naar plaatsen binnen een bepaalde afstand te zoeken.

De nabij query werkt met beide 2d en 2dsphere indexen.

In het volgende voorbeeld zoeken we naar plaatsen die minder dan 1 km en meer dan 10 meter verwijderd zijn van de gegeven positie:

@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"));
}

En de bijbehorende zoekopdracht in de mongo schaal:

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

Merk op dat de resultaten zijn gesorteerd van dichtstbijzijnde naar verste.

Evenzo, als we een locatie heel ver weg gebruiken, zullen we geen plaatsen in de buurt vinden:

@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());
}

We hebben ook de nearSphere methode, die precies werkt als near, behalve dat het de afstand berekent met behulp van sferische geometrie.

4.2. Binnen zoekopdracht

Vervolgens verkennen we de geoWithin vraag.

De geoWithin zoekopdracht stelt ons in staat om te zoeken naar plaatsen die volledig bestaan ​​binnen een bepaalde Geometrie , zoals een cirkel, doos of veelhoek. Dit werkt ook met beide 2d en 2dsphere indices.

In dit voorbeeld zoeken we naar plaatsen binnen een straal van 5 km vanaf de opgegeven middenpositie:

@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"));
}

Merk op dat we de afstand van km naar radiaal moeten transformeren (gewoon delen door de straal van de aarde).

En de resulterende vraag:

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

Vervolgens zoeken we naar alle plaatsen die zich binnen een rechthoekig "vak" bevinden. We moeten het vak definiëren aan de hand van de positie linksonder en rechtsboven:

@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"));
}

Dit is de overeenkomstige zoekopdracht in mongo schaal:

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

Ten slotte, als het gebied waarin we willen zoeken geen rechthoek of cirkel is, kunnen we een veelhoek gebruiken om een ​​specifieker gebied te definiëren :

@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"));
}

En hier is de bijbehorende vraag:

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

We hebben alleen een polygoon met zijn buitengrenzen gedefinieerd, maar we kunnen er ook gaten aan toevoegen. Elke hole is een Lijst van Punt s:

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

4.3. Zoekopdracht kruisen

Laten we tot slot eens kijken naar de geoIntersects vraag.

De geoIntersects query vindt objecten die elkaar tenminste kruisen met een gegeven Geometrie. Ter vergelijking:geoWithin vindt objecten die volledig bestaan ​​binnen een gegeven Geometrie .

Deze query werkt met de 2dsphere alleen index.

Laten we dit in de praktijk bekijken, met een voorbeeld van het zoeken naar een plaats die een Polygoon kruist :

@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"));
}

De resulterende vraag:

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. Express change-sessie elk verzoek

  2. Meerdere groepsbewerkingen met behulp van het Mongo-aggregatieraamwerk

  3. Node.js-bestandsupload (Express 4, MongoDB, GridFS, GridFS-Stream)

  4. Hoe kan ik mijn mongodb repareren?