Как правильно увеличить много дат в 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
).