Подтвердить что ты не робот

Mongodb обновляет глубоко вложенный поддокумент

У меня есть структура документа, которая глубоко вложена, например:

{id: 1, 
 forecasts: [ { 
             forecast_id: 123, 
             name: "Forecast 1", 
             levels: [ 
                { level: "proven", 
                  configs: [
                            { 
                              config: "Custom 1",
                              variables: [{ x: 1, y:2, z:3}]
                            }, 
                            { 
                              config: "Custom 2",
                              variables: [{ x: 10, y:20, z:30}]
                            }, 
                    ]
                }, 
                { level: "likely", 
                  configs: [
                            { 
                              config: "Custom 1",
                              variables: [{ x: 1, y:2, z:3}]
                            }, 
                            { 
                              config: "Custom 2",
                              variables: [{ x: 10, y:20, z:30}]
                            }, 
                    ]
                }
            ]
        }, 
    ]

}

Я пытаюсь обновить коллекцию, чтобы вставить новую конфигурацию, которая выглядит так:

newdata =  {
  config: "Custom 1", 
  variables: [{ x: 111, y:2222, z:3333}]
}

Я пытаюсь сделать что-то подобное в mongo (в Python):

db.myCollection.update({"id": 1, 
                        "forecasts.forecast-id": 123, 
                        "forecasts.levels.level": "proven", 
                        "forecasts.levels.configs.config": "Custom 1"
                         },
                         {"$set": {"forecasts.$.levels.$.configs.$": newData}}
                      )

Я получаю "Невозможно применить оператор позиционирования без соответствующего поля запроса, содержащего ошибку массива". Каков правильный способ сделать это в монго? Это mongo v2.4.1.

4b9b3361

Ответ 1

К сожалению, вы не можете использовать оператор $ более одного раза для каждого ключа, поэтому вам нужно использовать числовые значения для остальных. Как в:

db.myCollection.update({
    "id": 1, 
    "forecasts.forecast-id": 123, 
    "forecasts.levels.level": "proven", 
    "forecasts.levels.configs.config": "Custom 1"
  },
  {"$set": {"forecasts.$.levels.0.configs.0": newData}}
)

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

Одна возможность: сделайте forecasts собственную коллекцию и предположив, что у вас есть фиксированный набор значений level, сделайте level объект вместо массива:

{
  _id: 123,
  parentId: 1,
  name: "Forecast 1", 
  levels: {
    proven: { 
      configs: [
        { 
          config: "Custom 1",
          variables: [{ x: 1, y:2, z:3}]
        }, 
        { 
          config: "Custom 2",
          variables: [{ x: 10, y:20, z:30}]
        }, 
      ]
    },
    likely: {
      configs: [
        { 
          config: "Custom 1",
          variables: [{ x: 1, y:2, z:3}]
        }, 
        { 
          config: "Custom 2",
          variables: [{ x: 10, y:20, z:30}]
        }, 
      ]
    }
  }
}

Затем вы можете обновить его, используя:

db.myCollection.update({
    _id: 123,
    'levels.proven.configs.config': 'Custom 1'
  },
  { $set: { 'levels.proven.configs.$': newData }}
)

Ответ 2

Удалось решить проблему с использованием мангуста:

Все, что вам нужно знать, это "_id всего субдокумента в цепочке (mongoose автоматически создает" _id "для каждого поддокумента).

например -

  SchemaName.findById(_id, function (e, data) {
      if (e) console.log(e);
      data.sub1.id(_id1).sub2.id(_id2).field = req.body.something;

      // or if you want to change more then one field -
      //=> var t = data.sub1.id(_id1).sub2.id(_id2);
      //=> t.field = req.body.something;

      data.save();
  });

Подробнее о вспомогательном документе _id method в документации по mongoose.

Объяснение: _id для SchemaName, _id1 для sub1 и _id2 для sub2 - вы можете сохранить цепочку таким образом.

* Вам не нужно использовать метод findById, но он мне кажется наиболее удобным, так как вам все равно нужно знать остальную часть _id.

Ответ 4

Учитывая, что MongoDB, по-видимому, не является хорошим механизмом для этого, я считаю разумным использовать mongoose, чтобы просто извлечь элемент из коллекции mongo с помощью .findOne(...), запустить поиск for-loop в соответствующих подэлементах ( поиск по словам ObjectID), изменить этот JSON, затем сделать Schema.markModified('your.subdocument'); Schema.save(); Он, вероятно, неэффективен, но он очень прост и отлично работает.

Ответ 5

Исправлено. https://jira.mongodb.org/browse/SERVER-831

Но эта функция доступна, начиная с версии разработки MongoDB 3.5.12.

Примечание. Этот вопрос задан в Aug 11 2013, и он разрешен в Aug 11 2017

Ответ 6

Okkk.we может обновить наш вложенный вложенный документ в mongodb.this - наша схема.

var Post = new mongoose.Schema({
    name:String,
    post:[{
        like:String,
        comment:[{
            date:String,
            username:String,
            detail:{
                time:String,
                day:String
            }
        }]
    }]
})

для этой схемы

  Test.update({"post._id":"58206a6aa7b5b99e32b7eb58"},
    {$set:{"post.$.comment.0.detail.time":"aajtk"}},
          function(err,data){
//data is updated
})

Ответ 7

Обмен моими уроками. Недавно я столкнулся с тем же требованием, когда мне нужно обновить вложенный элемент массива. Моя структура выглядит следующим образом

  {
    "main": {
      "id": "ID_001",
      "name": "Fred flinstone Inc"
    },
    "types": [
      {
        "typeId": "TYPE1",
        "locations": [
          {
            "name": "Sydney",
            "units": [
              {
                "unitId": "PHG_BTG1"
              }
            ]
          },
          {
            "name": "Brisbane",
            "units": [
              {
                "unitId": "PHG_KTN1"
              },
              {
                "unitId": "PHG_KTN2"
              }
            ]
          }
        ]
      }
    ]
  }

Мое требование - добавить некоторые поля в определенные единицы []. Мое решение - сначала найти индекс вложенного элемента массива (например, foundUnitIdx) Я использовал два метода:

  • используйте ключевое слово $set
  • укажите динамическое поле в $set, используя синтаксис []

                query = {
                    "locations.units.unitId": "PHG_KTN2"
                };
                var updateItem = {
                    $set: {
                        ["locations.$.units."+ foundUnitIdx]: unitItem
                    }
                };
                var result = collection.update(
                    query,
                    updateItem,
                    {
                        upsert: true
                    }
                );
    

Надеюсь, это поможет другим.:)

Ответ 8

ЛЕГКОЕ РЕШЕНИЕ ДЛЯ Mongodb 3.2 + https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

У меня была аналогичная ситуация и я решил это так. Я использовал мангуст, но он все равно должен работать в vanilla MongoDB. Надеюсь, это полезно кому-то.

const MyModel = require('./model.js')
const query = {id: 1}

// First get the doc
MyModel.findOne(query, (error, doc) => {

    // Do some mutations
    doc.foo.bar.etc = 'some new value'

    // Pass in the mutated doc and replace
    MyModel.replaceOne(query, doc, (error, newDoc) => {
         console.log('It worked!')
    })
}

В зависимости от вашего варианта использования вы можете пропустить начальную функцию findOne()