Как правильно увеличить много дат в mongoDB?

Не будучи особенно сильным парнем Javascript, у меня возникли некоторые проблемы, пытаясь обновить много Date объекты в монго.

Кажется, что $inc еще не был реализован для Date объекты. Итак, чтобы попытаться увеличить число дат по дням, я вызвал (что-то вроде) этот скрипт из bash через mongo myScript.js:

conn = new Mongo();
db   = conn.getDB('myDatabase');

var incrementDates = function() {
  db.blah.find(myQuery).forEach(function(doc) {

    db.blah.update(
       { _id     : doc._id
       , my_date : { $exists : true }
       }
     , { $set : { my_date : new Date(doc.my_date.getTime() + 86400000) }}
    );

  });
}

incrementDates();

Основная идея, кажется, работает достаточно хорошо в оболочке mongoDB:

> var doc = db.blah.findOne(myQuery)
> doc.my_date
ISODate("1962-11-02T23:00:00Z")
> new Date(doc.my_date.getTime() + 86400000);
ISODate("1962-11-03T23:00:00Z")

Но не так хорошо в сценарии:

TypeError: doc.my_date has no properties

Итак, я понимаю, что я пытаюсь позвонить getTime на null где-то, даже если запрос в моем обновлении должен возвращать только документы, где my_date существует.

Есть идеи о том, что здесь происходит? Что еще более важно: есть ли лучший способ сделать это?

3 ответа

Решение

Проблема в том, что мой $exists запрос (очевидно, на второй взгляд) не в том месте. Возвращались документы, которые, конечно же, не включали my_date,

Вот исправленная функция, которая работает как ожидалось.

var incrementDates = function() {
  db.blah.find({ ... , my_date : { $exists : true } ).forEach(function(doc) {
    db.blah.update(
       { _id     : doc._id }
     , { $set : { my_date : new Date(doc.my_date.getTime() + 86400000) }}
    );
  });
}

Начиная с Mongo 4.2, db.collection.update() может принимать конвейер агрегации, наконец, разрешая обновление поля на основе его собственного значения; таким образом избегая неэффективных шаблонов поиска / foreach.

Кроме того, вы смотрели на $inc для добавления дня, но теперь, когда мы можем использовать конвейер агрегации в качестве обновления, $add оператор может использоваться:

// { "date" : ISODate("2020-04-05T07:14:17.802Z"), "x" : "y" }
db.collection.updateMany(
  { date : { $exists : true } },
  [{ $set: { date: { $add: ["$date", 24*60*60000] } } }]
)
// { "date" : ISODate("2020-04-06T07:14:17.802Z"), "x" : "y" }
  • Первая часть { date : { $exists : true } } - это запрос на совпадение, отфильтровывающий, какие документы нужно обновить (в нашем случае все документы, имеющие date поле).

  • Вторая часть [{ $set: { date: { $add: ["$date", 24*60*60000] } } }] - конвейер агрегирования обновлений (обратите внимание на квадратные скобки, обозначающие использование конвейера агрегирования). $set новый оператор агрегирования и псевдоним $addFields. Тогда любой оператор агрегации можно использовать внутри$setэтап; в нашем случае простой $add Расстояние между существующей датой и представлением дня в миллисекундах.

Начиная с Mongo 5.0 release schedule, это будет хороший пример использования нового оператор агрегации:

      // { "date" : ISODate("2020-04-05T07:14:17.802Z"), "x" : "y" }
db.collection.updateMany(
  { date : { $exists : true } },
  [{ $set: { date: { $dateAdd: { startDate: "$date", unit: "day", amount: 1 } } } }]
)
// { "date" : ISODate("2020-04-06T07:14:17.802Z"), "x" : "y" }
  • Первая часть { date : { $exists : true } } - это запрос на совпадение, отфильтровывающий, какие документы нужно обновить (в нашем случае все документы, имеющие поле).

  • Вторая часть [{ $set: { date: { $dateAdd: { startDate: "$date", unit: "day", amount: 1 } } } }], обновляет значение date поле, добавив ( $dateAdd) 1 ( amount) day ( unit) к $date ( startDate).

Другие вопросы по тегам