我正在运行Mongodb 3.6。以下是我的文档结构,它存储产品列表的每月费率信息:
{
"_id": 12345,
"_class": "com.example.ProductRates",
"rates": [
{
"productId": NumberInt(1234),
"rate": 100.0,
"rateCardId": NumberInt(1),
"month": NumberInt(201801)
},
{
"productId": NumberInt(1234),
"rate": 200.0,
"rateCardId": NumberInt(1),
"month": NumberInt(201802)
},
{
"productId": NumberInt(1234),
"rate": 400.0,
"rateCardId": NumberInt(2),
"month": NumberInt(201803)
},
{
"productId": NumberInt(1235),
"rate": 500.0,
"rateCardId": NumberInt(1),
"month": NumberInt(201801)
},
{
"productId": NumberInt(1235),
"rate": 234,
"rateCardId": NumberInt(2),
"month": NumberInt(201803)
}
]
}
对相关联的计费卡的任何更改,都会将更新传播到“费率”数组中的多个子文档。
以下是需要应用于上述文档的更改
{
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
},
{
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
},
{
"productId" : NumberInt(1235),
"rate" : 700.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
有没有一种方法可以在数组速率下更新子文档,而无需将整个文档加载到内存中,以便合并更改?假设我的子文档标识符是费率的组合。[]. productId
,费率。[]。月
和费率。[]。rateCardId
。
我可以使用一次更新多个文档$[
db.avail.rates_copy.update(
{ "_id" : 12345 },
{ $set: { "rates.$[item].rate": 0 } },
{ multi: true,
arrayFilters: [ { "item.rateCardId": {$in: [ 1, 2]} } ]
}
)
而在我的例子中,值将根据上述标识符组合在文档之间更改,这些标识符组合来自不同的系统。
有没有办法说,用新值更新变更集中与(productId、month和rateCardId)匹配的所有子文档。
在最简短的回答中,它既是“是”又是“否”。
确实有一种方法可以匹配单个数组元素,并在单个语句中使用单独的值更新它们,因为您实际上可以提供“多个”数组过滤器
条件,并在更新语句中使用这些标识符。
这里特定示例的问题是,“更改集”(最后一个)中的一个条目实际上与当前存在的任何数组成员都不匹配。此处的“假定”操作是将新的未匹配成员推送到未找到它的数组中。但是,该特定操作不能在“单个操作”中完成,但您可以使用bulkWrite()
发出“多个”语句来涵盖该情况。
解释这一点,在你的“改变集”中考虑前两个项目。您可以使用多个arrayFilter
应用“单个”update语句,如下所示:
db.avail_rates_copy.updateOne(
{ "_id": 12345 },
{
"$set": {
"rates.$[one]": {
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
},
"rates.$[two]": {
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
},
{
"arrayFilters": [
{
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
{
"two.productId": NumberInt(1234),
"two.rateCardId": NumberInt(1),
"two.month": NumberInt(201802)
}
]
}
)
如果运行,您将看到修改后的文档变为:
{
"_id" : 12345,
"_class" : "com.example.ProductRates",
"rates" : [
{ // Matched and changed this by one
"productId" : 1234,
"rate" : 400,
"rateCardId" : 1,
"month" : 201801
},
{ // And this as two
"productId" : 1234,
"rate" : 500,
"rateCardId" : 1,
"month" : 201802
},
{
"productId" : 1234,
"rate" : 400,
"rateCardId" : 2,
"month" : 201803
},
{
"productId" : 1235,
"rate" : 500,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1235,
"rate" : 234,
"rateCardId" : 2,
"month" : 201803
}
]
}
请注意,在阵列过滤器列表中指定每个“标识符”,并使用多个条件匹配元素,如下所示:
{
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
因此,每个“条件”有效地映射为:
<identifier>.<property>
所以它知道要通过
$[的更新块中的语句来查看
“速率”
数组
"rates.$[one]"
并查看
“rates”
的每个元素以匹配条件。因此,“one”
标识符将匹配前缀为“one”
的条件,同样,对于前缀为“two”
的另一组条件,因此实际的update语句仅适用于与分配给标识符的条件匹配的条件。
如果您只是想要
"比率"
属性而不是整个对象,那么您只需注释为:
{ "$set": { "rates.$[one].rate": 400, "rates.$[two].rate": 500 } }
因此,第一部分相对容易理解,但是如上所述,为“不存在的元素”执行
$push
是另一回事,因为我们基本上需要“文档”级别的查询条件来确定数组元素是否“丢失”。
这本质上意味着,您需要使用
$push
发出更新,查找每个数组元素以查看它是否存在。如果不存在,则文档是匹配的,并执行$push
。
这就是
bulkWrite()
发挥作用的地方,您可以通过在上面的第一个操作中为“更改集”中的每个元素添加额外的更新来使用它:
db.avail_rates_copy.bulkWrite(
[
{ "updateOne": {
"filter": { "_id": 12345 },
"update": {
"$set": {
"rates.$[one]": {
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
},
"rates.$[two]": {
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
},
"rates.$[three]": {
"productId" : NumberInt(1235),
"rate" : 700.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
},
"arrayFilters": [
{
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
{
"two.productId": NumberInt(1234),
"two.rateCardId": NumberInt(1),
"two.month": NumberInt(201802)
},
{
"three.productId": NumberInt(1235),
"three.rateCardId": NumberInt(1),
"three.month": NumberInt(201802)
}
]
}},
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId" : NumberInt(1234),
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
}
}
}
},
"update": {
"$push": {
"rates": {
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
}
}
}
}},
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId" : NumberInt(1234),
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
},
"update": {
"$push": {
"rates": {
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
}},
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId" : NumberInt(1235),
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
},
"update": {
"$push": {
"rates": {
"productId" : NumberInt(1235),
"rate" : 700.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
}}
],
{ "ordered": true }
)
请注意带有查询筛选器的
$elemMatch
,因为这是通过“多个条件”匹配数组元素的要求。我们不需要在ArrayFilter
条目上使用它,因为它们只查看已应用到的每个数组项,但作为“查询”,条件要求$elemMatch
,因为简单的“点符号”会返回不正确的匹配。
另请参见此处使用的
$not
运算符“否定”$elemMatch
,因为我们的真正条件是仅将“没有匹配数组元素”的文档与提供的条件匹配,这就是选择附加新元素的理由。
向服务器发出的这条语句实际上尝试了四个更新操作,一个用于尝试更新匹配的数组元素,另一个用于尝试
$push
的三个“更改集”中的每一个,其中发现文档与“更改集”中数组元素的条件不匹配。
因此,结果正如预期的那样:
{
"_id" : 12345,
"_class" : "com.example.ProductRates",
"rates" : [
{ // matched and updated
"productId" : 1234,
"rate" : 400,
"rateCardId" : 1,
"month" : 201801
},
{ // matched and updated
"productId" : 1234,
"rate" : 500,
"rateCardId" : 1,
"month" : 201802
},
{
"productId" : 1234,
"rate" : 400,
"rateCardId" : 2,
"month" : 201803
},
{
"productId" : 1235,
"rate" : 500,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1235,
"rate" : 234,
"rateCardId" : 2,
"month" : 201803
},
{ // This was appended
"productId" : 1235,
"rate" : 700,
"rateCardId" : 1,
"month" : 201802
}
]
}
根据实际不匹配的元素数量,
bulkWrite()
响应将报告这些语句中实际匹配并影响文档的元素数量。在这种情况下,它是2
匹配和修改的,因为“第一次”更新操作匹配现有数组条目,“最后一次”更改更新匹配文档不包含数组条目,并执行$push
进行修改。
这就是组合方法,其中:
>
你问题中更新的第一部分非常简单,可以在一个语句中完成,正如第一部分所展示的。
第二部分,在当前文档数组中有一个“目前不存在”的数组元素,这实际上需要您使用
bulk↑()
以便在一个请求中发出“多个”操作。
因此,对于单个操作,更新为“是”。但增加差异意味着多重操作。但是您可以将这两种方法结合起来,正如本文所示。
有许多“花哨”的方式可以用代码基于“更改集”数组内容构建这些语句,因此您不需要对每个成员进行“硬编码”。
作为JavaScript的基本情况,并与当前版本的mongo shell兼容(有点令人恼火的是,它不支持对象扩展操作符):
db.getCollection('avail_rates_copy').drop();
db.getCollection('avail_rates_copy').insert(
{
"_id" : 12345,
"_class" : "com.example.ProductRates",
"rates" : [
{
"productId" : 1234,
"rate" : 100,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1234,
"rate" : 200,
"rateCardId" : 1,
"month" : 201802
},
{
"productId" : 1234,
"rate" : 400,
"rateCardId" : 2,
"month" : 201803
},
{
"productId" : 1235,
"rate" : 500,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1235,
"rate" : 234,
"rateCardId" : 2,
"month" : 201803
}
]
}
);
var changeSet = [
{
"productId" : 1234,
"rate" : 400.0,
"rateCardId": 1,
"month" : 201801
},
{
"productId" : 1234,
"rate" : 500.0,
"rateCardId": 1,
"month" : 201802
},
{
"productId" : 1235,
"rate" : 700.0,
"rateCardId": 1,
"month" : 201802
}
];
var arrayFilters = changeSet.map((obj,i) =>
Object.keys(obj).filter(k => k != 'rate' )
.reduce((o,k) => Object.assign(o, { [`u${i}.${k}`]: obj[k] }) ,{})
);
var $set = changeSet.reduce((o,r,i) =>
Object.assign(o, { [`rates.$[u${i}].rate`]: r.rate }), {});
var updates = [
{ "updateOne": {
"filter": { "_id": 12345 },
"update": { $set },
arrayFilters
}},
...changeSet.map(obj => (
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": Object.keys(obj).filter(k => k != 'rate')
.reduce((o,k) => Object.assign(o, { [k]: obj[k] }),{})
}
}
},
"update": {
"$push": {
"rates": obj
}
}
}}
))
];
db.getCollection('avail_rates_copy').bulkWrite(updates,{ ordered: true });
这将动态构建一个“批量”更新操作列表,如下所示:
[
{
"updateOne": {
"filter": {
"_id": 12345
},
"update": {
"$set": {
"rates.$[u0].rate": 400,
"rates.$[u1].rate": 500,
"rates.$[u2].rate": 700
}
},
"arrayFilters": [
{
"u0.productId": 1234,
"u0.rateCardId": 1,
"u0.month": 201801
},
{
"u1.productId": 1234,
"u1.rateCardId": 1,
"u1.month": 201802
},
{
"u2.productId": 1235,
"u2.rateCardId": 1,
"u2.month": 201802
}
]
}
},
{
"updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId": 1234,
"rateCardId": 1,
"month": 201801
}
}
}
},
"update": {
"$push": {
"rates": {
"productId": 1234,
"rate": 400,
"rateCardId": 1,
"month": 201801
}
}
}
}
},
{
"updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId": 1234,
"rateCardId": 1,
"month": 201802
}
}
}
},
"update": {
"$push": {
"rates": {
"productId": 1234,
"rate": 500,
"rateCardId": 1,
"month": 201802
}
}
}
}
},
{
"updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId": 1235,
"rateCardId": 1,
"month": 201802
}
}
}
},
"update": {
"$push": {
"rates": {
"productId": 1235,
"rate": 700,
"rateCardId": 1,
"month": 201802
}
}
}
}
}
]
正如在一般答案的“长格式”中所描述的一样,当然只是简单地使用输入“数组”内容来构造所有这些语句。
您可以用任何语言进行这样的动态对象构造,并且所有MongoDB驱动程序都接受某种类型的结构输入,您可以“操纵”该结构,然后在将其发送到服务器执行之前将其转换为BSON。
注:
在我的MongoDB中,我有文档,如下所示。如何更新内部文档结构中的文档:<code>〔{1},〔{2}〕,〔{3}〕、〔{4}〕</code>。 例如:子子文档中的子文档具有和以及 和
问题内容: 我正在使用MongoDB数据库,该数据库的收集模型包括 班级 , 学生 , 学科 和[学术] 表现 。以下是基于猫鼬的架构和模型: 该集合的文件是最复杂的地段; 示例文档为: 我能够使用以下代码检索现有的班级文档并将其添加到分数中: 但是,如何添加/更新/删除该特定学生的成绩?我需要能够通过以下方式与集合进行交互: 检索所有学生或特定学生的分数(检索数组中的特定元素) 为特定主题添加/
我目前正在Java应用程序上使用MongoDB的驱动程序。假设我将以下两个文档存储在一个集合中: 文件A: 文件B: 现在,我想通过使用key1和key2的值从集合中检索DocumentB的精确匹配,而不返回documenta,而不是_id,因为我事先不知道它。 如果我只是使用DocumentB作为查询(没有_id),Mongo也将返回DocumentA,因为它匹配所有的键和值,不考虑Docume
主要内容:update() 方法,save() 方法在 MongoDB 中,可以使用 update() 和 save() 方法来更新集合中的文档。其中 update() 方法可以更新现有文档中的值,而 save() 方法则可以使用传入文档来替换已有文档。 update() 方法 update() 方法用于更新现有文档中的值,其语法格式如下: db.collection_name.update( <query>, <update>,
问题内容: 我正在使用MongoDB 3.2和MongoDB Java驱动程序3.2。我有一个包含数百个更新文档的数组,这些文档现在应该保存/存储在MongoDB中。为了做到这一点,我遍历数组,并为该数组中的每个文档调用方法。 现在,我想通过批量更新重新实现此逻辑。我尝试在使用MongoDB Java驱动程序3.2的MongoDB 3.2中找到批量更新的示例。 我尝试了这段代码: 但是,似乎这种方
将mongodb与pymongo一起使用,我有以下文档: 我想更新示例子文档(这是一个数组元素,因为可能有多个示例)。我有以下代码,但它不工作... 谁能告诉我这个有什么问题吗?