2015-07-06 131 views
3

我正在嘗試更新MongoDB中的子文檔數組。我知道位置運算符$不支持單個查詢中的多個更新。MongoDB中的位置更新

樣品文件:

{ 
    "_id": ObjectId("559a6c281816ba598cdf96cd"), 
    "collections": [ 
    { 
     "id": 12, 
     "name": "a" 
    }, 
    { 
     "id": 12, 
     "name": "b" 
    } 
    ] 
} 

我需要一個額外的字段"price" : 12更新id : 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客戶端中進行更新?

+0

你不是帶*號開始你的問題:「我知道,在一個查詢中多次更新不被定位運營商支持」 *?因此,由於這兩個子文檔都具有相同的「ID」值,那麼您還希望發生什麼?不要假設你使用'「collections.price」:{「$ exists」:false}'因爲這是元素的路徑。 –

+0

@BlakesSeven我認爲在相同的條件下可能會重複更新,即多個查詢中有多個更新。當我有許多我想要更新的相同ID的文檔時。我將用確切的場景更新問題。 – xtreak

+0

請按照「$ exists:false」不能匹配元素。如果所有項目都有相同的「ID」,那麼它只會匹配第一個。不同的「id」值當然是另一種情況。 –

回答

1

真的是沒有多少這方面比閱讀文件,以瞭解有多少數組元素有和指數(或不同的「ID」的值更新它們,但它並沒有真正重要的是首先閱讀對象。

在最安全的方式,不會改變整個文件和「挽救」它回來了,但執行每個數組元素的更新:

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() 

其中至少有一次將所有的更新到服務器

但一般過程是,則需要由OT的指數,以查明「確切」元素她的唯一標識符以便發送正確的更新位置。

試圖像

{ "customer_orders.price": { "$exists": False } } 

{ "customer_orders.product_id": 234 } 

是要打到了以下問題:

  • 它無論如何都會匹配多個東西在這兩種情況下,一般
  • 對於$exists,是不可接受的r位置$匹配操作,只有精確值匹配產生更新索引。

因此,從文檔本身讀取「確切」的id或位置索引,然後處理更新。