Многократное обновление свойств встроенных документов - PullRequest
3 голосов
/ 14 декабря 2011

У меня есть следующая коллекция:

{ 
"Milestones" : [      
    {       "ActualDate" : null,   
    "Index": 0,
    "Name" : "milestone1",  
    "TargetDate" : ISODate("2011-12-13T22:00:00Z"),         
    "_id" : ObjectId("4ee89ae7e60fc615c42e28d1")},         
    {       "ActualDate" : null,    
    "Index" : 0,    
    "Name" : "milestone2",  
    "TargetDate" : ISODate("2011-12-13T22:00:00Z"),         
    "_id" : ObjectId("4ee89ae7e60fc615c42e28d2") } ]
, 
"Name" : "a", "_id" : ObjectId("4ee89ae7e60fc615c42e28ce") 
}

Я хочу обновить определенные документы: для которых указано _id, List of Milestones._id и ActualDate имеет значение null.Я набираю точки, мой код выглядит так:

var query = Query.And(new[] { Query.EQ("_id", ObjectId.Parse(projectId)),
  Query.In("Milestones._id", new BsonArray(values.Select(ObjectId.Parse))), 
 Query.EQ("Milestones.ActualDate", BsonNull.Value) });                

var update = Update.Set("Milestones.$.ActualDate", DateTime.Now.Date);    

Coll.Update(query, update, UpdateFlags.Multi, SafeMode.True);

Или в собственном коде:

db.Projects.update({ "_id" : ObjectId("4ee89ae7e60fc615c42e28ce"), "Milestones._id" : { "$in" : [ObjectId("4ee89ae7e60fc615c42e28d1"), ObjectId("4ee89ae7e60fc615c42e28d2"), ObjectId("4ee8a648e60fc615c41d481e")] }, "Milestones.ActualDate" : null },{ "$set" : { "Milestones.$.ActualDate" : ISODate("2011-12-13T22:00:00Z") } }, false, true)

Но обновляется только первый элемент.

Ответы [ 3 ]

10 голосов
/ 14 декабря 2011

Это невозможно в текущий момент. Флаг multi в обновлении означает обновление нескольких корневых документов. Оператор положения может соответствовать только одному элементу вложенного массива. В mongodb jira есть такая функция . Вы можете проголосовать и подождать.

Текущим решением может быть только загрузка документа, обновление по вашему желанию и сохранение назад или многократное атомарное обновление для каждого идентификатора вложенного массива.

Из документации на mongodb.org :

В настоящее время оператор $ применяется только к первому сопоставленному элементу в запрос

1 голос
/ 23 апреля 2013

Как ответил Андрей Оршич, на данный момент это невозможно, по крайней мере, не так, как вы хотите.Но загрузка документа, изменение массива и последующее его сохранение будут работать.Риск состоит в том, что какой-то другой процесс может изменить массив в это время, поэтому вы перезапишете его изменения.Чтобы избежать этого, вы можете использовать оптимистическую блокировку, особенно если массив не изменяется каждую секунду.

  1. загрузить документ, включая новый атрибут: milestones_version
  2. изменить массивпри необходимости
  3. сохранить обратно в mongodb, но теперь добавьте ограничение запроса к milestones_version и увеличьте его:

    db.Projects.findAndModify({
        query: {
            _id: your_project_id,
            milestones_version: expected_milestones_version
        },
        update: {
            $set: {
                Milestones: modified_milestones
            },
            $inc: {
                milestones_version: 1
            }
        },
        new: 1
    })
    

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

Основная проблема этого решения заключается в том, что вам нужно редактировать каждый проект один за другим (нетmulti: true).Вы все еще можете написать функцию javascript и запустить ее на сервере.

0 голосов
/ 24 октября 2017

Согласно их странице JIRA «Эта новая функция доступна начиная с версии разработки MongoDB 3.5.12 и включена в рабочую версию MongoDB 3.6»

https://jira.mongodb.org/browse/SERVER-1243

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...