sql >> Database >  >> NoSQL >> MongoDB

MongoDB - Geospatiale kruising van twee polygoon

Dus als ik hier met een frisse geest naar kijk, staar het antwoord me recht in het gezicht. Het belangrijkste dat je al hebt aangegeven, is dat je het "kruispunt" van twee zoekopdrachten in één antwoord wilt vinden.

Een andere manier om dit te bekijken, is dat u wilt dat alle punten die door de eerste query zijn gebonden, vervolgens als "invoer" worden gebruikt voor de tweede query, enzovoort, zoals vereist. Dat is in wezen wat een kruispunt doet, maar de logica is eigenlijk letterlijk.

Gebruik dus gewoon het aggregatieraamwerk om de overeenkomende zoekopdrachten te ketenen. Bekijk voor een eenvoudig voorbeeld de volgende documenten:

{ "loc" : { "type" : "Point", "coordinates" : [ 4, 4 ] } }
{ "loc" : { "type" : "Point", "coordinates" : [ 8, 8 ] } }
{ "loc" : { "type" : "Point", "coordinates" : [ 12, 12 ] } }

En de geketende aggregatiepijplijn, slechts twee zoekopdrachten:

db.geotest.aggregate([
    { "$match": {
        "loc": {
            "$geoWithin": {
                "$box": [ [0,0], [10,10] ]
            }
        }
    }},
    { "$match": {
        "loc": {
            "$geoWithin": {
                "$box": [ [5,5], [20,20] ]
            }
        }
    }}
])

Dus als je dat logisch beschouwt, vindt het eerste resultaat de punten die binnen de grenzen van het eerste vak of de eerste twee items vallen. Die resultaten worden vervolgens verwerkt door de tweede zoekopdracht, en aangezien de nieuwe kadergrenzen beginnen bij [5,5] dat sluit het eerste punt uit. Het derde punt was al uitgesloten, maar als de kaderbeperkingen werden omgekeerd, zou het resultaat alleen hetzelfde middelste document zijn.

Hoe dit werkt is vrij uniek voor de $geoWithin query-operator in vergelijking met verschillende andere geofuncties:

De resultaten zijn dus zowel goed als slecht. Goed omdat je dit type bewerking kunt doen zonder een index, maar slecht omdat zodra de aggregatiepijplijn de verzamelingsresultaten heeft gewijzigd na de eerste querybewerking, de index niet meer kan worden gebruikt. Dus elk prestatievoordeel van een index gaat verloren bij het samenvoegen van de "set" resultaten van alles wat na de initiële Polygon/MultiPolygon zoals ondersteund.

Om deze reden zou ik u toch aanraden om de snijpuntgrenzen "buiten" de query die aan MongoDB is verstrekt, te berekenen. Hoewel het aggregatieraamwerk dit kan doen vanwege de "geketende" aard van de pijplijn, en hoewel de resulterende kruispunten steeds kleiner worden, is uw beste prestatie een enkele query met de juiste grenzen die alle indexvoordelen kan gebruiken.

Er zijn verschillende methoden om dat te doen, maar ter referentie is hier een implementatie met behulp van de JSTS bibliotheek, een JavaScript-poort van de populaire JTS bibliotheek voor Java. Er kunnen andere of andere taalpoorten zijn, maar dit heeft eenvoudige GeoJSON-parsing en ingebouwde methoden voor zaken als het verkrijgen van de snijpuntgrenzen:

var async = require('async');
    util = require('util'),
    jsts = require('jsts'),
    mongo = require('mongodb'),
    MongoClient = mongo.MongoClient;

var parser = new jsts.io.GeoJSONParser();

var polys= [
  {
    type: 'Polygon',
    coordinates: [[
      [ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 10, 0 ], [ 0, 0 ]
    ]]
  },
  {
    type: 'Polygon',
    coordinates: [[
      [ 5, 5 ], [ 5, 20 ], [ 20, 20 ], [ 20, 5 ], [ 5, 5 ]
    ]]
  }
];

var points = [
  { type: 'Point', coordinates: [ 4, 4 ]  },
  { type: 'Point', coordinates: [ 8, 8 ]  },
  { type: 'Point', coordinates: [ 12, 12 ] }
];

MongoClient.connect('mongodb://localhost/test',function(err,db) {

  db.collection('geotest',function(err,geo) {

    if (err) throw err;

    async.series(
      [
        // Insert some data
        function(callback) {
          var bulk = geo.initializeOrderedBulkOp();
          bulk.find({}).remove();
          async.each(points,function(point,callback) {
            bulk.insert({ "loc": point });
            callback();
          },function(err) {
            bulk.execute(callback);
          });
        },

        // Run each version of the query
        function(callback) {
          async.parallel(
            [
              // Aggregation
              function(callback) {
                var pipeline = [];
                polys.forEach(function(poly) {
                  pipeline.push({
                    "$match": {
                      "loc": {
                        "$geoWithin": {
                          "$geometry": poly
                        }
                      }
                    }
                  });
                });

                geo.aggregate(pipeline,callback);
              },

              // Using external set resolution
              function(callback) {
                var geos = polys.map(function(poly) {
                  return parser.read( poly );
                });

                var bounds = geos[0];

                for ( var x=1; x<geos.length; x++ ) {
                  bounds = bounds.intersection( geos[x] );
                }

                var coords = parser.write( bounds );

                geo.find({
                  "loc": {
                    "$geoWithin": {
                      "$geometry": coords
                    }
                  }
                }).toArray(callback);
              }
            ],
            callback
          );
        }
      ],
      function(err,results) {
        if (err) throw err;
        console.log(
          util.inspect( results.slice(-1), false, 12, true ) );
        db.close();
      }
    );

  });

});

Het gebruik van de volledige GeoJSON "Polygon"-representaties daar, omdat dit zich vertaalt naar wat JTS kan begrijpen en ermee kan werken. De kans is groot dat alle input die je zou kunnen ontvangen voor een echte aanvraag ook in dit formaat zou zijn in plaats van het toepassen van gemakken zoals $box .

Het kan dus worden gedaan met het aggregatieraamwerk, of zelfs parallelle query's die de "set" resultaten samenvoegen. Maar hoewel het aggregatieraamwerk het misschien beter doet dan het extern samenvoegen van reeksen resultaten, zullen de beste resultaten altijd komen door eerst de grenzen te berekenen.



  1. Deserialisering van de interface in MongoDB met behulp van C# - Onbekende discriminatorwaarde

  2. Hoe alleen specifieke velden voor een query retourneren in Spring Data MongoDB?

  3. JavaScript wijst geen element toe aan het object

  4. tijdreeksen opslaan in redis