MongoDB中的正面更新

时间:2015-07-06 12:14:36

标签: python mongodb pymongo

我正在尝试更新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 : falseprice: {$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客户端进行更新?

1 个答案:

答案 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或位置索引,然后处理更新。