MongoDB atomic "findOrCreate": findOne, вставить, если не существует, но не обновлять

как говорится в заголовке, я хочу выполнить поиск (один) для документа, посредством _id, и если он не существует, создайте его, затем, было ли оно найдено или было создано, вернуть его в обратном вызове.

Я не хочу обновлять его, если он существует, поскольку я читал findAndModify. Я видел много других вопросов по Stackoverflow относительно этого, но опять же, не хочу ничего обновлять.

Я не уверен, создавая (из-за отсутствия), ТО, на самом деле это обновление, о котором все говорят, все это путается: (

Ответ 1

Начиная с MongoDB 2.4, больше не нужно полагаться на уникальный индекс (или любой другой обходной путь) для атомных findOrCreate подобных операций.

Это благодаря оператору $setOnInsert, начиная с 2.4, что позволяет указывать обновления, которые должны выполняться только при вставке документов.

Это, в сочетании с опцией upsert, означает, что вы можете использовать findAndModify для выполнения атомной операции findOrCreate.

db.collection.findAndModify({
  query: { _id: "some potentially existing id" },
  update: {
    $setOnInsert: { foo: "bar" }
  },
  new: true,   // return new doc if one is upserted
  upsert: true // insert the document if it does not exist
})

Поскольку $setOnInsert влияет только на вставленные документы, если существующий документ найден, модификация не будет выполнена. Если документ не существует, он будет поддерживать один с указанным _id, а затем выполнить установку только вставки. В обоих случаях документ возвращается.

Ответ 2

Его немного грязно, но вы можете просто вставить его.

Убедитесь, что на нем есть уникальный индекс (если вы используете _id, это нормально, оно уже уникально).

Таким образом, если элемент уже присутствует, он вернет исключение, которое вы можете поймать.

Если он отсутствует, новый документ будет вставлен.

Обновлено: подробное объяснение этой техники в Документация MongoDB

Ответ 3

Версии драйверов> 2

Используя последнюю версию драйвера (> версия 2), вы будете использовать findOneAndUpdate, так как findAndModify устарела. Новый метод принимает 3 аргумента: filter, объект update (который содержит свойства по умолчанию, которые должны быть вставлены для нового объекта) и options которых необходимо указать операцию upsert.

Используя синтаксис обещания, это выглядит так:

const result = await collection.findOneAndUpdate(
  { _id: new ObjectId(id) },
  {
    $setOnInsert: { foo: "bar" },
  },
  {
    returnOriginal: false,
    upsert: true,
  }
);

const newOrUpdatedDocument = result.value;

Ответ 4

Вот что я сделал (Ruby MongoDB driver):

$db[:tags].update_one({:tag => 'flat'}, {'$set' => {:tag => 'earth' }}, { :upsert => true })}

Он обновит его, если он существует, и вставьте его, если это не так.