Запрос MongoDB и агрегация для типа даты BSON - PullRequest
0 голосов
/ 20 января 2020

Я работаю над проектом, который должен истечь непринятые заказы через определенное количество времени. Вот примерная структура заказа:

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

Если поле accepted_at все еще не заполнено через несколько часов после created_at, заказ будет считаться просроченным.

Если заказ принято, и completed_at по-прежнему равно нулю после time часов с created_at, заказ не будет выполнен.

Я хочу получить неудачные заказы из документа. Как то так:

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

Ответы [ 2 ]

1 голос
/ 23 января 2020

Это правильный ответ для типа даты BSON.

Пример данных

[
  {
    "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")
    }
  }
]

Запрос

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")
        ]
      }
    }
  }
])

Результат

[
  {
    "_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
  }
]

Рабочий код можно увидеть в mongoplayground .

NOTE

  • Разница между типом даты BSON и временными метками BSON

    Тип временной метки BSON предназначен для внутреннего использования MongoDB. В большинстве случаев при разработке приложений вам нужно использовать тип даты BSON.

  • Тип отметки времени BSON и тип даты BSON являются 64-разрядными целыми числами.

BSON Тип даты:

Внутренне объекты Date хранятся как 64-разрядное целое число со знаком, представляющее количество миллисекунд с момента Unix эпоха (1 января 1970 г.).

Отметки времени BSON:

BSON имеет специальный тип отметки времени для внутреннего использования MongoDB и не связан с обычным типом даты. Этот тип внутренней метки времени представляет собой 64-битное значение, где:

  • старшие 32 бита представляют собой значение time_t (секунды с начала Unix)
  • младшие 32 бита инкрементный порядковый номер для операций в течение данной секунды.

Официальная документация MongoDB

  • Вы не можете использовать функцию Date () непосредственно в запросе ,

    Date () возвращает текущую дату в виде строки в оболочке mon go.

Работает только в mon go shell. Но в Node.js все нормально, потому что драйвер mon go преобразует его.

  • ISODate - это просто вспомогательная функция.

    ISODate () - это вспомогательная функция, встроенная в MongoDB и заключающая в себе собственный объект JavaScript Date. При использовании конструктора ISODate () из оболочки Mon go он фактически возвращает объект JavaScript Date.

Советы по составлению: даты и даты в MongoDB

1 голос
/ 20 января 2020

Я бы посоветовал вам хранить значения временных меток для всех, это наиболее универсальное решение, а также простое управление для запросов такого типа

Так что ваша схема будет выглядеть как

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

А затем напишите ваш запрос следующим образом:

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"
        ]
      }
    }
  }
])

Пример данных

[
  {
    "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
    }
  }
]

Пример ответа

[
  {
    "_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
  }
]
...