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)的情況為
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的方向性研究
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(相交)的支援是正常的,超過半球或者接近半球時,判定方式讓人摸不著頭腦。如果有懂的大神看見了。麻煩指教以下。