РЕДАКТИРОВАТЬ: После дальнейших размышлений мой первоначальный ответ был правильным, но расточительным. В частности, первый шаг не является необходимым, поэтому вот пересмотренная версия:
Вы можете эмулировать этот процесс в два этапа:
findAndModify с _id
и max_value
$lte
значением, которое вы сейчас пытаетесь вставить. Поскольку _id
является уникальным, вы знаете, что этому запросу может соответствовать только ноль или один документ - при условии, что документ с таким _id
существует, он равен нулю в случае, если max_value
больше, чем вы вставка, и один в случае, когда он меньше или равен. В обновлении $push
новое значение и $set
max_value
.
Если и только если шаг # 1 завершился неудачно, снова найдитеAndModify с _id
и $push
новым значением для массива. Поскольку шаг № 1 не выполнен, мы знаем, что текущий max_value
больше нового значения, поэтому мы можем его игнорировать и просто $push
новое значение.
Вот пример кода Python для реализации этого:
# the_id is the ObjectId of the document we want to modify
# new_value is the new value to append to the list
rslt1 = rslt2 = None
rslt1 = db.collection.find_and_modify(
{'_id': the_id, 'max_value': {'$lte': new_value}},
{'$push': {'array': new_value}, '$set': {'max_value': new_value}})
if rslt1 is None:
rslt2 = db.collection.find_and_modify(
{'_id': the_id},
{'$push': {'array': new_value}})
# only one of these will be non-None; this
# picks whichever is non-None and assigns
# it to rslt
rslt = rslt1 or rslt2
(Этот оригинальный ответ работает, но обновленная версия выше более эффективна.)
Вы можете эмулировать этот процесс в три этапа:
findAndModify документ с указанными _id
и с max_value
$gt
текущим значением, которое вы пытаетесь вставить. Поскольку _id
является уникальным, вы знаете, что этому запросу может соответствовать только ноль или один документ - при условии, что документ с таким _id
существует, он равен нулю в случае, если max_value
меньше, чем вы вставка и одна в том случае, когда она больше. Часть обновления для этого findAndModify будет $push
новым значением массива.
Если и только если шаг # 1 завершился неудачно, снова найдитеAndModify с _id
и max_value
$lte
значением, которое вы сейчас пытаетесь вставить. В обновлении $push
новое значение и $set
max_value
.
В том и только в том случае, если шаг № 2 завершился неудачно, снова найдитеAndModify с _id
и $push
новым значением массива. Это относится к случаю, когда между шагами № 1 и № 2 другой поток повысил значение max_value
до значения, превышающего значение, которое вы в настоящее время вставляете.
Вот пример кода Python для реализации этого:
# the_id is the ObjectId of the document we want to modify
# new_value is the new value to append to the list
rslt1 = rslt2 = rslt3 = None
rslt1 = db.collection.find_and_modify(
{'_id': the_id, 'max_value': {'$gt': new_value}},
{'$push': {'array': new_value}})
if rslt1 is None:
rslt2 = db.collection.find_and_modify(
{'_id': the_id, 'max_value': {'$lte': new_value}},
{'$push': {'array': new_value}, '$set': {'max_value': new_value}})
if rslt1 is None and rslt2 is None:
rslt3 = db.collection.find_and_modify(
{'_id': the_id},
{'$push': {'array': new_value}})
# only one of these will be non-None; this
# picks whichever is non-None and assigns
# it to rslt
rslt = rslt1 or rslt2 or rslt3