【问题标题】:Nested MongoDB query with Golang mgo.v2使用 Golang mgo.v2 嵌套 MongoDB 查询
【发布时间】:2016-09-15 13:31:19
【问题描述】:

我正在查询一个带有嵌套项目数组的 json 结构。我想返回整个结构,但只包括匹配查询条件的嵌套项目。

所以 - 样本结构是

{
     parentId:1,
     items: [
            {
               field1: 1
               field2: 2
            },
            {
               field1: 3
               field2: 4
            }
            ]
}

我想用这样的东西查询 ->

db.getCollection('mycollection').find({"items.field1":1, "items.field2":2}) 

这可行,但它也带回了第二个子项目,我希望它实际返回的是这个 ->

{
     parentId:1,
     items: [
            {
               field1: 1
               field2: 2
            }
            ]
}

我已经能够在 MongoDB 本身中创建一个查询来实现我想要的 ->

db.getCollection('mycollection').aggregate(
  { $unwind : "$items" },
  { $match : {
     "items.field1": 1,
     "items.field2": 2,
  }}
)

但是当尝试使用 mgo.v2 进行设置时,它被证明有点麻烦。 collection.Find 方法似乎不喜欢 $unwind 命令,并且似乎无法获得正确的语法以使其与 Pipe 方法一起使用。

有人对如何创建它有任何建议吗?我可以只创建字符串并将其传递给执行吗?

【问题讨论】:

    标签: mongodb go


    【解决方案1】:

    您需要的实际管道涉及使用 $filter 运算符,该运算符根据指定条件选择要返回的数组子集。它返回一个仅包含符合条件的元素的数组。

    在您的情况下,您需要运行以下聚合操作

    db.mycollection.aggregate([
       {
            "$project": {
                "parentId": 1,          
                "items": {
                    "$filter": {
                        "input": "$items",
                        "as": "item",
                        "cond": { 
                            "$and": [
                                { "$eq": ["$$item.field1", 1] },
                                { "$eq": ["$$item.field2", 2] }
                            ]
                        }
                    }
                }
            }
        }
    ])
    

    测试


    对于不支持 $filter 运算符的 MongoDB 版本,您可以使用 set operators 的组合作为:

    db.mycollection.aggregate([
       {
            "$project": {
                "parentId": 1,          
                "items": {
                    "$setDifference": [
                        { "$map": {
                            "input": "$items",
                            "as": "item",
                            "in": {
                                "$cond": [
                                   { 
                                        "$and": [
                                            { "$eq": ["$$item.field1", 1] },
                                            { "$eq": ["$$item.field2", 2] }
                                        ]
                                    },
                                    "$$item",
                                    false
                                ]
                            }
                        }},
                        [false]
                    ]
                }
            }
        }
    ])
    

    作为最后的手段,您可以使用 $unwind 运算符,因为该运算符会为每个数组条目生成每个文档的副本,这会使用更多内存(聚合管道的内存上限可能为 10%内存),因此也需要时间来生产以及“时间”来处理。你可以这样运行:

    db.mycollection.aggregate([
        { "$unwind" : "$items" },
        { "$match" : {
            "items.field1": 1,
            "items.field2": 2,
        }},
        {
            "$group": {
                "_id": "$_id",
                "parentId": { "$first": "$parentId" },
                "items": { "$push": "$items" }
            }
        }
    ])
    

    您可以在 mgo 中以 pipeline 的形式运行:

    pipeline := []bson.M{   
        bson.M{ "$unwind": "$items" },
        bson.M{
            "$match": bson.M{ 
                "items.field1": 1,
                "items.field2": 2
            }
        },
        bson.M{
            "$group": bson.M{
                "_id": "$_id",
                "parentId": bson.M{ "$first": "$parentId" },
                "items": bson.M{ "$push": "$items" }
            }
        }   
    }
    pipe := mycollection.Pipe(pipeline)
    iter := pipe.Iter()
    

    在 Robomongo 中测试

    【讨论】:

    • 感谢您的反馈,但这不起作用,我收到以下错误 -> 不能在数组或切片文字中使用 bson.M 文字(类型 bson.M)作为 bson.D 类型
    • 我将 bson.D 更改为 bson.M 编译,但结果不正确。它根本没有嵌套的子项。
    • 我也试过这个 - pipeline := []bson.M{ bson.M{"$unwind": "$items"}, bson.M{"$match": bson. M{ "items.field1": bson.M{"$eq": 1}, "items.field2": bson.M{"$eq": 2}, }, }, }
    • 完成,所缺少的只是所需的输出,示例文档已经存在
    • @P456678 我已经更新了我的答案以包含不使用 $unwind 运算符的优化解决方案。
    【解决方案2】:

    参考chridam's answer,相信$filter的可以简化一点:

    c := session.DB("test").C("mycollection")
    
    pipe := c.Pipe([]bson.M{{
        "$project": bson.M{
            "parentId": 1,
            "items": bson.M{
                "$filter": bson.M{
                    "input": "$items",
                    "as":    "item",
                    "cond": bson.M{
                        "$and": []bson.M{
                            {"$eq": []interface{}{"$$item.field1", 1}},
                            {"$eq": []interface{}{"$$item.field2", 2}},
                        },
                    },
                },
            },
        },
    }})
    
    resp := []bson.M{}
    err = pipe.All(&resp)
    
    if err != nil {
        panic(err)
    }
    
    fmt.Println(resp)
    

    另一个$unwind

    c := session.DB("test").C("mycollection")
    
    pipe := c.Pipe([]bson.M{
        {"$unwind": "$items"},
        {"$match": bson.M{
            "items.field1": 1,
            "items.field2": 2,
        }},
        {"$group": bson.M{
            "_id":      "$_id",
            "parentId": bson.M{"$first": "$parentId"},
            "items":    bson.M{"$push": "$items"},
        }},
    })
    
    resp := []bson.M{}
    err = pipe.All(&resp)
    
    if err != nil {
        panic(err)
    }
    
    fmt.Println(resp) 
    

    都编译没有错误并返回

    [map[
        _id:ObjectIdHex(".....") 
        parentId:1 
        items:[map[
            field2:2 
            field1:1
        ]]
    ]]
    

    去 1.6,mongo 3.2

    【讨论】:

      猜你喜欢
      • 2021-06-20
      • 2015-05-20
      • 1970-01-01
      • 1970-01-01
      • 2015-02-10
      • 2012-10-17
      • 2016-05-07
      • 2018-06-22
      • 1970-01-01
      相关资源
      最近更新 更多