Upserts в mongodb при использовании пользовательских значений _id - PullRequest
14 голосов
/ 13 января 2012

Мне нужно вставить документ, если он не существует. Я знаю, что вариант "upsert" может сделать это, но у меня есть некоторые особые потребности.

Сначала мне нужно создать документ только с его полем _id, но только если он еще не существует. Мое поле _id - это число, сгенерированное мной (не ObjectId). Если я использую опцию «upsert», то получаю «Mod on _id not позволен»

db.mycollection.update({ _id: id }, { _id: id }, { upsert: true });

Я знаю, что мы не можем использовать _id в наборе $.

Итак, мой вопрос: есть ли какой-нибудь способ «создать, если не существует» атомарно в mongodb?

EDIT: По предложению @Barrie это работает (используя nodejs и mongoose):

var newUser = new User({ _id: id });
newUser.save(function (err) {               
    if (err && err.code === 11000) {            
            console.log('If duplicate key the user already exists', newTwitterUser);
        return;
    }
    console.log('New user or err', newTwitterUser);
});

Но я все еще задаюсь вопросом, является ли это лучшим способом сделать это.

Ответы [ 4 ]

25 голосов
/ 18 декабря 2012

У меня была такая же проблема, но я нашел лучшее решение для своих нужд.Вы можете использовать тот же стиль запроса, если просто удалите атрибут _id из объекта обновления.Поэтому, если сначала вы получите сообщение об ошибке:

db.mycollection.update({ _id: id }, {$set: { _id: id, name: 'name' }}, { upsert: true });

, используйте вместо этого:

db.mycollection.update({ _id: id }, {$set: { name: 'name' }}, { upsert: true });

Это лучше, потому что это работает как для вставки, так и для обновления.

5 голосов
/ 21 мая 2014

ОБНОВЛЕНИЕ : Upsert с _id можно сделать без $setOnInsert, как объяснено @Barrie выше.

Хитрость заключается в использовании $setOnInsert:{_id:1} с upsert, таким образом _idзаписывается только в том случае, если это вставка, а не для обновлений.

Только, была ошибка, препятствовавшая тому, чтобы это работало до v2.6 - я только что попробовал это на 2.4, и это неработает.

Обходной путь, который я использую, имеет другое поле идентификатора с уникальным индексом.Например.$setOnInsert:{myId:1}.

4 голосов
/ 13 января 2012

Вы можете просто использовать insert (). Если документ с указанным вами _id уже существует, метод insert () завершится ошибкой, ничего не изменится - поэтому «создать, если он не существует» - это то, что он уже делает по умолчанию, когда вы используете insert () с пользователем создан _id.

1 голос
/ 07 августа 2014

Обратите внимание, что $ setOnInsert не работает легко, когда вы вставляете простой объект key => value (не $ set или другой).Мне нужно использовать это (в PHP):

public function update($criteria , $new_object, array $options = array()){
    // In 2.6, $setOnInsert with upsert == true work with _id field
    if(isset($options['upsert']) && $options['upsert']){
        $firstKey = array_keys($new_object)[0];
        if(strpos($firstKey, '$')===0){
            $new_object['$setOnInsert']['_id'] = $this->getStringId();
        }
        //Even, we need to check if the object exists
        else if($this->findOne($criteria, ['_id'])===null){
            //In this case, we need to set the _id
            $new_object['_id'] = $this->getStringId();
        }

    }
    return parent::update($criteria, $new_object, $options);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...