【问题标题】:Get objects from an array along with other fields in same level, based on some condition, using mongodb aggregate根据某些条件,使用 mongodb 聚合从数组中获取对象以及同一级别的其他字段
【发布时间】:2016-05-20 07:01:50
【问题描述】:

我有以下数据。我想根据某些条件从 od 数组中获取对象。除此之外,我还想获得 em 和 name 字段。

我对@9​​87654321@不是很熟悉。所以我需要帮助来解决我的问题。

{
_id : 1,
em : 'abc@12s.net',
name : 'NewName',
od : 
[
    {
        "oid" : ObjectId("1234"),
        "ca" : ISODate("2016-05-05T13:20:10.718Z")
    },
    {
        "oid" : ObjectId("2345"),
        "ca" : ISODate("2016-05-11T13:20:10.718Z")
    },
    {
        "oid" : ObjectId("57766"),
        "ca" : ISODate("2016-05-13T13:20:10.718Z")
    }
]       
},
{
_id : 2,
em : 'ab6c@xyz.net',
name : 'NewName2',
od : 
[
    {
        "oid" : ObjectId("1234"),
        "ca" : ISODate("2016-05-11T13:20:10.718Z")
    },
    {
        "oid" : ObjectId("2345"),
        "ca" : ISODate("2016-05-12T13:20:10.718Z")
    },
    {
        "oid" : ObjectId("57766"),
        "ca" : ISODate("2016-05-05T13:20:10.718Z")
    }
]       
}

我尝试使用 $match、$project 和 $unwind of aggregate 来获得所需的结果。我的查询如下:-

db.collection.aggregate([
{
    $match : {
                    "od.ca" : {
                        '$gte': '10/05/2016',
                        '$lte': '15/05/2016'
                    }
                }
},
{
    $project:{
                    _id: '$_id',
                    em: 1,
                    name : 1,
                    od : 1
                }
},
{
    $unwind : "$od"
}, 
{
    $match : {
                    "od.ca" : {
                        '$gte': '10/05/2016',
                        '$lte': '15/05/2016'
                    }
                }
}])

我得到的结果是 em 和 name 和 od 数组,其中一个来自 od 的对象,即同一个电子邮件 ID 有多个记录。

 {
_id : 1,
em : 'abc@12s.net',
name : 'NewName',
od : 
[
{
    "oid" : ObjectId("57766"),
    "ca" : ISODate("2016-05-13T13:20:10.718Z")
}
]       
}
{
_id : 1,
em : 'abc@12s.net',
name : 'NewName',
od : 
[
{
    "oid" : ObjectId("2345"),
    "ca" : ISODate("2016-05-11T13:20:10.718Z")
}
]       
}

但我想要的是返回结果将是每个电子邮件 ID,在 od 数组中匹配条件的所有对象。我想要的一个样本输出是:-

 {
_id : 1,
em : 'abc@12s.net',
name : 'NewName',
od : 
[
    {
        "oid" : ObjectId("2345"),
        "ca" : ISODate("2016-05-11T13:20:10.718Z")
    },
    {
        "oid" : ObjectId("57766"),
        "ca" : ISODate("2016-05-13T13:20:10.718Z")
    }
]       
}

我在查询中做错了什么?如果查询假设返回这样,我怎样才能得到我想要的结果?有人可以告诉我应该尝试什么或查询中的哪些更改可以帮助我获得想要的结果吗?

【问题讨论】:

    标签: javascript mongodb mongodb-query aggregation-framework


    【解决方案1】:

    您不一定需要一组这些聚合运算符,除非您的 MongoDB 版本早于 2.6.X 版本。 $filter 运算符可以很好地完成这项工作。

    考虑以下示例,其中 $filter 运算符在 $project 管道阶段应用时将过滤 od 数组以仅包含具有ca日期大于等于'2016-05-10'且小于等于'2016-05-15'

    var start = new Date('2016-05-10'),
        end = new Date('2016-05-15');
    
    db.collection.aggregate([
        { 
            "$match": {
                "od.ca": { "$gte": start, "$lte": end }
            }
        },
        {
            "$project": {
                "em": 1,
                "name": 1,
                "od": {
                    "$filter": {
                        "input": "$od",
                        "as": "o",
                        "cond": {  
                            "$and": [
                                { "$gte": [ "$$o.ca", start ] },
                                { "$lte": [ "$$o.ca", end ] }
                            ]
                        }
                    }
                }
            }
        }
    ])
    

    请记住,此运算符仅适用于 MongoDB 版本 3.2.X 及更高版本。


    否则,对于 2.6.X3.0.X 的版本,您可以结合使用 $map$setDifference 运算符来“过滤” ca 数组中的文档。

    $map 运算符基本上将一些由 $cond 运算符评估的值映射到一组错误值或通过给定条件的文档。 $setDifference 运算符然后返回集合与先前计算的差异。看看前面的例子是如何实现的:

    var start = new Date('2016-05-10'),
        end = new Date('2016-05-15');
    
    db.collection.aggregate([
        { 
            "$match": {
                "od.ca": { "$gte": start, "$lte": end }
            }
        },
        {
            "$project": {
                "em": 1,
                "name": 1,
                "od": {
                    "$setDifference": [
                        {
                            "$map": {
                                "input": "$od",
                                "as": "o",
                                "in": {
                                    "$cond": [
                                        {  
                                            "$and": [
                                                { "$gte": [ "$$o.ca", start ] },
                                                { "$lte": [ "$$o.ca", end ] }
                                            ]
                                        },
                                        "$$o",
                                        false
                                    ]
                                }
                            }
                        },
                        [false]
                    ]
                }
            }
        }
    ])
    

    对于 2.4.X 及更早版本,您可能必须使用 $match$unwind$group 的组合> 运算符在上述运算符不存在的情况下实现相同。

    前面的示例演示了这一点,这就是您所尝试的,但只是缺少一个 $group 管道步骤来将所有展平文档分组到原始文档架构中,尽管减去了过滤后的数组元素:

    db.collection.aggregate([
        { 
            "$match": {
                "od.ca": { "$gte": start, "$lte": end }
            }
        },  
        { "$unwind": "$od" },
        { 
            "$match": {
                "od.ca": { "$gte": start, "$lte": end }
            }
        },
        {
            "$group": {
                "$_id": "$_id",
                "em": { "$first": "$em" },
                "name": { "$first": "$name" },
                "od": { "$push": "$od" }
            }
        }
    ])
    

    【讨论】:

    • 我使用的是 3.0.0 的 mongodb。我最初考虑使用过滤器,但因为它可以与 3.0.0 一起使用。将尝试您为 2.5.X 到 3.0.X 提供的解决方案。
    • 这给了我想要的结果,我想要的方式。 @chridam
    • 我的错,应该是 2.6.X 而不是 2.5.X 如最初所述。此外,$filter3.2 版本中的新内容,因此它不适用于 3.0,作为更正点。
    猜你喜欢
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-16
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多