在没有 [long,lat] 数组的情况下运行 mongodb $geoWithin
Posted
技术标签:
【中文标题】在没有 [long,lat] 数组的情况下运行 mongodb $geoWithin【英文标题】:Running mongodb $geoWithin without a [long,lat] array 【发布时间】:2016-03-09 10:05:29 【问题描述】:我有一个 mongodb $geoWithin
查询如下
db.test.find(
'loc':
$geoWithin:
$geometry:
type : "Polygon" ,
coordinates: [[list of co-ordinates]]
);
所以这里查询在loc
字段上运行,该字段是lng
、lat
值的数组,但幸运的是在我的数据中,lat
和lng
值位于 2 个不同的字段中,例如
lat:12,
long:122
在这种情况下,我该如何运行上述查询?
【问题讨论】:
【参考方案1】:您真正能做的最好的事情就是转换您的文档以更好地存储数据。根据偏好,您可能应该选择 GeoJSON 格式。不过以后再说吧。
幸运的是,$geoWithin
实际上并不“需要”索引(但拥有一个索引仍然是更好的选择),您实际上可以使用聚合框架“即时”进行转换:
转换“动态”
希望你至少有MongoDB 2.6,还有$map
:
db.collection.aggregate([
// Tranform to array
"$project":
"location":
"$map":
"input": ["lng","lat"],
"as": "el",
"in":
"$cond": [
"$eq": [ "$$el", "lng" ] ,
"$long",
"$lat"
,
// Then match
"$match":
"location":
"$geoWithin":
"$geometry":
"type": "Polygon" ,
"coordinates": [[list of co-ordinates]]
])
MongoDB 3.2 具有更简单的语法:
db.collection.aggregate([
// Tranform to array - pretty simple huh!
"$project":
"location": ["$long","$lat"]
,
// Then match
"$match":
"location":
"$geoWithin":
"$geometry":
"type": "Polygon" ,
"coordinates": [[list of co-ordinates]]
])
或者如果您还有 MongoDB 2.4 - 升级!好的,那就用这个吧:
db.collection.aggregate([
// Add an array field
"$project":
"long": 1,
"lat": 1
"location": "$const": [ "A", "B" ]
,
// Unwind it
"$unwind": "$location" ,
// Group back and map it!
"$group":
"_id": "$_id",
"location":
"$push":
"$cond": [
"$eq": [ "$location", "A" ] ,
"$long",
"$lat"
]
,
// Then match
"$match":
"location":
"$geoWithin":
"$geometry":
"type": "Polygon" ,
"coordinates": [[list of co-ordinates]]
])
“永久”转换
但最好的情况是永久更改文档结构。做到这一点的现代方法是在bulk 中,例如:
var ops = [];
db.collection.find().forEach(function(doc)
ops.push(
"updateOne":
"filter": "_id": doc._id ,
"update":
"$set":
"location":
"type": "Point",
"coordinates": [doc.long,doc.lat]
,
"$unset": "long": "", "lat": ""
);
// Send once in 1000 only
if ( ops.length % 1000 == 0 )
db.collection.bulkWrite(ops);
ops = [];
)
// Clear remaining queue
if ( ops.length > 0 )
db.collection.bulkWrite(ops);
但一般来说,循环源文档并更新每个文档以创建新的“位置”字段。然后当然是“索引”它:
db.collection.createIndex( "location": "2dsphere" )
现在文档看起来像这样并且实际上有一个索引,您可以直接使用常规的$geoWithin
查询,这也可以从当前的索引数据中更快地工作。
【讨论】:
试图在查询db.getCollection('test').aggregate([ // Tranform to array - pretty simple huh! "$project": "location": ["longitudeDegrees","latitudeDegrees"] , // Then match "$match": "location": "$geoWithin": "$geometry": "type": "Polygon" , "coordinates": [[[0,0], [0,6], [6,6], [6,0] ]] ])
下运行并收到此错误异常:对象表达式中不允许的字段类型数组(在“位置”)
@BiJ 仔细阅读。这仅对最新的 MongoDB 3.2 版本有效。您可能需要$map
版本,这就是为什么它在顶部。
@BiJ 你也错过了变量。如果您的字段实际上命名为"longitudeDegrees"
,那么您宁愿这样做: "$project": "location": [ "$longitudeDegrees","$latitudeDegrees"]
。但同样,这只适用于 MongoDB 3.2.x 版本。其他的发布方式都展示了。
对不起我的错误。我正在运行 mongo 3.0.5,所以我想这不适用于我。我尝试使用具有以下坐标 [0,0]、[0,6]、[6,6]、[6,0]、[0,0] 的 $map 运行查询。查询执行得很好,但我有一个集合 "_id" : ObjectId("56dffa8f010fc3915d4d8c82"), "latitudeDegrees" : 3, "longitudeDegrees" : 3
,它没有显示在结果中。结果为空
我执行的查询是db.getCollection('test').aggregate(["$project":"location":"$map":"input":["longitudeDegrees","latitudeDegrees"],"as":"el","in":"$cond":["$eq":["$$el","longitudeDegrees"],"$long","$lat"],"$match":"location":"$geoWithin":"$geometry":"type":"Polygon","coordinates":[[[0,0],[0,6],[6,6],[6,0],[0,0]]]])
以上是关于在没有 [long,lat] 数组的情况下运行 mongodb $geoWithin的主要内容,如果未能解决你的问题,请参考以下文章
为啥这个用于 lat/long 的 Lua Haversine 代码不起作用?