说实话,刚入行那会儿,我对着MongoDB的文档看了三天三夜,脑子还是浆糊。特别是那个mongoose geo数据类型,听着高大上,用起来全是坑。今天不整那些虚的,直接掏心窝子聊聊,怎么让这玩意儿乖乖听话,帮你找到离用户最近的门店或者朋友。
记得去年给一个本地生活项目做重构,老板要求必须实现“附近的人”功能,还要精确到米。我当时心想,这还不简单?随手写了个查询,结果上线后,服务器直接崩了。为什么?因为数据量上去了,没有建对索引,每次查询都在全表扫描。那几天我头发掉了一把,真的。
咱们先说基础。mongoose geo数据类型,说白了就是让MongoDB知道你的数据是有地理位置的。你不能只存个经纬度字符串,得用GeoJSON格式。比如,你要存一个咖啡馆的位置,得这么写:
location: {
type: "Point",
coordinates: [116.404, 39.915]
}
注意啊,顺序是[经度, 纬度],千万别搞反了。我见过太多新人把经纬度写反,结果查出来的结果在地球另一头,尴尬不?有一次我帮朋友调试,他查出来的“附近”餐厅全在太平洋上,我差点笑出声,但也挺心疼他的时间。
接下来是重头戏,索引。建了mongoose geo数据类型,必须建2dsphere索引。这是为了支持球面几何查询,毕竟地球是圆的,不是平的。代码很简单:
db.collection.createIndex({ location: "2dsphere" })
这一步不做,后面的查询效率低得让你怀疑人生。我之前的那个项目,加了索引后,查询速度从几秒降到了毫秒级,用户体验瞬间提升。
然后就是查询了。用$near或者$geoWithin。$near是按距离排序,适合找最近的;$geoWithin是找在某个区域内的,比如找所有在北京五环内的店。这里有个小坑,$near返回的结果是有序的,而$geoWithin返回的是无序的。如果你要分页,$near可能会比较麻烦,因为每次查询的距离范围变了,结果集也会变,容易导致数据重复或遗漏。我当时就是踩了这个坑,分页查着查着,有的店查不到,有的店重复出现,用户投诉不断。
还有一个细节,距离单位。MongoDB默认返回的距离是弧度,你得自己换算成米或者公里。公式是:距离(米) = 弧度 * 地球半径(6378137)。我一般封装个工具函数,免得每次都要算。
最后,别忽视数据清洗。用户上传的位置可能有偏差,或者格式不对。我在项目里加了个中间件,在保存前校验一下GeoJSON格式,确保数据干净。这一步虽然麻烦,但能省去后期无数的调试时间。
总之,mongoose geo数据类型不是不能用,而是要用对。索引、格式、查询方式,每一步都不能马虎。如果你还在为附近的功能头疼,不妨试试这些经验。当然,如果你实在搞不定,或者项目时间紧,也可以找专业的人帮忙。毕竟,专业的事交给专业的人,省心省力。
本文关键词:mongoose geo数据类型