匹配多级嵌入式阵列中的项目

时间:2017-09-20 08:52:21

标签: mongodb mongodb-query aggregation-framework

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

{
    "_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
                        }
                    }
                }
            }
        }
    ])

我尝试使用一个条件,但当我使用$ anyElementTrue然后$时,MongoDB无法识别(使用未定义的变量:)$$ KEEP和$$ PRUNE($redact)映射在“名称”属性上。

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

1 个答案:

答案 0 :(得分:0)

我更感到惊讶的是,在您提到的答案中,我并非没有"强烈建议您不要嵌套数组"像这样。在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因为"内部&#34 ;对包含的数组进行$filter次操作。

因为"内部"因此,数组可以改变内容,"外部"数组需要$map才能返回"更改"元件。

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

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

  

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

     

子元素可以使用普通"Dot Notation",因为它们只是"单数"各自阵列中的条件。所以这确实减少了简洁的语法,并且仍然匹配完全相同的文档。