我正在尝试更新MongoDB中的子文档数组。我知道位置运算符$
不支持单个查询中的多个更新。
示例文件:
{
"_id": ObjectId("559a6c281816ba598cdf96cd"),
"collections": [
{
"id": 12,
"name": "a"
},
{
"id": 12,
"name": "b"
}
]
}
我需要使用额外字段id : 12
更新"price" : 12
子文档。我尝试了以下查询,但匹配的相同子文档是gettting已更新,因此我添加了一个额外条件"price" : {$exists : false}
并尝试了"price" : {$ne : 12}
。当我添加"price" : {$exists : false}
时,没有文件被返回。我正在使用PyMongo和python。所以我需要在python代码中执行更新并更新文档。有没有解决方法呢?
尝试查询:
db.scratch.update({ "collections.id" : 12 } , {"$set" : {"collections.$.price" : 12 }})
使用price : false
,price: {$exists : false}
的上述组合尝试了它,但它们也不起作用。但我不断回复一个文档更新的消息。我在我的mongo shell中使用mongo-hacker。
我正在构建一个迁移工具,其中所有客户信息都作为单个文档出现。
{
"_id": ObjectId("559a2d9bfffe043444c72889"),
"age": NumberLong("23"),
"customer_address": [
{
"type": "Work",
"verified": true,
"address": "1A NY"
}
],
"customer_id": NumberLong("3"),
"customer_orders": [
{
"order_date": ISODate("2015-01-01T00:12:01Z"),
"order_id": NumberLong("2"),
"product_id": NumberLong("234")
},
{
"order_date": ISODate("2015-12-01T00:00:00Z"),
"order_id": NumberLong("3"),
"product_id": NumberLong("245")
},
{
"order_date": ISODate("2015-12-21T00:00:00Z"),
"order_id": NumberLong("4"),
"product_id": NumberLong("267")
},
{
"order_id": NumberLong("5"),
"order_date": ISODate("2015-12-29T00:00:00Z"),
"product_id": NumberLong("289")
},
{
"order_id": NumberLong("9"),
"order_date": ISODate("2015-02-01T00:12:05Z"),
"product_id": NumberLong("234")
}
]
}
我从客户表中获取基本信息,从客户地址表中获取地址,并从MySQL中的外键引用相关的另一个表中获取产品日志。现在我想用正确的名称和价格更新产品ID,以便我可以获得客户的视图而不是进行查询以获得产品ID的相应价格,并且因为
中没有加入{
"_id": ObjectId("559a2d9bfffe043444c72889"),
"age": NumberLong("23"),
"customer_address": [
{
"type": "Work",
"verified": true,
"address": "1A NY"
}
],
"customer_id": NumberLong("3"),
"customer_orders": [
{
"name": "Brush",
"order_date": ISODate("2015-01-01T00:12:01Z"),
"order_id": NumberLong("2"),
"product_id": NumberLong("234"),
"price": 12
},
{
"order_date": ISODate("2015-12-01T00:00:00Z"),
"order_id": NumberLong("3"),
"product_id": NumberLong("245")
},
{
"order_date": ISODate("2015-12-21T00:00:00Z"),
"order_id": NumberLong("4"),
"product_id": NumberLong("267")
},
{
"order_id": NumberLong("5"),
"order_date": ISODate("2015-12-29T00:00:00Z"),
"product_id": NumberLong("289")
},
{
"name": "Brush",
"order_id": NumberLong("9"),
"order_date": ISODate("2015-02-01T00:12:05Z"),
"product_id": NumberLong("234"),
"price": 12
}
]
}
尝试过的查询:
db.customer.update({"customer_orders.product_id" : 234 , "customer_orders.name" : {$exists : false}}, {"$set" : {"customer_orders.$.name" : "Brush", "customer_orders.$.price" : 12} } )
返回更新的0个文件。
db.customer.update({"customer_orders.product_id" : 234 , "customer_orders.name" : {$exists : true}}, {"$set" : {"customer_orders.$.name" : "Brush", "customer_orders.$.price" : 12} } )
返回1个文档已更新,但即使在顺序执行相同命令后,第一个字段也会更新。那么有解决方法还是我需要在Python客户端进行更新?
答案 0 :(得分:1)
除了阅读文档以找出有多少数组元素并通过索引更新它们(或者不同的" id"值,但它真的没有多大帮助。 beyound首先读取对象并不重要。
以最安全的方式,不要改变整个文件,并且"保存"它返回,但每个数组元素执行更新:
id = ObjectId("559a2d9bfffe043444c72889")
doc = coll.find_one({ "_id": id })
for idx, el in enumerate(doc["customer_orders"]):
if ( el["product_id"] == 234 ):
update = { "$set": {} }
update["$set"]["customer_orders."+str(idx)+".price"] = 12
update["$set"]["customer_orders."+str(idx)+".name"] = "Brush"
coll.update({ "_id": id },update)
您可以通过批量操作提高效率:
id = ObjectId("559a2d9bfffe043444c72889")
doc = coll.find_one({ "_id": id })
bulk = coll.initialize_ordered_bulk_op()
for idx, el in enumerate(doc["customer_orders"]):
if ( el["product_id"] == 234 ):
update = { "$set": {} }
update["$set"]["customer_orders."+str(idx)+".price"] = 12
update["$set"]["customer_orders."+str(idx)+".name"] = "Brush"
bulk.find({ "_id": id }).update(update)
bulk.execute()
至少将所有更新一次性发送到服务器
但一般的做法是你需要确定"确切"元素或者通过其他唯一标识符的索引来发送正确的更新位置。
尝试类似
的内容{ "customer_orders.price": { "$exists": False } }
或
{ "customer_orders.product_id": 234 }
会遇到以下问题:
$exists
,对于位置$
匹配操作是不可接受的,只有精确值匹配才会生成更新索引。所以请阅读"确切"来自文档本身的id或位置索引,然后处理更新。