• 今天来谈谈用MongoDB来支撑物流业务的情形
  • 青春赌徒 发表于 2017/2/16 14:25:00 | 分类标签: MongoDB教程 GIS功能
  • 快递物流系统里最常见的一种业务类型就是订单的查询和记录。订单的特点是随着递送过程,订单数据需要随时更新路径。数据结构上需要可以灵活应对,这点非常符合Document模型,并且MongoDB支持GIS功能,非常适用于MongoDB来支撑物流业务。并且MongoDB具备Sharding能力,而物流行业里订单比较独立,夸订单的操作很少,而对单订单来说,更新追加的操作会较多,比如再物流中转处理上。所以物流业务模型上与MongoDB非常的匹配。以下讲解一个虚拟的DEMO,可供参考,用到的特性:

    1.MongoDB Document的数组结构
    2.TTL索引,自动过期历史数据
    3.复合索引,多条件查询索引
    4.Partial Indexes,条件索引,只索引有效数据,降低索引的占用空间
    5.MongoDB GIS功能,支持GeoJSON标准,可以接入第三方系统处理GEO信息。

    数据结构定义

    {
      "_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,三者的坐标转换可以参考附录相关的链接。

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:青春赌徒
  • 加入时间:2013/6/11 0:00:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮