Как определить, действительно ли $addToSet добавил новый элемент в документ MongoDB или элемент уже существует?

Я использую драйвер C# (v1.8.3 от NuGet), и мне трудно определить, является ли $addtoSet/upsert Операция фактически добавила новый элемент в данный массив, или, если элемент уже существовал.

Добавление нового элемента может попасть в два случая: либо документ вообще не существует, а просто создан программой upsert, либо документ существует, но массив не существует или не содержит данный элемент.

Причина, по которой мне нужно это сделать, заключается в том, что у меня есть большие наборы данных для загрузки в MongoDB, которые могут (не должны, но могут) ломаться во время обработки. Если это произойдет, я должен иметь возможность начать резервное копирование с самого начала, не выполняя дублирующую последующую обработку (продолжайте обработку идемпотентом). В моем потоке, если определено, что элемент был добавлен заново, я ставлю в очередь процесс обработки этого элемента в нисходящем направлении, если определено, что он уже был добавлен в документ, то дальнейшая работа в нисходящем направлении не требуется. Моя проблема в том, что результат всегда возвращает сообщение о том, что вызов изменил один документ, даже если элемент уже существовал в массиве, и на самом деле ничего не было изменено.

Исходя из моего понимания API C# драйвера, я должен быть в состоянии сделать звонок с WriteConcern.Acknowledged, а затем проверьте WriteConcernResult.DocumentsAffected чтобы увидеть, действительно ли он обновил документ или нет.

Моя проблема заключается в том, что во всех случаях результат беспокойства о записи возвращает тот документ, который был обновлен.:/

Вот пример документа, который вызывает мой код $addToSet on, который может иметь или не иметь этот конкретный элемент в списке "items" для начала:

{
    "_id" : "some-id-that-we-know-wont-change",
    "items" : [ 
        {                
            "s" : 4,
            "i" : "some-value-we-know-is-static",
        }
    ]
}

Мой запрос всегда использует _id значение, которое известно на основе метаданных обработки:

var query = new QueryDocument
{
     {"_id", "some-id-that-we-know-wont-change"}                       
};

Мое обновление выглядит следующим образом:

var result = mongoCollection.Update(query, new UpdateDocument()
{
     {                                                
          "$addToSet", new BsonDocument()
               {
                    { "items", new BsonDocument()
                         {
                              { "s", 4 },
                              { "i", "some-value-we-know-is-static" }                                                                            
                          } 
                    }
               }
     }
}, new MongoUpdateOptions() { Flags = UpdateFlags.Upsert, WriteConcern = WriteConcern.Acknowledged }); 

if(result.DocumentsAffected > 0 || result.UpdatedExisting)
{
     //DO SOME POST PROCESSING WORK THAT SHOULD ONLY HAPPEN ONCE PER ITEM                                                
}

Если я запускаю этот код один раз для пустой коллекции, документ добавляется, и ответ такой, как ожидалось (DocumentsAffected = 1, UpdatedExisting = false). Если я запустлю его снова (любое количество раз), документ не будет обновлен, поскольку он останется неизменным, но результат теперь неожиданный (DocumentsAffected = 1, UpdatedExisting = true).

Разве это не должно возвращаться DocumentsAffected = 0 если документ не изменился?

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

Есть ли способ заставить это работать за один звонок?

1 ответ

Конечно, здесь вы фактически проверяете ответ, который указывает, был ли документ обновлен или вставлен, или фактически, если ни одна из операций не произошла. Это ваш лучший показатель для $addToSet чтобы выполнить обновление, документ будет обновлен.

$addToSet Сам оператор не может создавать дубликаты, такова природа оператора. Но у вас действительно могут быть некоторые проблемы с вашей логикой:

{                                                
      "$addToSet", new BsonDocument()
           {
                { "items", new BsonDocument()
                     {
                          { "id", item.Id },
                          { "v", item.Value } 
                     }
                }
           }
 }

Ясно, что вы показываете, что элемент в вашем "наборе" состоит из двух полей, поэтому, если этот контент изменяется каким-либо образом (то есть идентичным идентификатором, но разным значением), тогда этот элемент фактически является "уникальным" членом набора и будет быть добавленным Там не будет никакого способа, например, для $addToSet Оператор не добавляет новые значения исключительно на основе "id" в качестве уникального идентификатора. Вы должны были бы фактически свернуть это в коде.

Вторая возможность здесь для формы дубликата состоит в том, что ваша часть запроса неправильно находит документ, который должен быть обновлен. Результатом этого будет создание нового документа, который содержит только новый указанный элемент в "наборе". Таким образом, распространенная ошибка использования выглядит примерно так:

db.collection.update(
    { 
        "id": ABC,
        "items": { "$elemMatch": {
            "id": 123, "v": 10
         }},
    {
        "$addToSet": {
            "items": {
                "id": 123, "v": 10
            }
        }
    },
    { "upsert": true }
)

Результатом такой операции всегда будет создание нового документа, поскольку существующий документ не содержит указанный элемент в "наборе". Правильная реализация - не проверять наличие элемента "set" и разрешать $addToSet делать работу.

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

Если вы уверены, что создаются новые записи, просмотрите код на наличие $push или действительно, и манипулирование массивами в коде, который, кажется, действует в том же поле.

Но если вы используете оператор правильно, то $addToSet делает именно то, для чего предназначен.

Другие вопросы по тегам