ですから、これを新鮮な心で見ると、答えは私を正面から見つめています。すでに述べた重要なことは、1つの応答で2つのクエリの「共通部分」を見つけたいということです。
これを確認する別の方法は、最初のクエリによってバインドされたすべてのポイントを2番目のクエリの「入力」にし、必要に応じて以下同様に設定することです。これは基本的に交差点が行うことですが、ロジックは実際には文字通りです。
したがって、集約フレームワーク を使用するだけです。 一致するクエリをチェーンします。簡単な例として、次のドキュメントについて考えてみます。
{ "loc" : { "type" : "Point", "coordinates" : [ 4, 4 ] } }
{ "loc" : { "type" : "Point", "coordinates" : [ 8, 8 ] } }
{ "loc" : { "type" : "Point", "coordinates" : [ 12, 12 ] } }
そして、連鎖集約パイプライン、たった2つのクエリ:
db.geotest.aggregate([
{ "$match": {
"loc": {
"$geoWithin": {
"$box": [ [0,0], [10,10] ]
}
}
}},
{ "$match": {
"loc": {
"$geoWithin": {
"$box": [ [5,5], [20,20] ]
}
}
}}
])
したがって、それを論理的に考えると、最初の結果は、最初のボックスまたは最初の2つの項目の範囲内にあるポイントを見つけます。次に、これらの結果は2番目のクエリによって処理され、新しいボックスの境界は[5,5]
で始まるためです。 それは最初のポイントを除外します。 3番目のポイントはすでに除外されていますが、ボックスの制限を逆にすると、結果は同じ中間ドキュメントのみになります。
これがどのように機能するかは、 $geoWithin
他のさまざまな地理関数と比較したクエリ演算子:
したがって、結果は良いことも悪いこともあります。インデックスを設定せずにこのタイプの操作を実行できるという点では優れていますが、最初のクエリ操作後に集計パイプラインが収集結果を変更すると、それ以上のインデックスを使用できなくなるため、悪い結果になります。したがって、サポートされている最初のポリゴン/マルチポリゴンの後の「セット」結果をマージすると、インデックスのパフォーマンス上の利点は失われます。
このため、MongoDBに発行されたクエリの「外側」で交差境界を計算することをお勧めします。パイプラインの「チェーン」の性質により集約フレームワークがこれを実行でき、結果の交差がますます小さくなりますが、最高のパフォーマンスは、すべてのインデックスの利点を使用できる正しい境界を持つ単一のクエリです。
これを行うにはさまざまな方法がありますが、参考のために、JSTS を使用した実装を示します。 ライブラリ。これは、人気のあるJTS のJavaScriptポートです。 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の完全な「ポリゴン」表現を使用すると、JTSが理解して操作できるものに変換されます。 $box
。
したがって、集計フレームワークを使用して実行することも、結果の「セット」をマージする並列クエリを使用して実行することもできます。ただし、集計フレームワークは、結果のセットを外部でマージするよりも優れている場合がありますが、最良の結果は常に、最初に境界を計算することから得られます。