MongoDB 在字典列表中查找查询

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)

我应该在查询中修改哪些内容才能获得结果?

如果需要更改架构,请告诉我正确的架构应该是什么?

谢谢。

Bla*_*ven 6

通过该方法提供的基本“投影”.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,但应避免,因为这会对性能产生重大影响。然而,此处所示的聚合管道具有与任何标准查询基本相同的性能特征,并且几乎没有额外的开销。