MongoDB query and aggregation for BSON date type

两盒软妹~` 提交于 2020-07-22 05:14:39

问题


I am working on a project which needs to expire unaccepted orders after a set amount of time. Here is the sample structure of an order:

{
  _id: ObjectId,
  time: int
  history: {
    created_at: Date,
    accepted_at: ['Null', 'Date'],
    completed_at: ['Null', 'Date']
  }
}

If accepted_at field is still null after some hours from created_at, the order will be considered as expired.

If order is accepted and completed_at is still null after time hours from created_at the order will be failed.

I want to get failed orders from document. Something like this:

db.orders.aggregate([
  {
    $match: {
      "history.accepted_at": { $exists: true },
      "history.created_at" : {
         $exists: true,
         $lte: new Date((new Date()).getTime() - $time *3600 * 1000)
      }
    }
  }
])

回答1:


I would suggest you to store values of timestamps for all, that is most universal solution & also easy to manipulate for queries of such kind

So your schema be like

{
    time:{
        type: Number,
        default: 0
    },
    history:{
        created_at:{
            type: Number,
            default: 0
        },
        accepted_at:{
            type: Number,
            default: 0
        },
        completed_at:{
            type: Number,
            default: 0
        }
    }
}

And then write your query like this

db.collection.aggregate([
  {
    $addFields: {
      "end_deadline": {
        $add: [
          "$history.created_at",
          {
            $multiply: [
              "$time",
              3600000
            ]
          }
        ]
      },

    }
  },
  {
    $match: {
      "history.accepted_at": {
        $exists: true
      },
      "history.completed_at": null,
      $expr: {
        $gte: [
          ISODate("2020-01-23T00:00:00.441Z"),
          "$end_deadline"
        ]
      }
    }
  }
])

Sample Data

[
  {
    "time": 12,
    "start_from": 1579910400441,
    "history": {
      "created_at": 1579737600441,
      "accepted_at": 1579741200441
    }
  },
  {
    "time": 24,
    "start_from": 1579932000441,
    "history": {
      "created_at": 1579737600441,
      "accepted_at": 1579739400441,
      "completed_at": 1579935600441
    }
  },
  {
    "time": 24,
    "start_from": 1578700800441,
    "history": {
      "created_at": 1578528000441,
      "accepted_at": 1578614400441
    }
  }
]

Sample response

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "end_deadline": 1.579780800441e+12,
    "history": {
      "accepted_at": 1.579741200441e+12,
      "created_at": 1.579737600441e+12
    },
    "start_from": 1.579910400441e+12,
    "time": 12
  },
  {
    "_id": ObjectId("5a934e000102030405000002"),
    "end_deadline": 1.578614400441e+12,
    "history": {
      "accepted_at": 1.578614400441e+12,
      "created_at": 1.578528000441e+12
    },
    "start_from": 1.578700800441e+12,
    "time": 24
  }
]



回答2:


This is the correct answer for BSON Date type.

Sample Data

[
  {
    "time": 12,
    "start_from": new Date("2020-01-25T00:00:00.441+00:00"),
    "history": {
      "created_at": new Date("2020-01-23T00:00:00.441+00:00"),
      "accepted_at": new Date("2020-01-23T01:00:00.441+00:00")
    }
  },
  {
    "time": 24,
    "start_from": new Date("2020-01-25T06:00:00.441+00:00"),
    "history": {
      "created_at": new Date("2020-01-23T00:00:00.441+00:00"),
      "accepted_at": new Date("2020-01-23T00:30:00.441+00:00"),
      "completed_at": new Date("2020-01-25T07:00:00.441+00:00")
    }
  },
  {
    "time": 24,
    "start_from": new Date("2020-01-11T00:00:00.441+00:00"),
    "history": {
      "created_at": new Date("2020-01-09T00:00:00.441+00:00"),
      "accepted_at": new Date("2020-01-10T00:00:00.441+00:00")
    }
  }
]

Query

db.collection.aggregate([
  {
    $addFields: {
      "deadline": {
        $add: [
          "$start_from",
          {
            $multiply: [
              "$time",
              3600000
            ]
          }
        ]
      },

    }
  },
  {
    $match: {
      "history.accepted_at": {
        $exists: true
      },
      "history.completed_at": null,
      $expr: {
        $gte: [
          "$deadline",
          ISODate("2020-01-23T00:00:00.441Z")
        ]
      }
    }
  }
])

Result

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "end_deadline": ISODate("2020-01-25T12:00:00.441Z"),
    "history": {
      "accepted_at": ISODate("2020-01-23T01:00:00.441Z"),
      "created_at": ISODate("2020-01-23T00:00:00.441Z")
    },
    "start_from": ISODate("2020-01-25T00:00:00.441Z"),
    "time": 12
  }
]

You can see the working code in mongoplayground.

NOTE

  • Difference between BSON date type and BSON timestamps

    The BSON timestamp type is for internal MongoDB use. For most cases, in application development, you will want to use the BSON date type.

  • BSON timestamp type and BSON Date type are both 64-bit integers.

BSON Date type:

Internally, Date objects are stored as a signed 64-bit integer representing the number of milliseconds since the Unix epoch (Jan 1, 1970).

BSON Timestamps:

BSON has a special timestamp type for internal MongoDB use and is not associated with the regular Date type. This internal timestamp type is a 64 bit value where:

  • the most significant 32 bits are a time_t value (seconds since the Unix epoch)
  • the least significant 32 bits are an incrementing ordinal for operations within a given second.

MongoDB official documentation

  • You cannot use Date() function directly in query.

    Date() returns the current date as a string in the mongo shell.

It only works in mongo shell. But in Node.js, it's OK because the mongo driver will convert it.

  • ISODate is just a helper function.

    ISODate() is a helper function that's built into to MongoDB and wraps the native JavaScript Date object. When you use the ISODate() constructor from the Mongo shell, it actually returns a JavaScript Date object.

Compose Tips: Dates and Dating in MongoDB



来源:https://stackoverflow.com/questions/59822405/mongodb-query-and-aggregation-for-bson-date-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!