应使用linestring而非point存物流轨迹,因其有序、支持长度计算和地理空间查询;需建2dsphere索引,按时间分片或压缩冗余点以应对大数据量,并清洗gps漂移等脏数据。

GeoJSON在MongoDB里存轨迹,为什么不能直接用Point?
因为物流轨迹是“一连串移动点”,不是单个位置。用Point只能存当前定位,历史路径就丢了。必须用LineString(线)或MultiPoint(点集)——但LineString更合适:它天然有序、支持长度计算、能被$geoWithin和$geoIntersects查询。
常见错误是把一堆Point塞进数组当“伪轨迹”,结果没法用地理空间索引加速查询,聚合时还得手动排序拼接,性能差还易出错。
-
LineString坐标数组必须是[[lng, lat], [lng, lat], ...]格式,注意是“经度在前”,和常见API(如高德、百度)返回顺序一致,但和Leaflet默认相反 - MongoDB要求
LineString至少含2个点,空轨迹或单点要单独字段标识(比如加status: "pending") - 别把时间戳混在坐标里——GeoJSON规范不支持带时间的坐标,应拆到独立字段,如
timestamps: [ISODate("..."), ISODate("...")]
建什么索引才能让“查某辆车昨天跑过哪条高速”变快?
只建2dsphere索引不够。轨迹是线,但业务查询常是“是否经过某区域”(面)或“是否靠近某点”(点),所以索引必须覆盖整个LineString的几何信息。
正确做法:对geometry字段建2dsphere索引,并确保写入时type严格为"LineString",坐标数组合法。否则索引会静默失效。
基于三引擎设计,从微信文章、新闻和博客网页提取干净内容,支持标题作者日期元数据,多格式和批量处理。
- 建索引命令:
db.shipments.createIndex({ "geometry": "2dsphere" }) - 验证索引是否生效:用
explain()看查询计划里有没有IXSCAN和geoNear阶段 - 避免踩坑:如果轨迹点过多(比如每5秒报一次,一天超1.7万点),
LineString可能超16MB文档限制——这时得按时间段分片,比如每小时存一条LineString,再用shipmentId + hour复合主键
怎么查“所有经过深圳湾大桥的运单”?
核心是用$geoIntersects配合多边形(Polygon)描述桥的地理范围。不能用$near,那是找“最近的点”,而桥是一段线性区域。
示例中,先用地图工具画出深圳湾大桥缓冲区(比如500米宽的面),导出为GeoJSON Polygon,然后查询:
db.shipments.find({
geometry: {
$geoIntersects: {
$geometry: {
type: "Polygon",
coordinates: [[[113.9, 22.5], [114.0, 22.5], [114.0, 22.4], [113.9, 22.4], [113.9, 22.5]]]
}
}
}
})
- 注意
$geoIntersects只对2dsphere索引有效,且要求查询的$geometry必须闭合(首尾坐标相同) - 真实场景中,桥的坐标得用高精度WGS84,别用地图截图粗略描点,否则漏判
- 如果要查“经过且方向为东→西”,就得额外存每段线段的方向角(
azimuth),靠$geoWithin结合$expr做二次过滤
轨迹太长导致写入失败或查询慢,怎么办?
MongoDB单文档16MB硬限制,但更实际的瓶颈是:客户端序列化/反序列化长数组卡顿、网络传输耗时、内存占用飙升。不是数据量大才出问题,而是结构不合理。
- 按时间切分:例如每10分钟生成一条
LineString,存到shipment_id + timestamp_hour + seq为_id的集合里,用$facet聚合还原全路径 - 按关键点压缩:原始轨迹每5秒一个点,但物流只关心进出园区、收费站、异常停留——用Douglas-Peucker算法预处理,保留拐点,减少90%+坐标数
- 冷热分离:最近24小时轨迹放主集合(带
2dsphere索引),历史轨迹归档到另一集合,只保留start/end点和总里程,查详情再按需拉取原始分片
真正麻烦的从来不是“怎么存”,而是“哪些点值得存”——GPS漂移、重复上报、设备离线补传,这些脏数据不清洗,再好的GeoJSON设计也扛不住。










