MongoDB findOne() 和 updateOne() 与嵌入式文档



假设我得到了以下文档,其中包含数组中的嵌入式文档:

{
"name" : "test",
"children" : [
{
"name" : "child1",
"children" : [
{
"name" : "sub1",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"},
{"name" : "c5"}
]
}
]
}, 
{
"name" : "child2",
"children" : [
{
"name" : "sub2",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
}, 
{
"name" : "sub3",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
}, 
{
"name" : "sub4",
"children" : [
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
}
]
}
]
}

我们可以看到,test有两个孩子:child1child2,而一个孩子有自己的孩子,如sub1sub2sub3sub4等。 目前,我必须更新一些儿童内容,但我遇到了以下两个问题:

第一期是关于findOne(),当时我不小心弄错了字(名字child1应该child2):

db.collection_name.findOne({
'name': 'test',
'children.name': 'child1',  // -> should be `child2`
'children.children.name': 'sub2'
})

我仍然可以正确获得文档"测试",但是由于我打错了字并且sub2实际上处于child2之下,而不是child1,那么我怎么还能得到这个正确的findOne()结果呢?

第二个问题是关于updateOne()的,当我尝试更新child1的数组children时,效果很好(代码也像官方文档中的示例):

db.collection_name.updateOne({
'name': 'test',
'children.name': 'child1'  // locate child1
}, {
'$set':{'children.$.children':[some other content]}
})

但是当我也尝试更新sub3的数组(在child2下)时,同样的方式失败并出现错误cannot use the part (children of children.children.1.children) to traverse the element,这是有问题的代码:

db.collection_name.updateOne({
'name': 'test',
'children.name': 'child2',
'children.children.name': 'sub3'  // locate sub3
}, {
'$set':{'children.children.$.children':[some other content]}
})

如果有人可以帮助解决这两个问题,我将非常感激,提前感谢!

对于您的第一个问题,您可以使用$elemMatch来查找确切的元素。

db.collection_name.findOne({
'name':'test',
children:{
$elemMatch:{
'name':'child2',
'children.name':'sub2'
}
}
})

我猜 mongo 不支持多个嵌套数组更新,但如果你知道子阵列的位置,你可以使用这种方法。

db.collection_name.updateOne({
'name':'test',
children:{
$elemMatch:{
'name':'child2',
'children.name':'sub3'
}
}
}
, {
$set: {"children.$.children.1.children" : 
[
{"name" : "c1"},
{"name" : "c2"},
{"name" : "c3"},
{"name" : "c4"}
]
}
}
)

第一个问题:查询数组时查询返回结果,因为匹配是针对数组的所有元素进行评估的,即第一个匹配'children.name': 'child1'匹配第一个元素,第二个'children.children.name': 'sub2'匹配第二个元素。

使用 $elemMatch(query) 将两个条件与同一数组元素匹配。

db.collection_name.findOne({
'name': 'test',
'children':{$elemMatch:{'name': 'child2', 'children.name': 'sub2'}}
})

比较上述两种方法 1 和 2

第二个问题:Mongodb 3.6 现在支持通过 arrayFilters 表达式进行多个位置更新。

下面的查询将替换 sub3 子级的所有子级。

db.collection_name.updateOne(
{'name': 'test'}, 
{'$set':{'children.$[first].children.$[second].children':[some data]}},
{'arrayFilters': [{ 'first.name': 'child2', 'second.name': 'sub3'}]}
)

下面的查询在 sub3 子级的子级中推送新子级。

db.collection_name.updateOne(
{'name': 'test'}, 
{'$push':{'children.$[first].children.$[second].children':new child data}},
{'arrayFilters': [{ 'first.name': 'child2', 'second.name': 'sub3'}]}
)

最新更新