このような操作には 2 つのタスクが含まれます。1 つは日付型のフィールドのリストを取得するためのものでMapReduce
、次は集計またはBulk
書き込み操作を介してコレクションを更新するためのものです。
注意: 次の方法論では、すべての日付フィールドがドキュメントのルート レベルにあり、埋め込まれていない、またはサブドキュメントではないことを前提としています。
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: 集計フレームワークを使用してコレクションを更新する
集約フレームワークを使用して、コレクション、特に$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: 一括でコレクションを更新する
このオプションは$addFields
上記の演算子がサポートされていない場合の回避策であるため、$project
パイプラインを使用して同じ実装で新しいタイムスタンプ フィールドを作成できます$subtract
が、結果を同じコレクションに書き込む代わりに、次を使用して集計結果からカーソルを反復処理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);