операция, как это будет включать в себя две задачи; один для получения списка полей с типом даты через MapReduce
и следующего для обновления коллекции посредством агрегации или Bulk
операции записи.
NB: Следующая методология предполагает, что все поля даты находятся на корневом уровне документа, а не вложенные или вложенные документы.
MapReduce
Первое, что вам нужно выполнить следующую mapReduce
операцию. Это поможет вам определить, если каждое свойство с каждым документом в коллекции типа даты и возвращает особый список полей даты:
// define helper function to determine if a key is of Date type
isDate = function(dt) {
return dt && dt instanceof Date && !isNaN(dt.valueOf());
}
// map function
map = function() {
for (var key in this) {
if (isDate(value[key])
emit(key, null);
}
}
// variable with collection name
collectionName = "yourCollectionName";
mr = db.runCommand({
"mapreduce": collectionName,
"map": map,
"reduce": function() {}
})
dateFields = db[mr.result].distinct("_id")
printjson(dateFields)
//output: [ "validFrom", "validTo", "registerDate"" ]
Вариант 1: Обновление коллекции с помощью структуры агрегации
You могут использовать структуру агрегации для обновления вашей коллекции, в частности оператора $addFields
, доступного в MongoDB версии 3.4 и новее. Если ваша версия сервера MongoDB не поддерживает это, вы можете обновить свою коллекцию с помощью другого обходного пути (как описано в следующем варианте).
Метки времени вычисляются с использованием арифметического оператора агрегации $subtract
с датой поля в качестве уменьшаемого и дат начала эпохи new Date("1970-01-01")
как вычитаемые.
Полученные документы конвейера агрегации затем записываются в тот же сборник через оператор $out
, таким образом обновляя коллекцию с новыми полями.
В сущности, вы хотите, чтобы в конечном итоге, выполнив следующую агрегирования трубопровода, который преобразует поля даты для меток времени с использованием вышеуказанного алгоритма:
pipeline = [
{
"$addFields": {
"validFrom": { "$subtract": [ "$validFrom", new Date("1970-01-01") ] },
"validTo": { "$subtract": [ "$validTo", new Date("1970-01-01") ] },
"registerDate": { "$subtract": [ "$registerDate", new Date("1970-01-01") ] }
}
},
{ "$out": collectionName }
]
db[collectionName].aggregate(pipeline)
Вы можете динамически создать массив выше трубопровода, учитывая список финиковые поля следующим образом:
var addFields = { "$addFields": { } },
output = { "$out": collectionName };
dateFields.forEach(function(key){
var subtr = ["$"+key, new Date("1970-01-01")];
addFields["$addFields"][key] = { "$subtract": subtr };
});
db[collectionName].aggregate([addFields, output])
вариант 2: Обновление коллекции с помощью Bulk
Так как эта опция является обходным путем, когда $addFields
оператора из выше не поддерживаются, вы можете использовать $project
трубопровода для создания новых полей временных меток с теми же реализациями $subtract
, но вместо того, чтобы писать результаты в ту же коллекцию, вы можете получить iterate the cursor из результатов совокупности с использованием метода forEach()
и с каждым документом обновить коллекцию, используя метод bulkWrite()
.
Следующий пример показывает этот подход:
ops = []
pipeline = [
{
"$project": {
"validFrom": { "$subtract": [ "$validFrom", new Date("1970-01-01") ] },
"validTo": { "$subtract": [ "$validTo", new Date("1970-01-01") ] },
"registerDate": { "$subtract": [ "$registerDate", new Date("1970-01-01") ] }
}
}
]
db[collectionName].aggregate(pipeline).forEach(function(doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": {
"validFrom": doc.validFrom,
"validTo": doc.validTo,
"registerDate": doc.registerDate
}
}
}
});
if (ops.length === 500) {
db[collectionName].bulkWrite(ops);
ops = [];
}
})
if (ops.length > 0)
db[collectionName].bulkWrite(ops);
Используя тот же метод, как Вариант 1 выше, для создания трубопровода и метода объемной объекты динамически:
var ops = [],
project = { "$project": { } },
dateFields.forEach(function(key){
var subtr = ["$"+key, new Date("1970-01-01")];
project["$project"][key] = { "$subtract": subtr };
});
setDocFields = function(doc, keysList) {
setObj = { "$set": { } };
return keysList.reduce(function(obj, key) {
obj["$set"][key] = doc[key];
return obj;
}, setObj)
}
db[collectionName].aggregate([project]).forEach(function(doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": setDocFields(doc, dateFields)
}
});
if (ops.length === 500) {
db[collectionName].bulkWrite(ops);
ops = [];
}
})
if (ops.length > 0)
db[collectionName].bulkWrite(ops);
Можете ли вы привести пример ? – chridam
@chridam Да, конечно. Представьте себе коллекцию с такими полями: loginName (string), password (string), validFrom (date), validTo (date), registerDate (date). Я бы хотел написать сценарий, который найдет все поля даты (поэтому в этом случае validFrom, validTo, registerDate) и изменяет их на timestamps/longs. Если это возможно. – S0m30n3