2015-07-06 2 views
3

Я пытаюсь обновить массив поддокументов в MongoDB. Я знаю, что множественные обновления в одном запросе не поддерживаются оператором позиционирования $.Позитанные обновления в MongoDB

Образец документа:

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

мне нужно обновить id : 12 поддокументы с дополнительным полем "price" : 12. Я попробовал следующий запрос, но тот же поддокумент, который соответствует, обновляется, поэтому я добавил дополнительное условие "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-hacker в своей оболочке mongo.

Я создаю средство миграции, в котором вся информация о клиенте присутствует как один документ.

{ 
    "_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": 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 Я думал, что возможны повторные обновления с одним и тем же условием, т. Е. Несколько обновлений в нескольких запросах. Когда у меня есть много документов того же идентификатора, где я хочу обновить. Я уточню вопрос с точным сценарием. – 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() 

который по крайней мере, посылает все обновления сразу на сервер

но общий курс является то, что вам нужно определить элемент «точный» либо по индексу других уникальный идентификатор, чтобы отправить правильную позицию обновления.

Пытаясь что-то вроде

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

или

{ "customer_orders.product_id": 234 } 

собирается ударить следующие проблемы:

  • Это будет соответствовать несколько вещей, так или иначе, в обоих случаях вообще
  • Для $exists неприемлемо для r a positional $ операции совпадения, только совпадения точных значений создают индекс для обновления.

Так что прочитайте «точные» идентификаторы идентификаторов или позиций из самого документа, а затем обработайте обновление.

Смежные вопросы