我有一个深层嵌套的文档结构,如下所示:
{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中执行此操作的正确方法是什么?这是mongo v2.4.1。
答案 0 :(得分:41)
不幸的是,每个键不能多次使用$
运算符,因此必须使用数值。如:
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 }}
)
答案 1 :(得分:18)
使用mongoose管理解决它:
所有您需要知道的是链中所有子文档的“_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方法in mongoose documentation的更多信息。
解释:_id用于SchemaName,_id1用于sub1,_id2用于sub2 - 你可以保持链接。
*您不必使用findById方法,但在我看来这是最方便的,因为您无论如何都需要了解其余的方法。
答案 2 :(得分:8)
MongoDB在版本3.5.2及更高版本中引入了 ArrayFilters 来解决此问题。
版本3.6中的新功能。
从MongoDB 3.6开始,在更新数组字段时,您可以指定 arrayFilters确定要更新的数组元素。
让我们将Schema设计说如下:
var ProfileSchema = new Schema({
name: String,
albums: [{
tour_name: String,
images: [{
title: String,
image: String
}]
}]
});
创建的文档如下所示:
{
"_id": "1",
"albums": [{
"images": [
{
"title": "t1",
"url": "url1"
},
{
"title": "t2",
"url": "url2"
}
],
"tour_name": "london-trip"
},
{
"images": [.........]:
}]
}
说我想更新" url"一个图像。
给定 - "document id", "tour_name" and "title"
为此更新查询:
Profiles.update({_id : req.body.id},
{
$set: {
'albums.$[i].images.$[j].title': req.body.new_name
}
},
{
arrayFilters: [
{
"i.tour_name": req.body.tour_name, "j.image": req.body.new_name // tour_name - current tour name, new_name - new tour name
}]
})
.then(function (resp) {
console.log(resp)
res.json({status: 'success', resp});
}).catch(function (err) {
console.log(err);
res.status(500).json('Failed');
})
答案 3 :(得分:5)
这是MongoDB中的一个非常老的错误
答案 4 :(得分:3)
鉴于MongoDB似乎没有为此提供良好的机制,我发现谨慎使用mongoose简单地使用.findOne(...)
从mongo集合中提取元素,对其相关运行for循环搜索subelements(通过说ObjectID寻找),修改那个JSON,然后做Schema.markModified('your.subdocument'); Schema.save();
它可能效率不高,但它非常简单并且工作正常。
答案 5 :(得分:2)
这是固定的。 https://jira.mongodb.org/browse/SERVER-831
但是从MongoDB 3.5.12开发版本开始可以使用此功能。
注意:此问题在Aug 11 2013
上提出,并在Aug 11 2017
答案 6 :(得分:1)
分享我的经验教训。我最近遇到了相同的要求,我需要更新嵌套数组项。 我的结构如下
{
"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中的动态字段 query = {
"locations.units.unitId": "PHG_KTN2"
};
var updateItem = {
$set: {
["locations.$.units."+ foundUnitIdx]: unitItem
}
};
var result = collection.update(
query,
updateItem,
{
upsert: true
}
);
希望这有助于他人。 :)
答案 7 :(得分:1)
我今天遇到了同样的问题,在google / stackoverflow / github上进行了很多探索之后,我发现arrayFilters
是解决此问题的最佳方法。它将与mongo 3.6及更高版本一起使用。
该链接终于挽救了我的生活:https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html
const OrganizationInformationSchema = mongoose.Schema({
user: {
_id: String,
name: String
},
organizations: [{
name: {
type: String,
unique: true,
sparse: true
},
rosters: [{
name: {
type: String
},
designation: {
type: String
}
}]
}]
}, {
timestamps: true
});
在快递中使用猫鼬,更新给定ID的花名册名称。
const mongoose = require('mongoose');
const ControllerModel = require('../models/organizations.model.js');
module.exports = {
// Find one record from database and update.
findOneRosterAndUpdate: (req, res, next) => {
ControllerModel.updateOne({}, {
$set: {
"organizations.$[].rosters.$[i].name": req.body.name
}
}, {
arrayFilters: [
{ "i._id": mongoose.Types.ObjectId(req.params.id) }
]
}).then(response => {
res.send(response);
}).catch(err => {
res.status(500).send({
message: "Failed! record cannot be updated.",
err
});
});
}
}
答案 8 :(得分:0)
我搜索了大约5个小时,终于找到了最佳,最简单的解决方案: HOW TO UPDATE NESTED SUB-DOCUMENTS IN MONGO DB
{id: 1,
forecasts: [ {
forecast_id: 123,
name: "Forecast 1",
levels: [
{
levelid:1221
levelname: "proven",
configs: [
{
config: "Custom 1",
variables: [{ x: 1, y:2, z:3}]
},
{
config: "Custom 2",
variables: [{ x: 10, y:20, z:30}]
},
]
},
{
levelid:1221
levelname: "likely",
configs: [
{
config: "Custom 1",
variables: [{ x: 1, y:2, z:3}]
},
{
config: "Custom 2",
variables: [{ x: 10, y:20, z:30}]
},
]
}
]
},
]}
查询:
db.weather.updateOne({
"_id": ObjectId("1"), //this is level O select
"forecasts": {
"$elemMatch": {
"forecast_id": ObjectId("123"), //this is level one select
"levels.levelid": ObjectId("1221") // this is level to select
}
}
},
{
"$set": {
"forecasts.$[outer].levels.$[inner].levelname": "New proven",
}
},
{
"arrayFilters": [
{ "outer.forecast_id": ObjectId("123") },
{ "inner.levelid": ObjectId("1221") }
]
}).then((result) => {
resolve(result);
}, (err) => {
reject(err);
});
答案 9 :(得分:-1)
Okkk.we可以在mongodb中更新我们的嵌套子文档。这是我们的架构。
Test.update({"post._id":"58206a6aa7b5b99e32b7eb58"},
{$set:{"post.$.comment.0.detail.time":"aajtk"}},
function(err,data){
//data is updated
})
此架构的解决方案
psmt1 = con.prepareStatement("insert into medication(tid,titname,minvalue,maxvalue,disc) values(AES_ENCRYPT(?, 'key'),AES_ENCRYPT(?, 'key'),AES_ENCRYPT(?, 'key'),AES_ENCRYPT(?, 'key'),AES_ENCRYPT(?, 'key'))");
byte[] keyBytes = "key".getBytes();
psmt1.setString(1, title[0]);
psmt1.setBytes(2, keyBytes);
psmt1.setString(3, title[1]);
psmt1.setBytes(4, keyBytes);
psmt1.setString(5, min);
psmt1.setBytes(6, keyBytes);
psmt1.setString(7, max);
psmt1.setBytes(8, keyBytes);
psmt1.setString(9, disc);
psmt1.setBytes(10, keyBytes);
psmt1.executeUpdate();
答案 10 :(得分:-1)
Mongodb 3.2 + 的简便解决方案 https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/
我有类似的情况并像这样解决了。我使用的是猫鼬,但它应该仍然适用于香草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()