快递物流系统里最常见的一种业务类型便是订单的查询和记录。订单的特点是随着递送过程,订单数据须要随时更新路径。数据构造上须要可以灵巧应对,这点非常符合Document模型,并且MongoDB支持GIS功能,非常适用于MongoDB来支撑物流业务。并且MongoDB具备Sharding能力,而物盛行业里订单比较独立,夸订单的操作很少,而对单订单来说,更新追加的操作会较多,比如再物流中转处理上。以是物流业务模型上与MongoDB非常的匹配。以下讲解一个虚拟的DEMO,可供参考,用到的特性:
MongoDB Document的数组构造
TTL索引,自动过期历史数据

复合索引,多条件查询索引
Partial Indexes,条件索引,只索引有效数据,降落索引的占用空间
MongoDB GIS功能,支持GeoJSON标准,可以接入第三方系统处理GEO信息。
理解阿里云MongoDB详细配置及价格
数据构造定义
{ \"大众_id\公众: <String>, // 订单ID
\"大众status\"大众: <String>, // 订单状态,shipping,deliveried等
\公众order_image_url\"大众: <String>, // 订单图片信息
\"大众create_date\"大众: <ISODate>, // 订单创建日期
\公众from\公众: { // 发货信息
\"大众city\公众: <String>, \"大众address\"大众: <String>,
\"大众name\"大众: <String>, \"大众phone\"大众: <String>,
\"大众location\"大众: <GeoJSON>
}, \公众delivery\公众: { // 收货信息
\公众city\"大众: <String>, \公众address\公众: <String>,
\"大众name\公众: <String>, \"大众phone\公众: <String>,
\公众location\"大众: <GeoJSON>
}, \"大众details\"大众: [ // 物流详情,数组构造
{ \"大众action\"大众: \"大众reviced\"大众, // 物流动作
\"大众operator\"大众: \"大众快递小哥1号\公众, // 操作员
\公众date\"大众: <ISODate>
}...
]}
例如:
{
\"大众_id\公众: \公众E123456789\"大众,
\"大众status\公众: \"大众delivering\"大众,
\"大众create_date\"大众: ISODate(\公众2016-06-21T09:00:00+08:00\"大众),
\"大众order_image_url\公众: \"大众http://oss/xxxxx.jpg\"大众,
\"大众from\"大众: {
\"大众city\公众: \"大众Hangzhou\"大众,
\"大众address\"大众: \公众文一西路969号\"大众,
\"大众name\"大众: \公众小王\公众,
\"大众phone\公众: \"大众18657112345\"大众,
\"大众location\公众: {
\"大众type\"大众: \公众Point\公众,
\公众coordinates\公众: [ 120, 30
] } },
\公众delivery\"大众: {
\公众city\"大众: \公众北京\"大众,
\"大众address\公众: \"大众朝阳区\"大众,
\"大众name\"大众: \公众朝阳群众\公众,
\"大众phone\公众: \公众18601011011\"大众,
\"大众location\公众: {
\"大众type\"大众: \"大众Point\公众,
\"大众coordinates\"大众: [ 116, 39
] } },
\"大众details\"大众: [
{
\"大众action\公众: \公众reviced\"大众,
\"大众operator\公众: \"大众快递小哥1号\"大众,
\"大众date\公众: ISODate(\公众2016-06-21T09:00:00+08:00\"大众) },
{
\公众action\"大众: \公众shipping\公众,
\"大众station\"大众: \"大众hangzhou-airport\公众,
\"大众date\"大众: ISODate(\"大众2016-06-22T01:00:00+08:00\"大众) },
{
\公众action\"大众: \公众shipping\公众,
\公众station\"大众: \公众beijing-airport\"大众,
\"大众date\公众: ISODate(\"大众2016-06-22T07:00:00+08:00\"大众) },
{
\公众action\"大众: \"大众shipping\"大众,
\"大众station\"大众: \"大众chaoyang-station\公众,
\"大众date\"大众: ISODate(\"大众2016-06-22T15:00:00+08:00\"大众) },
{
\"大众action\"大众: \公众delivering\公众,
\"大众operator\"大众: \公众快递小哥2号\"大众,
\"大众date\"大众: ISODate(\"大众2016-06-23T10:00:00+08:00\"大众) }
]}
几个把稳点:
利用了GEO特性,利用GeoJSON标准,但是坐标系上须要把稳坐标标准问题;
韶光处理上,物盛行业会涉及到国际化,以是严格按照ISO的标准,定义韶光,CN+80:00
;
订单详情里很好的利用了Document的数组特性;
快递订单是唯一的,可以作为MongoDB的主键;
insert到collection中后的文档:
> db.order.find().pretty()
{ \"大众_id\公众 : \"大众E123456789\"大众, \"大众status\"大众 : \"大众delivering\"大众, \"大众create_date\公众 : ISODate(\公众2016-06-21T01:00:00Z\"大众), \"大众order_image_url\公众 : \"大众http://oss/xxxxx.jpg\公众, \"大众from\"大众 : { \"大众city\"大众 : \公众Hangzhou\"大众, \公众address\"大众 : \公众文一西路969号\公众, \公众name\公众 : \"大众小王\"大众, \"大众phone\"大众 : \"大众18657112345\公众, \公众location\公众 : { \"大众type\公众 : \"大众Point\"大众, \"大众coordinates\"大众 : [ 120, 30
]
}
}, \"大众delivery\"大众 : { \公众city\"大众 : \"大众北京\"大众, \公众address\"大众 : \"大众朝阳区\"大众, \公众name\"大众 : \公众朝阳群众\"大众, \"大众phone\"大众 : \公众18601011011\公众, \"大众location\公众 : { \公众type\公众 : \"大众Point\公众, \公众coordinates\"大众 : [ 116, 39
]
}
}, \公众details\"大众 : [
{ \"大众action\公众 : \公众reviced\"大众, \公众operator\公众 : \"大众快递小哥1号\"大众, \"大众date\"大众 : ISODate(\"大众2016-06-21T01:00:00Z\"大众)
},
{ \"大众action\"大众 : \"大众shipping\公众, \"大众station\"大众 : \公众hangzhou-airport\公众, \"大众date\"大众 : ISODate(\公众2016-06-21T17:00:00Z\"大众)
},
{ \"大众action\"大众 : \公众shipping\"大众, \"大众station\"大众 : \"大众beijing-airport\"大众, \"大众date\"大众 : ISODate(\"大众2016-06-21T23:00:00Z\"大众)
},
{ \"大众action\"大众 : \公众shipping\公众, \公众station\"大众 : \"大众chaoyang-station\公众, \公众date\公众 : ISODate(\公众2016-06-22T07:00:00Z\公众)
},
{ \"大众action\"大众 : \"大众delivering\"大众, \"大众operator\"大众 : \公众快递小哥2号\"大众, \公众date\"大众 : ISODate(\公众2016-06-23T02:00:00Z\"大众)
}
]
}
数据操作
物流快递的订单修正紧张是查询和信息追加两种,紧张先容这两种:
订单信息查询,最常见的操作,用户的订单查询:
db.order.find({_id:\公众E123456789\"大众});
有时须要做信息统计,按照状态来查询:
db.order.find({\"大众status\"大众:\"大众delivering\"大众, \公众delivery.city\公众:\公众北京\"大众, \"大众delivery.address\"大众:\公众朝阳区\"大众});
物流状态更新时,须要更新相应的订单,MongoDB上直接$push
过去即可:
db.order.update( { _id:\"大众E123456789\"大众},
{$push: {details:
{\"大众action\"大众:\"大众delivering\"大众, \"大众operator\公众 : \公众快递小哥3号\"大众, \"大众date\"大众 : ISODate(\"大众2016-06-23T13:00:00+8:00\"大众)}
}})
索引创建
_id
索引,默认存在,不须要再创建;当数据量较大时,可以利用sharding构造,shardkey的选择上可以利用Hash(_id)
。
TTL索引,字段create_date
,180天后自动清理数据:
db.order.createIndex({\"大众create_date\"大众:1}, {\"大众expireAfterSeconds\公众:15552000})
位置和状态索引,为了能快速处理“某地未处理订单”查询,这是一个多条件的查询,所以是一个复合索引,status
字段放在前面,由于多数的查询都会依赖状态字段
db.order.createIndex({\"大众status\公众:1, \公众delivery.city\公众:1, \"大众delivery.address\"大众:1})
在这个Demo里,还有一种加快查询速率的方法便是,创建一个只包含指定状态的一个Partial Indexes索引。比如status
必须为delivering
才加入到索引中,有效掌握索引的大小,加快查询速率。
db.order.createIndex({\"大众delivery.city\"大众:1, \"大众delivery.address\"大众:1},{partialFilterExpression:{'status':{$eq:\公众delivering\公众}}})
MongoDB GIS
MongoDB遵照的事GeoJSON规范,工具的描述上通过一个type字段描述GeoJSON类型,coordinates字段描述空间信息。
{ type: \公众<GeoJSON type>\公众 , coordinates: <coordinates> }
coordinates是一个[longitude, latitude]
的数组类型。其余值得关注的是MongoDB GEO的利用的是WGS84标准。WGS84也是国际标准,中国利用的著名的火星坐标GCJ-02,还有一套百度坐标BD09,三者的坐标转换可以参考附录干系的链接。
附录
GeoJSON
https://docs.mongodb.com/manual/reference/geojson/
http://geojson.org/geojson-spec.html
https://docs.mongodb.com/manual/reference/glossary/#term-wgs84
MongoDB Geo Index
https://docs.mongodb.com/manual/core/2dsphere/
https://docs.mongodb.com/manual/reference/glossary/#term-legacy-coordinate-pairs
GeoHack
https://tools.wmflabs.org/geohack/geohack.php?pagename=Hangzhou¶ms=30\_15\_N\_120\_10\_E\_type:city(9018000)_region:CN-33_
https://tools.wmflabs.org/geohack/geohack.php?pagename=Beijing¶ms=39\_55\_N\_116\_23\_E\_type:city(21700000)_region:CN-11_
https://en.wikipedia.org/wiki/World\_Geodetic\_System
Geo Datum
https://github.com/wandergis/coordtransform
更多深度技能内容,请关注云栖社区微信"大众号:yunqiinsight。