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.
Ответы
Ответ 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.
Ответ 3
Это очень OLD-ошибка в MongoDB
https://jira.mongodb.org/browse/SERVER-831
Ответ 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()