raw*_*wat 3 mongodb mongodb-query aggregation-framework
我有一个如下的集合
{
"state" : "VIC",
"sites" :
[
{
"name" : "VIC01",
"pes" :
[
{
"id" : "1",
"longname" : "rdnvej300exh0443",
"shortname" : "RVE4-E-043",
"location" : "Exhibition"
},
{
"id" : "2",
"longname" : "rdnvej160pee0343",
"shortname" : "RV3W-E-043",
"location" : "Windsor"
},
{
"id" : "3",
"location" : "my home"
}
],
"partners" :
[
{
"id" : "REACH",
"fnns" : ["N54321R","N24686R","N46818R","N10461R"]
},
{
"id" : "NCS_CORE",
"fnns" : [ "N54320R","N71311R","N35797R","N57919R"]
}
]
},
{
"name" : "CLAYTON",
"pes" :
[
{
"id" : "1",
"longname" : "rdnvej1822da0o43",
"shortname" : "RVCZ-E-043",
"location" : "Clayton"
},
{
"id" : "2",
"longname" : "rdnvej1822da0o44",
"shortname" : "RVCZ-E-044",
"location" : "Clayton"
}
],
"partners" :
[
{
"id" : "NCS_CORE",
"fnns" : ["N54331R","N24686R","N46818R","N10461R"]
},
{
"id" : "NCS_CLAYTON_OPS2",
"fnns" : [ "N54321R","N71311R","N35797R","N57919R"]
}
]
}
]
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试检索
{州、sites.name、sites.partners.id}
来自“fnns”列表中具有“N54321R”的所有文档。
我尝试了下面的查询,但对于上面的查询,它给出了所有sites.partners.id。
1. db.topology.find( {"sites.partners.fnns" : { "$elemMatch" : { "$eq" : "N54321R" } } }, {"state" : 1 , "sites.name" : 1, "sites.partners.id" : 1 , "sites.partners.fnns" : 1} )
2. db.topology.find( {"sites.partners.fnns" : { $in : ["N54321R"] } }, {"state" : 1 , "sites.name" : 1, "sites.partners.id" : 1 , "sites.partners.fnns" : 1} )
3. db.topology.find( {"sites.partners.fnns" : "N54321R"} , {"state" : 1 , "sites.name" : 1, "sites.partners.id" : 1 , "sites.partners.fnns" : 1} )
Run Code Online (Sandbox Code Playgroud)
正确的输出应该是(仅显示sites.partners.id)
{ REACH , NCS_CLAYTON_OPS2 }
Run Code Online (Sandbox Code Playgroud)
但它给出了所有sites.partners.id,即
{ REACH , NCS_CORE , NCS_CORE , NCS_CLAYTON_OPS2 }
Run Code Online (Sandbox Code Playgroud)
我应该在查询中修改哪些内容才能获得结果?
如果需要更改架构,请告诉我正确的架构应该是什么?
谢谢。
通过该方法提供的基本“投影”.find()无法按照您要求的方式过滤数组中的内容。这里不处理外层多个元素的位置匹配或指定子文档的特定元素。$
相反,您需要.aggregate()过滤掉内容,因此为了仅返回那些具有匹配值的“合作伙伴”元素,目前最有效的方法是:
db.collection.aggregate([
{ "$match": { "sites.partners.fddn": "N54321R" } },
{ "$project": {
"state": 1,
"sites": {
"$setDifference": [
{ "$map": {
"input": "$sites",
"as": "site",
"in": {
"$let": {
"vars": {
"partners": {
"$setDifference": [
{ "$map": {
"input": "$$site.partners",
"as": "partner",
"in": {
"$cond": [
{ "$setIsSubset": [ ["N54321R"], "$$partner.fnns" ] },
"$$partner",
false
]
}
}},
[false]
]
}
},
"in": {
"$cond": [
{ "$eq": [{ "$size": "$$partners" }, 0] },
false,
{
"name": "$$site.name",
"pes": "$$site.pes",
"partners": "$$partners"
}
]
}
}
}
}},
[false]
]
}
}}
])
Run Code Online (Sandbox Code Playgroud)
这将返回过滤后的结果,例如:
{
"_id" : ObjectId("564433c2f7a4adee6c13205b"),
"state" : "VIC",
"sites" : [
{
"name" : "VIC01",
"pes" : [
{
"id" : "1",
"longname" : "rdnvej300exh0443",
"shortname" : "RVE4-E-043",
"location" : "Exhibition"
},
{
"id" : "2",
"longname" : "rdnvej160pee0343",
"shortname" : "RV3W-E-043",
"location" : "Windsor"
},
{
"id" : "3",
"location" : "my home"
}
],
"partners" : [
{
"id" : "REACH",
"fnns" : [
"N54321R",
"N24686R",
"N46818R",
"N10461R"
]
}
]
},
{
"name" : "CLAYTON",
"pes" : [
{
"id" : "1",
"longname" : "rdnvej1822da0o43",
"shortname" : "RVCZ-E-043",
"location" : "Clayton"
},
{
"id" : "2",
"longname" : "rdnvej1822da0o44",
"shortname" : "RVCZ-E-044",
"location" : "Clayton"
}
],
"partners" : [
{
"id" : "NCS_CLAYTON_OPS2",
"fnns" : [
"N54321R",
"N71311R",
"N35797R",
"N57919R"
]
}
]
}
]
}
Run Code Online (Sandbox Code Playgroud)
这里发生了一些事情,特别是如果您不熟悉聚合框架,因此最好逐步完成它们。
首先是$match管道阶段,它本质上是一个“查询”,就像您作为第一个参数发出的一样.find()。那里使用的路径将选择实际具有匹配元素的文档:
{ "$match": { "sites.partners.fddn": "N54321R" } },
Run Code Online (Sandbox Code Playgroud)
sites.partners.fddn与您正在寻找的值正确匹配的“路径”也是如此。“点符号”是现阶段的正确形式。它匹配一个文档,但查看位置$运算符,数组匹配实际上仅算作 的元素sites,并且仅算作发生匹配的第一个元素。这就是为什么这个“投影”对您的目的没有用处,但您仍然应该$match缩小您真正想要的文档的范围。
然后是$project,它是标准投影可以完成的功能的扩展形式,并且实际上允许完成无法完成的文档操作.find()。
其中有复合数组,这些是您需要检查和过滤的内容。用于此目的的工具分别是$map和$setDifference。
本质上,$map查看数组的每个元素并允许针对每个元素处理条件。在这种情况下,您想要的操作是$cond三元运算符。它根据第一个参数中的条件返回一个值,即第二个参数 wheretrue或第三个参数 where false。
如果你首先看一下它的最里面的部分:
{ "$map": {
"input": "$$site.partners",
"as": "partner",
"in": {
"$cond": [
{ "$setIsSubset": [ ["N54321R"], "$$partner.fnns" ] },
"$$partner",
false
]
}
}},
Run Code Online (Sandbox Code Playgroud)
这是查看内部“partners”数组的每个元素,并对其“ffns”数组进行另一个比较。稍微加快速度的是$setIsSubset,它比较您的值(指定为数组/集本身)以查看它是否实际上是正在测试的数组/集的“子集”(包含的成员)。这是true/false也输入到 a 的另一个逻辑结果$cond,它要么返回元素,要么false返回不满足条件的位置。
在不同地方显示$setDifference的总是做相同的工作,这是因为$map只是返回false每个元素的结果的想要的值,这种比较会删除所有false元素,只留下具有匹配值的内容。
最后,它$let允许声明变量以避免重复声明逻辑。尽管它不适用于本示例中的数据,但“合作伙伴”的过滤结果实际上可能是一个空数组。这在结果中通常是不期望的。
因此,在$map处理“sites”数组时,我们首先计算出“partners”的每个过滤结果,然后测试该结果以查看它是否确实具有一定的长度。如果不存在,则false返回类似的内容,否则返回元素的内容,并“使用”过滤后的“partners”版本代替每个成员的原始数组。$false然后应用相同的过滤。
可能有点难以理解,但值得学习和理解。也可以使用带有 的阶段进行处理$unwind,但应避免,因为这会对性能产生重大影响。然而,此处所示的聚合管道具有与任何标准查询基本相同的性能特征,并且几乎没有额外的开销。
| 归档时间: |
|
| 查看次数: |
5676 次 |
| 最近记录: |