快递物流系统里最常见的一种业务类型就是订单的查询和记录。订单的特点是随着递送过程,订单数据需要随时更新路径。数据结构上需要可以灵活应对,这点非常符合Document模型,并且MongoDB支持GIS功能,非常适用于MongoDB来支撑物流业务。并且MongoDB具备Sharding能力,而物流行业里订单比较独立,夸订单的操作很少,而对单订单来说,更新追加的操作会较多,比如再物流中转处理上。所以物流业务模型上与MongoDB非常的匹配。以下讲解一个虚拟的DEMO,可供参考,用到的特性:
了解阿里云MongoDB详细配置及价格
{
"_id": , // 订单ID
"status": , // 订单状态,shipping,deliveried等
"order_image_url": , // 订单图片信息
"create_date": , // 订单创建日期
"from": { // 发货信息
"city": , "address": ,
"name": , "phone": ,
"location":
},
"delivery": { // 收货信息
"city": , "address": ,
"name": , "phone": ,
"location":
},
"details": [ // 物流详情,数组结构
{
"action": "reviced", // 物流动作
"operator": "快递小哥1号", // 操作员
"date":
}...
]
}
例如:
{
"_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")
}
]
}
几个注意点:
+80:00
;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遵循的事GeoJSON规范,对象的描述上通过一个type字段描述GeoJSON类型,coordinates字段描述空间信息。
{ type: "
" , coordinates: }
coordinates是一个[longitude, latitude]
的数组类型。另外值得关注的是MongoDB GEO的使用的是WGS84标准。WGS84也是国际标准,中国使用的著名的火星坐标GCJ-02,还有一套百度坐标BD09,三者的坐标转换可以参考附录相关的链接。
GeoJSON
MongoDB Geo Index
GeoHack
Geo Datum
https://github.com/wandergis/coordtransform
叶翔,ApsaraDB For MongoDB 开发组