过滤$查找结果

时间:2016-06-15 11:12:54

标签: mongodb mongodb-query aggregation-framework

我有2个集合(带有示例文档):

报告

{
    id: "R1",
    type: "xyz",
}

reportfiles

{
    id: "F1",
    reportid: "R1",
    time: ISODate("2016-06-13T14:20:25.812Z")
},
{
    id: "F14",
    reportid: "R1",
    time: ISODate("2016-06-15T09:20:29.809Z")
}

正如您所看到的,report可能有多个reportfiles

我想执行查询,匹配报告id,按原样返回报告文档,另外还有一个存储为reportfile子文档的附加密钥,其中包含最新time (没有reportid会更好,因为它会多余),例如

{
    id: "R1",
    type: "xyz",
    reportfile: {
        id: "F14",
        reportid: "R1",
        time: ISODate("2016-06-15T09:20:29.809Z")
    }
}

我的问题是每个报告类型都有自己的一组属性,因此在聚合管道中使用$project不是最好的方法。

到目前为止,我得到了

db.reports.aggregate([{
            $match : 'R1'
        }, {
            $lookup : {
                from : 'reportfiles',
                localField : 'id',
                foreignField : 'reportid',
                as : 'reportfile'
            }
        }
    ])

当然以“报告文件”的形式返回给定reportid的所有文件的列表。如何有效地过滤该列表以获得我需要的唯一元素?

有效 - >我尝试使用$unwind作为下一个管道步骤但是生成的文档非常可怕而且毫无意义。

提前感谢任何建议!

2 个答案:

答案 0 :(得分:1)

您需要在$project阶段后向汇总管道中添加另一个$lookup阶段。

{ "$project": { 
    "id": "R1",
    "type": "xyz",
    "reportfile": {
        "$let": {
            "vars": { 
                "obj": {   
                    "$arrayElemAt": [
                        { "$filter": { 
                            "input": "$reportfile", 
                            "as": "report", 
                            "cond": { "$eq": [ "$$report.time", { "$max": "$reportfile.time" } ] }
                        }},
                        0
                    ]
                }
            },
            "in": { "id": "$$obj.id", "time": "$$obj.time" }
        }
    }
}}

$filter运算符“过滤”$lookup结果并返回一个包含满足条件的文档的数组。这里的条件是$eq,当文档具有$max imum值时,它返回true。

$arrayElemAt运算符切片 $ filter 的结果,并从数组中返回元素,然后使用{{3}将其分配给变量运算符。在那里,您可以使用$let轻松访问结果中所需的字段。

答案 1 :(得分:0)

您需要的是在reportfile集合上运行聚合操作,在reports集合上执行“加入”,管道 $group $sort 订购的操作(使用 $unwind )和展平的文档( $lookup )管道。然后可以使用reportid对上述结果进行分组,并使用 $first 累加器aoperators输出所需的结果。

以下演示了这种方法:

db.reportfiles.aggregate([
    { "$match": { "reportid": "R1" } },
    {
        "$lookup": {
            "from": 'reports',
            "localField" : 'reportid',
            "foreignField" : 'id',
            "as": 'report'
        }
    },
    { "$unwind": "$report" },
    { "$sort": { "time": -1 } },
    {
        "$group": {
            "_id": "$reportid",
            "type": { "$first": "$report.type" },
            "reportfile": {
                "$first": {
                    "id": "$id",
                    "reportid": "$reportid",
                    "time": "$time"
                }
            }
        }
    }
])

示例输出:

{
    "_id" : "R1",
    "type" : "xyz",
    "reportfile" : {
        "id" : "F14",
        "reportid" : "R1",
        "time" : ISODate("2016-06-15T09:20:29.809Z")
    }
}