sql >> Database teknologi >  >> NoSQL >> MongoDB

Geospatial support i MongoDB

1. Oversigt

I denne øvelse vil vi udforske den geospatiale støtte i MongoDB.

Vi vil diskutere, hvordan man gemmer geospatiale data, geo-indeksering og geospatial søgning. Vi vil også bruge flere geospatiale søgeforespørgsler som f.eks. nær , geoWithin , og geoIntersects .

2. Lagring af geospatiale data

Lad os først se, hvordan man gemmer geospatiale data i MongoDB.

MongoDB understøtter flere GeoJSON typer til at gemme geospatiale data. Gennem vores eksempler vil vi hovedsageligt bruge Punkt og Polygon typer.

2.1. Peg

Dette er den mest grundlæggende og almindelige GeoJSON type, ogdet bruges til at repræsentere et bestemt punkt på gitteret .

Her har vi et simpelt objekt på vores steder samling, der har felt placering som et Punkt :

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

Bemærk, at længdegradsværdien kommer først, derefter breddegraden.

2.2. Polygon

Polygon er lidt mere kompleks GeoJSON type.

Vi kan bruge Polygon at definere et område med dets ydre grænser og også indvendige huller, hvis det er nødvendigt.

Lad os se et andet objekt, der har sin placering defineret som en Polygon :

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

I dette eksempel definerede vi en række punkter, der repræsenterer ydre grænser. Vi skal også lukke grænsen, så det sidste punkt er lig med det første punkt.

Bemærk, at vi skal definere de ydre grænsepunkter i retning mod uret og hulgrænser i urets retning.

Ud over disse typer er der også mange andre typer som LineString, MultiPoint, MultiPolygon, MultiLineString, og GeometryCollection.

3. Geospatial indeksering

For at udføre søgeforespørgsler på de geospatiale data, vi har gemt, skal vi oprette et geospatialt indeks på vores placering felt.

Vi har grundlæggende to muligheder:2d og 2dsphere .

Men lad os først definere vores stedforsamling :

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

3.1. 2d Geospatialt indeks

2d indeks gør det muligt for os at udføre søgeforespørgsler, der fungerer baseret på 2d-planberegninger.

Vi kan oprette en 2d indeks på placeringen felt i vores Java-applikation som følger:

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

Selvfølgelig kan vi gøre det samme i mongoen skal:

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

3.2. 2dsphere Geospatialt indeks

2dsphere indeks understøtter forespørgsler, der fungerer baseret på kugleberegninger.

På samme måde kan vi oprette en 2dsphere indeks i Java ved hjælp af de samme indekser klasse som ovenfor:

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

Eller i mongo skal:

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

4. Søgning ved hjælp af geospatiale forespørgsler

Lad os nu, til den spændende del, søge efter objekter baseret på deres placering ved hjælp af geospatiale forespørgsler.

4.1. Tæt på forespørgsel

Lad os starte med nær. Vi kan bruge nær forespørgsel for at søge efter steder inden for en given afstand.

tæt på forespørgsel virker med både 2d og 2dsphere indekser.

I det næste eksempel søger vi efter steder, der er mindre end 1 km og mere end 10 meter væk fra den givne position:

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

Og den tilsvarende forespørgsel i mongo skal:

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

Bemærk, at resultaterne er sorteret fra nærmeste til fjerneste.

På samme måde, hvis vi bruger en meget langt væk placering, finder vi ingen steder i nærheden:

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

Vi har også nearSphere metode, der fungerer præcis som nær, bortset fra at den beregner afstanden ved hjælp af sfærisk geometri.

4.2. Inden for forespørgsel

Dernæst vil vi udforske geoWithin forespørgsel.

The geoWithin forespørgsel sætter os i stand til at søge efter steder, der eksisterer fuldt ud inden for en given geometri , som en cirkel, boks eller polygon. Dette fungerer også med både 2d og 2dsphere indekser.

I dette eksempel leder vi efter steder, der findes inden for en radius på 5 km fra den givne midterposition:

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

Bemærk, at vi skal transformere afstanden fra km til radian (du skal bare dividere med Jordens radius).

Og den resulterende forespørgsel:

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

Dernæst søger vi efter alle steder, der findes i en rektangel "boks". Vi skal definere boksen ved dens nederste venstre position og øverste højre position:

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

Her er den tilsvarende forespørgsel i mongo skal:

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

Endelig, hvis det område, vi ønsker at søge indenfor, ikke er et rektangel eller en cirkel, kan vi bruge en polygon til at definere et mere specifikt område :

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

Og her er den tilsvarende forespørgsel:

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

Vi definerede kun en polygon med dens ydre grænser, men vi kan også tilføje huller til den. Hvert hul vil være en liste af Punkt s:

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

4.3. Skæringsforespørgsel

Lad os endelig se på geoIntersects forespørgsel.

The geoIntersects forespørgslen finder objekter, der i det mindste skærer en given geometri. Til sammenligning geoWithin finder objekter, der eksisterer fuldt ud inden for en given geometri .

Denne forespørgsel fungerer med 2dsphere kun indeks.

Lad os se dette i praksis med et eksempel på at lede efter ethvert sted, der skærer en Polygon :

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

Den resulterende forespørgsel:

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. Installation af Redis på CentOS 8

  2. Sådan implementerer du ClusterControl på AWS for at administrere din clouddatabase

  3. Hvordan beskytter man adgangskodefeltet i Mongoose/MongoDB, så det ikke vender tilbage i en forespørgsel, når jeg udfylder samlinger?

  4. Java Client til at forbinde ElasticCache Redis Cache Node