2014-04-28 1 views
0

Похоже, что вы (/ I) не можете иметь как upsert, так и array element update operation.Обновить элемент, если позиция не указана с помощью Upsert

Если вы (питон):

findDct = { 
    "_id": ObjectId("535e3ab9c36b4417d031402f"), 
    'events.ids': '176976332' 
} 
print col.update(findDct, {"$set" : {"events.$.foo": "bar"} }, upsert=True) 

Он выбросит:

pymongo.errors.DuplicateKeyError: insertDocument :: caused by :: 11000 E11000 
duplicate key error index: test.col.$_id_ dup key: { : ObjectId('535e3ab9c36b4417d031402f') } 

Это происходит потому, что «_id», конечно, индекс и Монго пытается вставить документ как новый так как запрос на поиск выходит из строя на его 'events.ids': '176976332' (cheat).

Возможно ли обновить неизвестный элемент в массиве с помощью upsert True/how?

ответ

0

Да, но вы делаете это неправильно. Вместо того, чтобы «найти» элемент, который вы не уверены, существует ли он или нет, то попробуйте применить $addToSet оператор вместо:

db.collection.update(
    { "_id": ObjectId("535e3ab9c36b4417d031402f" }, 
    { 
     "$addToSet": { "events": { "foo": "bar" } } 
    }, 
    { "upsert": true } 
) 

Обратите внимание, из документации positional $ оператора, что вы не должны использовать оператор $ с «upserts», поскольку это приведет к тому, что имя поля будет интерпретировано как «литерал» (который включает в себя значение, как в "events.$.foo"), и это будет фактическое поле, вставленное в документ.

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

Другой адаптации с "bulk" методами, драйвер PyMongo уже имеет хороший API для этого, но это общая форма:

db.runCommand({ 
    "update": "collection", 
    "updates": [ 
     { 
      "q": { "_id": ObjectId("535e3ab9c36b4417d031402f" } }, 
      "u": { 
       "$addToSet": { 
        "events": { 
         "foo": "bar", "bar": "baz" 
        } 
       } 
      }, 
      "upsert": true 
     }, 
     { 
      "q": { "_id": ObjectId("535e3ab9c36b4417d031402f" } }, 
      "u": { 
       "$set": { "events.foo": "bar" } 
      } 
     } 
    ] 
}) 

Но все-таки быть очень осторожным, чтобы не производить дубликаты в вашем sub-document array, если вы можете четко видеть этот случай. Но это метод, так как каждое обновление будет каскадным, даже если первая форма не смогла что-либо добавить. Не лучший пример, но я надеюсь, что вы это поняли.

+0

Хммм, только если '' 'принимается командой' updates', что если ваша вторая часть что-то вроде: '{" q ": {" _id ": ObjectId (" 535e3ab9c36b4417d031402f "), 'events.ids' : '176976332'}, "u": {"$ set": {"events. $. Foo": "bar"}}} '? То, что мне удалось (без большого количества), - это сначала '$ set' с' upsert = false', и если это не будет '$ addToSet' (как и при первом обновлении). Теперь нужно измерить, что лучше (как для ресурсов сервера, так и для латентности): используйте массовый API с всегда 2 запросами для каждого вложенного документа или проверьте, не сработает ли $ set с позиционным $, и поэтому используйте $ addToSet. – Diolor

+0

@Diolor Что-то вроде этого, но если вы хотите использовать позиционные обновления с одинаковой сортировкой или «большой» конструкцией, то это должна быть последняя попытка. Поймите, что если элемент не существует в массиве, тогда любой «upsert» ** будет ** создавать новую запись. И указанное значение '_id' в запросе приводит к ошибке повторяющегося ключа. Понимаю? –

+0

Правда. Ну, это зависит от ваших потребностей, если это может стать опасным или нет. И если вы уверены, что документы всегда существуют (в моем случае), нет смысла использовать upsert. Понял. Спасибо – Diolor

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

  • Нет связанных вопросов^_^