【问题标题】:Match Items In Multi-Level Embedded Arrays匹配多级嵌入式数组中的项目
【发布时间】:2017-09-20 08:52:21
【问题描述】:

我使用以下代表运动 > 类别 > 锦标赛的集合。

{
    "_id" : ObjectId("597846358bbbc4440895f2e8"),
    "Name" : [ 
        { "k" : "en-US", "v" : "Soccer" }, 
        { "k" : "fr-FR", "v" : "Football" }
    ],
    "Categories" : [ 
        {
            "Name" : [ 
                { "k" : "en-US", "v" : "France" }, 
                { "k" : "fr-FR", "v" : "France" }
            ],
            "Tournaments" : [ 
                {
                    "Name" : [ 
                        { "k" : "en-US", "v" : "Ligue 1" }, 
                        { "k" : "fr-FR", "v" : "Ligue 1" }
                    ],
                }, 
                {
                    "Name" : [ 
                        { "k" : "en-US", "v" : "Ligue 2" }, 
                        { "k" : "fr-FR", "v" : "Ligue 2" }
                    ],
                }
            ]
        }, 
        {
            "Name" : [ 
                { "k" : "en-US", "v" : "England" }, 
                { "k" : "fr-FR", "v" : "Angleterre" }
            ],
            "Tournaments" : [ 
                {
                    "Name" : [ 
                        { "k" : "en-US", "v" : "Premier League" }, 
                        { "k" : "fr-FR", "v" : "Premier League" }
                    ],
                }, 
                {
                    "Name" : [ 
                        { "k" : "en-US", "v" : "Championship" }, 
                        { "k" : "fr-FR", "v" : "Championnat" }
                    ],
                }
            ]
        }, 
    ]
}

我想使用类别名称和锦标赛名称查询集合。我已经通过以下代码成功使用了“$elemMatch”:

db.getCollection('Sport').find({
    Categories: {
        $elemMatch: {
            Name: {
                $elemMatch: { v: "France" }
            },
            Tournaments: {
                $elemMatch: {
                    Name: {
                        $elemMatch: { v: "Ligue 1" }
                    }
                }
            }
        }
    } },
    { "Categories.$": 1, Name: 1 })

但是,我不能只收到类别对象中的匹配锦标赛。 使用这个问题的答案:MongoDB Projection of Nested Arrays,我已经建立了一个聚合:

db.getCollection('Sport').aggregate([{
            "$match": {
                "Categories": {
                    "$elemMatch": {
                        "Name": {
                            "$elemMatch": {
                                "v": "France"
                            }
                        },
                        "Tournaments": {
                            "$elemMatch": {
                                "Name": {
                                    "$elemMatch": {
                                        "v": "Ligue 1"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }, {
            "$addFields": {
                "Categories": {
                    "$filter": {
                        "input": {
                            "$map": {
                                "input": "$Categories",
                                "as": "category",
                                "in": {
                                    "Tournaments": {
                                        "$filter": {
                                            "input": "$$category.Tournaments",
                                            "as": "tournament",
                                            "cond": {
                                                // stuck here
                                            }
                                        }
                                    }
                                }
                            }
                        },
                        "as": "category",
                        "cond": {
                            // stuck here
                        }
                    }
                }
            }
        }
    ])

我尝试使用条件但 MongoDB 无法识别(使用未定义变量:)当我使用 $anyElementTrue 然后 $map on “名称”属性。

我的问题:如何检查名称集合是否包含我的字符串?

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    我更惊讶的是,在您引用的答案中,我没有像这样“强烈建议您不要嵌套数组”。在 MongoDB 的下一个版本之前,以这种方式嵌套是不可能自动更新的,而且它们是出了名的难以查询。

    对于这种特殊情况,您会这样做:

    db.getCollection('Sport').aggregate([
      { "$match": {
        "Categories": {
          "$elemMatch": {
            "Name.v": "France",
            "Tournaments.Name.v": "Ligue 1"    
          }
        }    
      }},
      { "$addFields": {
        "Categories": {
          "$filter": {
            "input": {
              "$map": {
                "input": "$Categories",
                "as": "c",
                "in": {
                  "Name": {
                    "$filter": {
                      "input": "$$c.Name",
                      "as": "n",
                      "cond": { "$eq": [ "$$n.v", "France" ] }
                    }    
                  },
                  "Tournaments": {
                    "$filter": {
                      "input": {
                        "$map": {
                          "input": "$$c.Tournaments",
                          "as": "t",
                          "in": {
                            "Name": {
                              "$filter": {
                                "input": "$$t.Name",
                                "as": "n",
                                "cond": { 
                                  "$eq": [ "$$n.v", "Ligue 1" ]
                                }
                              }
                            }        
                          }
                        }
                      },
                      "as": "t",
                      "cond": {
                        "$ne": [{ "$size": "$$t.Name" }, 0]
                      }
                    }
                  }
                }
              }
            },
            "as": "c",
            "cond": {
              "$and": [
                { "$ne": [{ "$size": "$$c.Name" },0] },
                { "$ne": [{ "$size": "$$c.Tournaments" },0] }
              ]
            }
          } 
        }  
      }}
    ])
    

    返回结果:

    /* 1 */
    {
        "_id" : ObjectId("597846358bbbc4440895f2e8"),
        "Name" : [ 
            {
                "k" : "en-US",
                "v" : "Soccer"
            }, 
            {
                "k" : "fr-FR",
                "v" : "Football"
            }
        ],
        "Categories" : [ 
            {
                "Name" : [ 
                    {
                        "k" : "en-US",
                        "v" : "France"
                    }, 
                    {
                        "k" : "fr-FR",
                        "v" : "France"
                    }
                ],
                "Tournaments" : [ 
                    {
                        "Name" : [ 
                            {
                                "k" : "en-US",
                                "v" : "Ligue 1"
                            }, 
                            {
                                "k" : "fr-FR",
                                "v" : "Ligue 1"
                            }
                        ]
                    }
                ]
            }
        ]
    }
    

    重点是每个数组都需要一个$filter,并且在外部级别,您正在寻找$size 不是0,这是因为对包含数组的“内部”$filter 操作。

    由于“内部”数组的内容可能因此发生变化,“外部”数组需要 $map 才能返回“更改”的元素。

    所以就结构而言,"Categories" 需要一个 $map,因为它具有内部元素。出于同样的原因,“内部”"Tournaments" 需要$map。每个数组一直到最终属性都需要$filter,每个带有$map 的包装数组都有一个带有$size 条件的$filter

    这是一般逻辑模式,它通过为每个嵌套级别重复该模式来工作。如上所述,这非常可怕。这就是为什么你真的应该不惜一切代价避免这样的“嵌套”。增加的复杂性几乎总是超过任何感知的收益。

    我还应该注意到你对$elemMatch 有点过分了,你真的只需要在"Categories" 数组级别,因为这是唯一需要满足多个条件的元素。

    子元素可以使用普通的"Dot Notation",因为它们只是各自数组中的“奇异”条件。所以这确实在一定程度上减少了简洁的语法,并且仍然匹配完全相同的文档。

    【讨论】:

    • 非常感谢。我使用嵌套数组是因为在我的设计中,如果没有它们的父项,就永远不会查询那些嵌入的文档。我们试图避免基于每种类型一个集合的关系设计。但是,您是对的,我只尝试更新根级数组(成功),但如果不替换整个对象,似乎无法更新嵌套数组。
    猜你喜欢
    • 2022-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 2021-10-25
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多