我在更新嵌套数组时遇到了很多问题,我创建了一个非常简单的测试,它似乎没有用,有没有人遇到过这个问题:
数据:
[ { "A": { "B": [ { "C": [ 1, 2, 3 ] }, { "C": [ 1, 2, 3 ] } ] } }, { "A": { "B": [ { "C": [ 1, 3 ] }, { "C": [ 1, 3 ] } ] } } ]
发现:
db.arrayQuery.find({"A.B.C": { $in: [1] }})
然后更新:
db.arrayQuery.update({"A.B.C": { $in: [1] }},{$pull : { "A.B.C" : 1}},{multi: true})
我得到cannot use the part (B of A.B.C) to traverse the element
我在这里读了一些问题,建议我只使用{$pull : { "C" : 1}}
,我不再收到错误,但没有任何反应。
答案 0 :(得分:1)
Mongo $elemMatch用于此案例,查询如下
db.arrayQuery.update({"A.B":{"$elemMatch":{"C":{"$in":[1]}}}},{"$pull":{"A.B.$.C":{"$in":[1]}}},false,true)
答案 1 :(得分:0)
MongoDB不支持匹配到数组的多个级别,因为位置运算符仅支持一级深度而只支持第一个匹配元素。有一个 JIRA 票证。有关其他类似问题,请参阅 Multiple use of the positional $
operator to update nested arrays 。
你有几个选择;考虑通过展平结构来修改你的模式,这样每个文档只代表一个数组级别,即:
A : {
B: {
C: [...]
}
}
另一个选项是使用 MapReduce 操作生成一个集合,该集合的文档属性具有每个对应arrayQuery
文档的数组索引位置值。 MapReduce的基本思想是它使用JavaScript作为其查询语言,但这往往比聚合框架慢得多,不应该用于实时数据分析。
在MapReduce操作中,您需要定义几个步骤,即映射步骤(将每个arrayQuery
B数组映射到集合中的每个文档,并且操作可以不执行任何操作或发出一些对象密钥和投影值)和减少步骤(采用发射值列表并将其减少为单个元素)。
对于地图步骤,理想情况下,您希望获取集合中的每个文档,每个B
数组字段的索引以及包含$pull
键的另一个键。
您的简化步骤将是一个函数(无效),简单定义为var reduce = function() {};
MapReduce操作的最后一步将创建一个名为arrayUpdates
的单独集合,其中包含已发出的操作数组对象以及具有$pull
条件的字段。在原始集合上运行MapReduce操作时,可以定期更新此集合。
总而言之,这个MapReduce方法看起来像:
var map = function(){
for(var i = 0; i < this.A.B.length; i++){
emit(
{
"_id": this._id,
"index": i
},
{
"index": i,
A: {
B: this.A.B[i]
},
"update": {
"value": "A.B." + i.toString() + ".C" // this projects the $pull query with the dynamic array element positions
}
}
);
}
};
var reduce = function(){};
db.arrayQuery.mapReduce(
map,
reduce,
{
"out": {
"replace": "arrayUpdates"
}
}
);
从MapReduce操作中查询输出集operations
通常会给你结果:
db.arrayUpdates.findOne()
<强>输出强>:
/* 1 */
{
"_id" : {
"_id" : ObjectId("5534da99180e849972938fe8"),
"index" : 0
},
"value" : {
"index" : 0,
"A" : {
"B" : {
"C" : [
1,
2,
3
]
}
},
"update" : {
"value" : "A.B.0.C"
}
}
}
然后,您可以使用db.arrayUpdates.find()
方法中的光标迭代并相应地更新您的集合:
var cur = db.arrayUpdates.find({"value.A.B.C": 1 });
// Iterate through results and update using the update query object set dynamically by using the array-index syntax.
while (cur.hasNext()) {
var doc = cur.next();
var update = { "$pull": {} };
// set the update query object
update["$pull"][doc.value.update.value] = 1;
db.arrayQuery.update({ "A.B.C": 1 }, update );
};
这会使用查询
为每个文档提取C数组中的值1db.arrayQuery.update({ "A.B.C": 1 }, update );
例如,第一个匹配文档的对象update
可以是{ $pull: {"A.B.1.C": 1} }
因此,运行上述内容后,db.arrayQuery.find()
查询的最终结果将是:
/* 0 */
{
"_id" : ObjectId("5534da99180e849972938fe8"),
"A" : {
"B" : [
{
"C" : [
2,
3
]
},
{
"C" : [
2,
3
]
}
]
}
}
/* 1 */
{
"_id" : ObjectId("5534da99180e849972938fe9"),
"A" : {
"B" : [
{
"C" : [
3
]
},
{
"C" : [
3
]
}
]
}
}