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

MongoDB - Геопространствено пресичане на два полигона

Така че, гледайки на това със свеж ум, отговорът е да ме гледа в очите. Ключовото нещо, което вече казахте, е, че искате да намерите „пресечната точка“ на две заявки в един отговор.

Друг начин да разгледате това е, че искате всички точки, обвързани от първата заявка, след това да бъдат "входни" за втората заявка и така нататък, както се изисква. По същество това е, което прави пресичането, но логиката всъщност е буквална.

Така че просто използвайте рамката за агрегиране за верижиране на съвпадащите заявки. За прост пример разгледайте следните документи:

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

И верижният тръбопровод за агрегиране, само две заявки:

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

Така че, ако приемете това логично, първият резултат ще намери точките, които попадат в границите на първоначалното поле или първите два елемента. След това върху тези резултати се действа от втората заявка и тъй като новите граници на полето започват от [5,5] което изключва първата точка. Третата точка вече беше изключена, но ако ограниченията на кутията бяха обърнати, резултатът щеше да бъде само същият среден документ.

Как работи това е доста уникално за $geoWithin оператор на заявка в сравнение с различни други гео функции:

Така че резултатите са както добри, така и лоши. Добре е, че можете да извършвате този тип операция без индекс на място, но е лошо, защото след като тръбопроводът за агрегиране е променил резултатите от събирането след първата операция на заявка, не може да се използва допълнителен индекс. Така че всяка полза от производителността на даден индекс се губи при обединяването на резултатите от „набора“ от всичко след първоначалния Многоъгълник/Многополигон, както се поддържа.

Поради тази причина бих препоръчал да изчислите границите на пресичане „извън“ на заявката, изпратена до MongoDB. Въпреки че рамката за агрегиране може да направи това поради „верижния“ характер на тръбопровода и въпреки че получените пресичания ще стават все по-малки и по-малки, най-добрата ви производителност е единична заявка с правилните граници, която може да използва всички предимства на индекса.

Има различни методи за това, но за справка тук е реализация, използваща JSTS библиотека, която е JavaScript порт на популярния JTS библиотека за Java. Може да има други или други езикови портове, но това има прост анализ на GeoJSON и вградени методи за такива неща като получаване на границите на пресичане:

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

  });

});

Използване на пълните GeoJSON "Polygon" представяния там, тъй като това се превежда до това, което JTS може да разбере и с което да работи. Вероятно всяка информация, която можете да получите за истинско приложение, също ще бъде в този формат, вместо да прилагате удобства като $box .

Така че може да се направи с рамката за агрегиране или дори с паралелни заявки, обединяващи „набора“ от резултати. Но въпреки че рамката за агрегиране може да го направи по-добре от външно сливане на набори от резултати, най-добрите резултати винаги ще дойдат от първо изчисляване на границите.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Команда mongodump на Mongodb, грешка при изпълнение на javascript

  2. MongoDB $oid срещу ObjectId

  3. Създайте филтърна агрегация през пролетта

  4. Как MongoDB дава възможност за машинно обучение

  5. MongoDB Java драйвер, създаващ база данни и колекция