donglu953744 2016-09-15 13:31
浏览 232

使用Golang mgo.v2的嵌套MongoDB查询

I'm querying a json structure with a nested array of items. I'd like to return the entire structure but only include the items of the nest which match the query criteria.

So - sample structure is

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

I would like to query with something like this ->

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

this works but it also brings back the second child item, what I want it to actually return is this ->

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

I have been able to create a query in MongoDB itself which achieves what I want ->

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

but when trying to set this up using mgo.v2 it's proving a bit of a mare. The collection.Find method doesn't seem to like the $unwind command and can't seem to get the right syntax to get it working with the Pipe method.

Does anyone have any suggestions how this can be created? Can I just create the string and pass it for execution?

  • 写回答

2条回答 默认 最新

  • duannian7116 2016-09-15 13:40
    关注

    The actual pipeline you need involves the use of the $filter operator, which selects a subset of the array to return based on the specified condition. It returns an array with only those elements that match the condition.

    In your case, you need to run the following aggregation operation

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

    Testing enter image description here


    For MongoDB versions which do not support the $filter operator, you can use a combination of set operators as:

    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]
                    ]
                }
            }
        }
    ])
    

    As a last resort, you can use the $unwind operator as the operator produces a copy of each document per array entry which uses more memory (possible memory cap on aggregation pipelines of 10% total memory) and thus also takes time to produce as well as "time" to process. You can run as:

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

    which you can run in mgo as a 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()
    

    Testing in Robomongo

    enter image description here

    评论

报告相同问题?

悬赏问题

  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计
  • ¥15 路易威登官网 里边的参数逆向
  • ¥15 Arduino无法同时连接多个hx711模块,如何解决?
  • ¥50 需求一个up主付费课程
  • ¥20 模型在y分布之外的数据上预测能力不好如何解决
  • ¥15 processing提取音乐节奏