Mongo updateMany 语句,包含要操作的对象内部数组
Mongo updateMany statement with an inner array of objects to manipulate
我正在努力编写一个 Mongo UpdateMany 语句来引用和更新数组中的对象。
这里我创建了3个文档。每个文档都有一个名为 innerArray
的数组,始终包含一个对象和一个日期字段。
use test;
db.innerArrayExample.insertOne({ _id: 1, "innerArray": [ { "originalDateTime" : ISODate("2022-01-01T01:01:01Z") } ]});
db.innerArrayExample.insertOne({ _id: 2, "innerArray": [ { "originalDateTime" : ISODate("2022-01-02T01:01:01Z") } ]});
db.innerArrayExample.insertOne({ _id: 3, "innerArray": [ { "originalDateTime" : ISODate("2022-01-03T01:01:01Z") } ]});
我想在原始日期字段的基础上添加一个新的日期字段,以这样结束:
{ _id: 1, "innerArray": [ { "originalDateTime" : ISODate("2022-01-01T01:01:01Z"), "copiedDateTime" : ISODate("2022-01-01T12:01:01Z") } ]}
{ _id: 2, "innerArray": [ { "originalDateTime" : ISODate("2022-01-02T01:01:01Z"), "copiedDateTime" : ISODate("2022-01-02T12:01:01Z") } ]}
{ _id: 3, "innerArray": [ { "originalDateTime" : ISODate("2022-01-03T01:01:01Z"), "copiedDateTime" : ISODate("2022-01-03T12:01:01Z") } ]}
在伪代码中,我说的是通过函数获取 originalDateTime
、运行 并添加相关的 copiedDateTime
值。
对于我的特定用例,我想要 运行 的函数从 originalDateTime
中删除时区,然后用新的时区覆盖它,相当于 Java ZonedDateTime
函数withZoneSameLocal
。 Aka 9pm UTC 变为 9pm Brussels(因此实际上是 7pm UTC)。技术理由和方法 .
我正在努力处理的查询部分是 updates/selects 来自数组元素的数据部分。在我的简单示例中,例如我设计了这个查询,但不幸的是它不起作用:
此函数将 copiedDateTime
放在正确的位置...但不评估操作日期的命令:
db.innerArrayExample.updateMany({ "innerArray.0.originalDateTime" : { $exists : true }}, { $set: { "innerArray.0.copiedDateTime" : { $dateFromString: { dateString: { $dateToString: { "date" : "$innerArray.0.originalDateTime", format: "%Y-%m-%dT%H:%M:%S.%L" }}, format: "%Y-%m-%dT%H:%M:%S.%L", timezone: "Europe/Paris" }}});
// output
{
_id: 1,
innerArray: [
{
originalDateTime: ISODate("2022-01-01T01:01:01.000Z"),
copiedDateTime: {
'$dateFromString': {
dateString: { '$dateToString': [Object] },
format: '%Y-%m-%dT%H:%M:%S.%L',
timezone: 'Europe/Paris'
}
}
}
]
}
这个简化的查询,也有同样的问题:
b.innerArrayExample.updateMany({ "innerArray.0.originalDateTime" : { $exists : true }}, { $set: { "innerArray.0.copiedDateTime" : "$innerArray.0.originalDateTime" }});
//output
{
_id: 1,
innerArray: [
{
originalDateTime: ISODate("2022-01-01T01:01:01.000Z"),
copiedDateTime: '$innerArray.0.originalDateTime'
}
]
}
如您所见,这个问题看起来与其他堆栈溢出问题不同。与其改变时区,不如让数组中的东西更新。
我计划采用此查询,使用不同的 location/timezone 组合创建它的 70,000 个变体,并针对具有数百万条记录的数据库 运行 它,所以我更喜欢使用 updateMany
而不是使用 Java 脚本遍历数据库中的每一行...除非那是唯一可行的解决方案。
我试过将 $set
放在方括号中。这改变了它解释一切的方式,评估右侧,但会导致其他问题:
test> db.innerArrayExample.updateMany({ "_id" : 1 }, [{ $set: { "innerArray.0.copiedDateTime" : "$innerArray.0.originalDateTime" }}]);
//output
{
_id: 1,
innerArray: [
{
'0': { copiedDateTime: [] },
originalDateTime: ISODate("2022-01-01T01:01:01.000Z")
}
]
}
上面好像是解释.0。作为文字而不是数组元素。 (根据我的需要,我知道数组始终只有 1 个项目)。我找不到满足我需要的示例。
我也尝试过 arrayFilters
,记录在我的 mongo updateMany 文档中,但我无法理解它如何与对象一起工作:
test> db.innerArrayExample.updateMany(
... { },
... { $set: { "innerArray.$[element].copiedDateTime" : "$innerArray.$[element].originalDateTime" } },
... { arrayFilters: [ { "originalDateTime": { $exists: true } } ] }
... );
MongoServerError: No array filter found for identifier 'element' in path 'innerArray.$[element].copiedDateTime'
test> db.innerArrayExample.updateMany(
... { },
... { $set: { "innerArray.$[0].copiedDateTime" : "$innerArray.$[element].originalDateTime" } },
... { arrayFilters: [ { "0.originalDateTime": { $exists: true } } ] }
... );
MongoServerError: Error parsing array filter :: caused by :: The top-level field name must be an alphanumeric string beginning with a lowercase letter, found '0'
如果有人能帮助我理解 Mongo 语法的微妙之处并帮助我回到正确的道路上,我将不胜感激。
您想使用 pipelined updates,您使用的语法存在的问题是它不允许使用聚合运算符和文档字段值。
这里有一个关于如何操作的简单示例:
db.collection.updateMany({},
[
{
"$set": {
"innerArray": {
$map: {
input: "$innerArray",
in: {
$mergeObjects: [
"$$this",
{
copiedDateTime: "$$this.originalDateTime"
}
]
}
}
}
}
}
])
我正在努力编写一个 Mongo UpdateMany 语句来引用和更新数组中的对象。
这里我创建了3个文档。每个文档都有一个名为 innerArray
的数组,始终包含一个对象和一个日期字段。
use test;
db.innerArrayExample.insertOne({ _id: 1, "innerArray": [ { "originalDateTime" : ISODate("2022-01-01T01:01:01Z") } ]});
db.innerArrayExample.insertOne({ _id: 2, "innerArray": [ { "originalDateTime" : ISODate("2022-01-02T01:01:01Z") } ]});
db.innerArrayExample.insertOne({ _id: 3, "innerArray": [ { "originalDateTime" : ISODate("2022-01-03T01:01:01Z") } ]});
我想在原始日期字段的基础上添加一个新的日期字段,以这样结束:
{ _id: 1, "innerArray": [ { "originalDateTime" : ISODate("2022-01-01T01:01:01Z"), "copiedDateTime" : ISODate("2022-01-01T12:01:01Z") } ]}
{ _id: 2, "innerArray": [ { "originalDateTime" : ISODate("2022-01-02T01:01:01Z"), "copiedDateTime" : ISODate("2022-01-02T12:01:01Z") } ]}
{ _id: 3, "innerArray": [ { "originalDateTime" : ISODate("2022-01-03T01:01:01Z"), "copiedDateTime" : ISODate("2022-01-03T12:01:01Z") } ]}
在伪代码中,我说的是通过函数获取 originalDateTime
、运行 并添加相关的 copiedDateTime
值。
对于我的特定用例,我想要 运行 的函数从 originalDateTime
中删除时区,然后用新的时区覆盖它,相当于 Java ZonedDateTime
函数withZoneSameLocal
。 Aka 9pm UTC 变为 9pm Brussels(因此实际上是 7pm UTC)。技术理由和方法
我正在努力处理的查询部分是 updates/selects 来自数组元素的数据部分。在我的简单示例中,例如我设计了这个查询,但不幸的是它不起作用:
此函数将 copiedDateTime
放在正确的位置...但不评估操作日期的命令:
db.innerArrayExample.updateMany({ "innerArray.0.originalDateTime" : { $exists : true }}, { $set: { "innerArray.0.copiedDateTime" : { $dateFromString: { dateString: { $dateToString: { "date" : "$innerArray.0.originalDateTime", format: "%Y-%m-%dT%H:%M:%S.%L" }}, format: "%Y-%m-%dT%H:%M:%S.%L", timezone: "Europe/Paris" }}});
// output
{
_id: 1,
innerArray: [
{
originalDateTime: ISODate("2022-01-01T01:01:01.000Z"),
copiedDateTime: {
'$dateFromString': {
dateString: { '$dateToString': [Object] },
format: '%Y-%m-%dT%H:%M:%S.%L',
timezone: 'Europe/Paris'
}
}
}
]
}
这个简化的查询,也有同样的问题:
b.innerArrayExample.updateMany({ "innerArray.0.originalDateTime" : { $exists : true }}, { $set: { "innerArray.0.copiedDateTime" : "$innerArray.0.originalDateTime" }});
//output
{
_id: 1,
innerArray: [
{
originalDateTime: ISODate("2022-01-01T01:01:01.000Z"),
copiedDateTime: '$innerArray.0.originalDateTime'
}
]
}
如您所见,这个问题看起来与其他堆栈溢出问题不同。与其改变时区,不如让数组中的东西更新。
我计划采用此查询,使用不同的 location/timezone 组合创建它的 70,000 个变体,并针对具有数百万条记录的数据库 运行 它,所以我更喜欢使用 updateMany
而不是使用 Java 脚本遍历数据库中的每一行...除非那是唯一可行的解决方案。
我试过将 $set
放在方括号中。这改变了它解释一切的方式,评估右侧,但会导致其他问题:
test> db.innerArrayExample.updateMany({ "_id" : 1 }, [{ $set: { "innerArray.0.copiedDateTime" : "$innerArray.0.originalDateTime" }}]);
//output
{
_id: 1,
innerArray: [
{
'0': { copiedDateTime: [] },
originalDateTime: ISODate("2022-01-01T01:01:01.000Z")
}
]
}
上面好像是解释.0。作为文字而不是数组元素。 (根据我的需要,我知道数组始终只有 1 个项目)。我找不到满足我需要的示例。
我也尝试过 arrayFilters
,记录在我的 mongo updateMany 文档中,但我无法理解它如何与对象一起工作:
test> db.innerArrayExample.updateMany(
... { },
... { $set: { "innerArray.$[element].copiedDateTime" : "$innerArray.$[element].originalDateTime" } },
... { arrayFilters: [ { "originalDateTime": { $exists: true } } ] }
... );
MongoServerError: No array filter found for identifier 'element' in path 'innerArray.$[element].copiedDateTime'
test> db.innerArrayExample.updateMany(
... { },
... { $set: { "innerArray.$[0].copiedDateTime" : "$innerArray.$[element].originalDateTime" } },
... { arrayFilters: [ { "0.originalDateTime": { $exists: true } } ] }
... );
MongoServerError: Error parsing array filter :: caused by :: The top-level field name must be an alphanumeric string beginning with a lowercase letter, found '0'
如果有人能帮助我理解 Mongo 语法的微妙之处并帮助我回到正确的道路上,我将不胜感激。
您想使用 pipelined updates,您使用的语法存在的问题是它不允许使用聚合运算符和文档字段值。
这里有一个关于如何操作的简单示例:
db.collection.updateMany({},
[
{
"$set": {
"innerArray": {
$map: {
input: "$innerArray",
in: {
$mergeObjects: [
"$$this",
{
copiedDateTime: "$$this.originalDateTime"
}
]
}
}
}
}
}
])