1. 程式人生 > >mongo-2ds索引對超過半球範圍的適用性測試

mongo-2ds索引對超過半球範圍的適用性測試

  以下測試均基於mongo v4.0 win10


一、GeoJSON

  GeoJSON是一種基於json的經緯度描述資料格式。在這裡主要服務於2dsphere索引查詢。

  基本格式  <type:"",coordinates:[]> ,type 對應的型別有Point、LineString(老版本有寫Line的)、Polygon、MultiPoint、MultiLineString、MultiPolygon。coordinates對應格式可參照【表1】。

更詳細的請自行百度。

二、2dsphere索引。

  基於地球的經緯度。舉例如下

  存在點 【28,2】,則以下區域是否與該點存在交集($geoIntersects)的情況為

表1
  type coordinates 是否包含
1 Point [28,2]
2 LineString [[28,1],[28,3]]
3 LineString [[28,3],[28,1]]
4 LineString [[27,2],[29,2]]
5 LineString [[29,2],[27,2]]
6 Polygon [[[27,1],[27,3],[29,3],[29,1],[27,1]]]
7 Polygon [[[27,3],[27,1],[29,1],[29,3],[27,3]]]
8 Polygon [[[29,1],[29,3],[27,3],[27,1],[29,1]]]
9 Polygon [[[29,3],[29,1],[27,1],[27,3],[29,3]]]

  從LineString的四個例子中我們可以得出:getIntersects對LineString的支援性並不高,不過考慮到現實場景中的查詢,都是以一個面積為參考的,所以LineString的測驗只瞭解一個結果,不再深入研究。

  從Polygon的四個例子中我們可以暫時得出:GeoJSON的陣列是不在乎順序的,為了驗證這個觀點,我們再補做方向性的測試資料。

三、geoIntersects的方向性研究

表2
  type coordinates 是否包含
1 Polygon [[[27,1],[29,1],[29,3],[27,3],[27,1]]]
2 Polygon [[[27,3],[29,3],[29,1],[27,1],[27,3]]]
3 Polygon [[[29,3],[27,3],[27,1],[29,1],[29,3]]]
4 Polygon [[[29,1],[27,1],[27,3],[29,3],[29,1]]]
5 Polygon [[[29,1],[-27,3],[-27,3],[29,3],[29,1]]]
6      
7  Polygon [[[-179,1],[27,1],[27,3],[-179,3],[-179,1]]]
8 Polygon [[[0,90],[180,90],[180,-90],[0,-90],[0,90]]]
9 Polygon [[[0,90],[0,-90],[180,-90],[180,90],[0,90]]]
10 Polygon [[[0,-90],[0,90],[180,90],[180,-90],[0,-90]]]
11 Polygon [[[0,-90],[180,-90],[180,90],[0,90],[0,-90]]]
12 Polygon [[[180,-90],[0,-90],[0,90],[180,90],[180,-90]]]
13 Polygon [[[180,90],[0,90],[0,-90],[180,-90],[180,90]]]
14 Polygon [[[180,90],[180,-90],[0,-90],[0,90],[180,90]]]
16 Polygon [[[180,-90],[180,90],[0,90],[0,-90],[180,-90]]]

  根據【表2編號1-5】到驗證了以上的猜想,GeoJSON陣列代表的區域是 : 陣列中的點連線成線,將經緯度網劃分成兩塊,取面積小的那一塊作為選定區域,進行篩選判定

  但是如果所選範圍擴大到整個半球甚至更大,GeoJSON的陣列該如何選擇判定區域?從【表2編號7-16】中,我們什麼也不能得出來,整個查詢呈現出一種混亂的狀態。又進行大量的對比實驗,做了各種統計分析圖後,結論依舊混亂,我甚至還去文具店買了一個地球儀。。。

  網上進行了大量查詢,最後還是在官網找到了一些資訊。3.0版本以後$within(表示包含)、$geoIntersects的Polygon查詢支援新的引數:crs。可以指定特殊的索引範圍劃分方式。如EPSG:4326,判定區域時具有嚴格的逆時針順序。更多更全的知識請移步 https://docs.mongodb.com/manual/reference/operator/query/geoIntersects/#geointersects-big-poly

  但是經過測驗發現,4.0版本默認了EPSG:4326,加與不加,影響不大,測驗如下($within與$geoIntersects結果一致):

1 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[27,1],[29,1],[29,3],[27,3],[27,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
2 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
3 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[29,1],[29,3],[27,3],[27,1],[29,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
4 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
5 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[29,3],[27,3],[27,1],[29,1],[29,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
6 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
7 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[27,3],[27,1],[29,1],[29,3],[27,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
8 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
9 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[27,1],[27,3],[29,3],[29,1],[27,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});

  甚至會出現這種莫名其妙的結果:

> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,1],[79,1],[79,3],[0,3],[0,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
{ "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } }
{ "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
{ "_id" : ObjectId("5bb4290d0c2287bbf737427d"), "name" : "ww wu", "age" : 40, "add" : { "type" : "Point", "coordinates" : [ 50, 2 ] } }
> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,1],[124,1],[124,3],[0,3],[0,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
{ "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } }
{ "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,1],[120,1],[120,3],[0,3],[0,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
{ "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } }
{ "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
{ "_id" : ObjectId("5bb4290d0c2287bbf737427d"), "name" : "ww wu", "age" : 40, "add" : { "type" : "Point", "coordinates" : [ 50, 2 ] } }

  可以看出,mongo對於2ds索引的支援度並不高。那麼我們完全按照官方文件的格式進行測試,看能不能得出一個有規律的結果。

  官方文件的格式:

  

db.places.find(
   {
     loc: {
       $geoIntersects: {
          $geometry: {
             type : "Polygon",
             coordinates: [
               [
                 [ -100, 60 ], [ -100, 0 ], [ -100, -60 ], [ 100, -60 ], [ 100, 60 ], [ -100, 60 ]
               ]
             ],
             crs: {
                type: "name",
                properties: { name: "urn:x-mongodb:crs:strictwinding:EPSG:4326" }
             }
          }
       }
     }
   }
)

   格式定義如下:

  1、首尾相接(以前測試的陣列都是滿足的)

  2、逆時針方向書寫

> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,3],[0,-1],[179,-1],[179,3],[0,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
{ "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } }
{ "_id" : ObjectId("5bcb0145377b37ef8e160bcb"), "add" : { "type" : "Point", "coordinates" : [ 2, 4 ] } }
{ "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } }
{ "_id" : ObjectId("5bcb0158377b37ef8e160bcc"), "add" : { "type" : "Point", "coordinates" : [ 2, -2 ] } }
{ "_id" : ObjectId("5bb4290d0c2287bbf737427d"), "name" : "ww wu", "age" : 40, "add" : { "type" : "Point", "coordinates" : [ 50, 2 ] } }
> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,3],[0,1],[179,1],[179,3],[0,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});

  講道理,完全沒有道理。


  總結,2ds對於小範圍的within(包含),geoIntersects(相交)的支援是正常的,超過半球或者接近半球時,判定方式讓人摸不著頭腦。如果有懂的大神看見了。麻煩指教以下。