如何替换 mongodb 中的特定字段,但 return 中的其余部分
How to replace specific fields in mongodb but return the rest
我不确定设计这个的最佳方法。我有一个集合,我想将其重新插入到一个新集合中,但我想保持结构完全相同减去几个字段。
{
_id: "123",
date: "1900-01-01T11:00:00.0000000",
name: "joe",
birthday: "1999-01-01"
}
这可能包含我不知道的其他字段 - 但我知道我想将所有日期转换为 ISO 日期类型 - 所以我只想特定于 4-5 个字段可能的 20 多个字段。
最终示例:
{
_id: "123",
date: ISO_Date("1900-01-01T11:00:00.0000000"),
name: "joe",
birthday: ISO_Date("1999-01-01T11:00:00.0000000")
}
我想我可以像这样使用添加选项创建一些新字段:
db.collection.aggregate([
//add
{
$addFields: {
convertDate: ISO_date(date) ,
convertBirthdate: ISO_date(date)
}
},
//
{
//stuck here
//select all
//replace the date strings with the new
$project :
{
//select * but avoid writing each field out as I could miss a few
*,
//replace date with addfields
date : $convertDate,
//replace birthday
birthday: $convertBirthdate
}
}
])
请告诉我这是否可行或其他更有效的方法。
你的方向是正确的。您可以简单地在 $addFields
中重复使用相同的字段名称。要插入到另一个新集合中,您可以尝试使用 $merge
或简单地使用 $out
.
db.collection.aggregate([
{
"$addFields": {
"date": {
"$toDate": "$date"
},
"birthday": {
"$toDate": "$birthday"
}
}
},
{
"$merge": {
"into": "collection2",
"on": "_id",
"whenMatched": "merge",
"whenNotMatched": "insert"
}
}
])
这里是Mongo playground供您参考。
这是一个单阶段变体,它遍历文档,尝试将它找到的任何字符串转换为日期,如果不能,则将其设置回原始值和类型。 $merge
或 $out
与此处的其他答案相同。这不是一个微妙的解决方案,但如果您知道您的日期字符串很好并且没有其他东西闻起来像日期字符串,那么它可能会有用。但是,值得注意的是,如果您打算一次性进行大量转换(或者至少很少,并且您有数百万个文档),将 material 转储到磁盘可能效率更高,在外部处理它,然后使用 mongoimport
以多线程(-j
选项)批量重新加载 material。
db.foo.aggregate([
{$replaceRoot: {newRoot: {$arrayToObject: {$map: {
input: {$objectToArray: "$$CURRENT"},
as: "z",
in: {k:"$$z.k",
v:{$cond:[{$ne:["string",{$type:"$$z.v"}]},"$$z.v",
{$convert: {input: "$$z.v", to: "date", onError: "$$z.v"}}]}
}
}} }
}}
]);
这里是用注释扩展的解决方案来解释发生了什么。
db.foo.aggregate([
// Turn the object into a k-v array named X:
{$project: {X: {$objectToArray: "$$CURRENT"}}}
// Use map to walk the X array. For each k-v encountered, if the
// type of v ($$z.v) is NOT a string, set v = $$z.v (itself), else
// use the $convert function to try to make it a date. If THAT fails,
// then convert will use the old $$z.v value.
,{$addFields: {Z: {$map: {
input: "$X",
as: "z",
in: {k:"$$z.k",
v:{$cond:{if: {$ne:["string",{$type:"$$z.v"}]},
then: "$$z.v",
else: {$convert: {input: "$$z.v", to: "date", onError: "$$z.v"}} }}
}
}}
}}
// Turn Z from a k-v array back into an object and "lift" it into
// the root document.
,{$replaceRoot: {newRoot: {$arrayToObject: "$Z"}}}
]);
这里是使用4.4版本的$function
运算符的大哥。它将递归地遍历文档并嗅探出类似日期的字符串并进行转换。强烈建议在管道末尾添加一个 $out
阶段,然后进行一些测试,因为这可能会改变很多事情或错过某些不太流行的日期字符串格式。
{$replaceRoot: {newRoot: {$function: {
body: function(obj) {
var process = function(holder, spot, value) {
if(Array.isArray(value)) { // test FIRST since [] instanceof Object is true!
for(var jj = 0; jj < value.length; jj++) {
process(value, jj, value[jj]);
}
} else if(value instanceof Object) {
walkObj(value);
} else {
if('string' === typeof value) {
q = Date.parse(value);
if(!isNaN(q)) {
holder[spot] = new Date(q);
}
}
}
};
var walkObj = function(obj) {
Object.keys(obj).forEach(function(k) {
process(obj, k, obj[k]);
});
}
walkObj(obj);
return obj;
},
args: [ "$$CURRENT" ],
lang: "js"
}}
}}
我不确定设计这个的最佳方法。我有一个集合,我想将其重新插入到一个新集合中,但我想保持结构完全相同减去几个字段。
{
_id: "123",
date: "1900-01-01T11:00:00.0000000",
name: "joe",
birthday: "1999-01-01"
}
这可能包含我不知道的其他字段 - 但我知道我想将所有日期转换为 ISO 日期类型 - 所以我只想特定于 4-5 个字段可能的 20 多个字段。
最终示例:
{
_id: "123",
date: ISO_Date("1900-01-01T11:00:00.0000000"),
name: "joe",
birthday: ISO_Date("1999-01-01T11:00:00.0000000")
}
我想我可以像这样使用添加选项创建一些新字段:
db.collection.aggregate([
//add
{
$addFields: {
convertDate: ISO_date(date) ,
convertBirthdate: ISO_date(date)
}
},
//
{
//stuck here
//select all
//replace the date strings with the new
$project :
{
//select * but avoid writing each field out as I could miss a few
*,
//replace date with addfields
date : $convertDate,
//replace birthday
birthday: $convertBirthdate
}
}
])
请告诉我这是否可行或其他更有效的方法。
你的方向是正确的。您可以简单地在 $addFields
中重复使用相同的字段名称。要插入到另一个新集合中,您可以尝试使用 $merge
或简单地使用 $out
.
db.collection.aggregate([
{
"$addFields": {
"date": {
"$toDate": "$date"
},
"birthday": {
"$toDate": "$birthday"
}
}
},
{
"$merge": {
"into": "collection2",
"on": "_id",
"whenMatched": "merge",
"whenNotMatched": "insert"
}
}
])
这里是Mongo playground供您参考。
这是一个单阶段变体,它遍历文档,尝试将它找到的任何字符串转换为日期,如果不能,则将其设置回原始值和类型。 $merge
或 $out
与此处的其他答案相同。这不是一个微妙的解决方案,但如果您知道您的日期字符串很好并且没有其他东西闻起来像日期字符串,那么它可能会有用。但是,值得注意的是,如果您打算一次性进行大量转换(或者至少很少,并且您有数百万个文档),将 material 转储到磁盘可能效率更高,在外部处理它,然后使用 mongoimport
以多线程(-j
选项)批量重新加载 material。
db.foo.aggregate([
{$replaceRoot: {newRoot: {$arrayToObject: {$map: {
input: {$objectToArray: "$$CURRENT"},
as: "z",
in: {k:"$$z.k",
v:{$cond:[{$ne:["string",{$type:"$$z.v"}]},"$$z.v",
{$convert: {input: "$$z.v", to: "date", onError: "$$z.v"}}]}
}
}} }
}}
]);
这里是用注释扩展的解决方案来解释发生了什么。
db.foo.aggregate([
// Turn the object into a k-v array named X:
{$project: {X: {$objectToArray: "$$CURRENT"}}}
// Use map to walk the X array. For each k-v encountered, if the
// type of v ($$z.v) is NOT a string, set v = $$z.v (itself), else
// use the $convert function to try to make it a date. If THAT fails,
// then convert will use the old $$z.v value.
,{$addFields: {Z: {$map: {
input: "$X",
as: "z",
in: {k:"$$z.k",
v:{$cond:{if: {$ne:["string",{$type:"$$z.v"}]},
then: "$$z.v",
else: {$convert: {input: "$$z.v", to: "date", onError: "$$z.v"}} }}
}
}}
}}
// Turn Z from a k-v array back into an object and "lift" it into
// the root document.
,{$replaceRoot: {newRoot: {$arrayToObject: "$Z"}}}
]);
这里是使用4.4版本的$function
运算符的大哥。它将递归地遍历文档并嗅探出类似日期的字符串并进行转换。强烈建议在管道末尾添加一个 $out
阶段,然后进行一些测试,因为这可能会改变很多事情或错过某些不太流行的日期字符串格式。
{$replaceRoot: {newRoot: {$function: {
body: function(obj) {
var process = function(holder, spot, value) {
if(Array.isArray(value)) { // test FIRST since [] instanceof Object is true!
for(var jj = 0; jj < value.length; jj++) {
process(value, jj, value[jj]);
}
} else if(value instanceof Object) {
walkObj(value);
} else {
if('string' === typeof value) {
q = Date.parse(value);
if(!isNaN(q)) {
holder[spot] = new Date(q);
}
}
}
};
var walkObj = function(obj) {
Object.keys(obj).forEach(function(k) {
process(obj, k, obj[k]);
});
}
walkObj(obj);
return obj;
},
args: [ "$$CURRENT" ],
lang: "js"
}}
}}