我在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)
}
rates.[].productId、rates.[].month和rates.[].rateCardId的组合。$[<identifier>]同时更新多个文档,但值相同。db.avail.rates_copy.update(
{ "_id" : 12345 },
{ $set: { "rates.$[item].rate": 0 } },
{ multi: true,
arrayFilters: [ { "item.rateCardId": {$in: [ 1, 2]} } ]
}
)
最佳答案
最简单的回答是“是”和“否”。
确实有一种方法可以匹配单个数组元素,并在单个语句中用单独的值更新它们,因为实际上可以提供“多个”arrayFilters条件,并在update语句中使用这些标识符。
这里的特定示例的问题是,“更改集”中的一个条目(最后一个)实际上与当前存在的任何数组成员都不匹配。这里的“假定”操作将$push新的未匹配成员放入未找到的数组中。但是,这个特定的操作不能在“单个操作”中完成,但是您可以使用bulkWrite()发出“多个”语句来覆盖该情况。
匹配不同的数组条件
在解释这一点时,请考虑“变更集”中的前两项。您可以使用多个arrayFilters应用“单个”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
}
]
}
arrayFilters列表中使用多个条件指定每个“identifier”以匹配元素,如下所示: {
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
<identifier>.<property>
"rates"数组: "rates.$[one]"
$[<indentifier>]的每个元素以匹配条件。因此,"rates"标识符将匹配以"one"为前缀的条件,同样,对于以"one"为前缀的另一组条件,因此实际的update语句仅适用于与分配给该标识符的条件匹配的那些条件。"two"属性而不是整个对象,那么您只需将其标记为:{ "$set": { "rates.$[one].rate": 400, "rates.$[two].rate": 500 } }
"rates"是另一回事,因为我们基本上需要“document”级别的查询条件来确定数组元素是否“missing”。$push查找每个数组元素,以查看它是否存在。如果不存在,则文档是匹配的,并执行$push。$push起作用的地方,您可以通过在上面的第一个操作中为“change set”中的每个元素添加一个额外的更新来使用它: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 }
)
bulkWrite(),因为这是通过“多个条件”匹配数组元素的要求。我们不需要在$elemMatch条目上这样做,因为它们只查看它们已经应用到的每个数组项,但是作为一个“查询”,条件需要arrayFilters因为简单的“点表示法”将返回不正确的匹配。$elemMatch运算符“否定”$not,因为我们的真正条件是只将“没有匹配数组元素”的文档匹配到所提供的条件,这就是为什么选择追加新元素的理由。$elemMatch的三个“更改集”中的每一个,其中发现文档与“更改集”。{
"_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
}
]
}
$push不匹配的元素的数量,响应将报告这些语句中实际与文档匹配并影响文档的元素数量。在本例中,它是bulkWrite()匹配和修改的,因为“first”更新操作匹配现有数组项,“last”更改更新匹配文档不包含数组项并执行2来修改。$push才能在单个请求中发出“多个”操作。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
}
}
}
}
}
]
bulkWrite()的<identifier>必须由字母数字字符组成,并且必须以字母字符开头。因此,在构造动态语句时,我们在前面加上arrayFilters前缀,然后是正在处理的每个项的当前数组索引。
关于mongodb - Mongodb中匹配子文档的批量更新数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49867541/
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat
我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>