MongoDB 不支持匹配多个级别的数组,因为位置运算符只支持一个级别的深度并且只支持第一个匹配元素。为此有一张 JIRA 票。有关其他类似问题,请参阅 Multiple use of the positional $ operator to update nested arrays。
这里有几个选项;考虑通过展平结构来修改您的架构,以便每个文档仅代表一个数组级别,即:
A : {
B: {
C: [...]
}
}
另一个选项是使用 MapReduce 操作生成一个集合,该集合包含的文档具有每个对应的 arrayQuery 文档的数组索引位置值的属性。 MapReduce 的基本思想是它使用 JavaScript 作为其查询语言,但这往往比聚合框架慢得多,不应用于实时数据分析。
在您的 MapReduce 操作中,您需要定义几个步骤,即映射步骤(将每个 arrayQuery B 数组映射到集合中的每个文档,并且该操作可以不做任何事情或发出带有键和投影值)和归约步骤(获取发射值列表并将其归约为单个元素)。
对于映射步骤,理想情况下,您希望获取集合中每个文档、每个 B 数组字段的索引和另一个包含 $pull 键的键。
您的 reduce 步骤将是一个简单定义为 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 数组中的值 1
db.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
]
}
]
}
}