Mongo-Aggregate:如何与另一个集合中的字段进行比较

Mongo Aggregate: how to compare with a field from another collection?

本文关键字:字段 比较 集合 另一个 Mongo-Aggregate      更新时间:2023-09-26

我正在尝试实现一个从文章集合中收集未读消息的函数。收藏中的每一篇文章都有一个"讨论"条目,其中包含讨论评论子文档。这种子文档的一个例子是:

{
    "id": NumberLong(7534),
    "user": DBRef("users", ObjectId("...")),
    "dt_create": ISODate("2015-01-26T00:10:44Z"),
    "content": "The discussion comment content"
}

母文档具有以下(部分)结构:

{
    model: {
        id: 17676,
        title: "Article title",
        author: DBRef("users", ObjectId(...)),
        // a bunch of other fields here
    },
    statistics: {
        // Statistics will be stored here (pageviews, etc)
    },
    discussions: [
        // Array of discussion subdocuments, like the one above
    ]
}

每个用户还有一个last_viewed条目,它是一个文档,示例如下:

{
    "17676" : "2015-01-10T00:00:00.000Z",
    "18038" : "2015-01-10T00:00:00.000Z",
    "18242" : "2015-01-20T00:00:00.000Z",
    "18325" : "2015-01-20T00:00:00.000Z"
}

这意味着用户最后一次查看ID为17676和18038的文章的讨论评论是在2015年1月10日,而ID为18242和18325的文章的评论是在15年1月20日。

因此,我想从文章文档中收集讨论条目,对于ID为17676的文章,我想收集2015-01-10之后创建的讨论条目,而对于ID为18242的文章,我们想显示2015-01-20之后创建的论述条目。

更新

根据Neil Lunn的回复,到目前为止我创建的功能是:

function getUnreadDiscussions(userid) {
    user = db.users.findOne({ 'model.id': userid });
    last_viewed = [];
    for(var i in user.last_viewed) {
        last_viewed.push({
            'id': parseInt(i),
            'dt': user.last_viewed[i]
        });
    }
    result = db.articles.aggregate([
        // For now, collect just articles the user has written
        { $match: { 'model.author': DBRef('users', user._id) } },
        { $unwind: '$discussions' },
        { $project: {
                'model': '$model',
                'discussions': '$discussions',
                'last_viewed': {
                    '$let': {
                        'vars': { 'last_viewed': last_viewed },
                        'in': {
                            '$setDifference': [
                                { '$map': {
                                    'input': '$$last_viewed',
                                    'as': 'last_viewed',
                                    'in': {
                                        '$cond': [
                                            { '$eq': [ '$$last_viewed.id', '$model.id' ] },
                                            '$$last_viewed.dt',
                                            false
                                        ]
                                    }
                                } },
                                [ false ]
                            ]
                        }
                    }
                }
            }
        },
        // To get a scalar instead of a 1-element array:
        { $unwind: '$last_viewed' },
        // Match only those that were created after last_viewed
        { $match: { 'discussions.dt_create': { $gt: '$last_viewed' } } },
        { $project: { 
            'model.id': 1, 
            'model.title': 1, 
            'discussions': 1, 
            'last_viewed': 1 
        } }
    ]);
    return result.toArray();
}

整个$let以及之后的$unwind将数据转换为以下部分投影(注释掉最后一个$match):

{
    "_id" : ObjectId("54d9af1dca71d8054c8d0ee3"),
    "model" : {
        "id" : NumberLong(18325),
        "title" : "Article title"
    },
    "discussions" : {
        "id" : NumberLong(7543),
        "user" : DBRef("users", ObjectId("54d9ae24ca71d8054c8b4567")),
        "dt_create" : ISODate("2015-01-26T00:10:44Z"),
        "content" : "Some comment here"
    },
    "last_viewed" : ISODate("2015-01-20T00:00:00Z")
},
{
    "_id" : ObjectId("54d9af1dca71d8054c8d0ee3"),
    "model" : {
        "id" : NumberLong(18325),
        "title" : "Article title"
    },
    "discussions" : {
        "id" : NumberLong(7554),
        "user" : DBRef("users", ObjectId("54d9ae24ca71d8054c8b4567")),
        "dt_create" : ISODate("2015-01-26T02:03:22Z"),
        "content" : "Another comment here"
    },
    "last_viewed" : ISODate("2015-01-20T00:00:00Z")
}

到目前为止,这里还不错。但现在的问题是,$match只选择在last_viewed日期之后创建的讨论不起作用。我得到一个空数组响应。但是,如果我硬编码日期并放入$match: { 'discussions.dt_create': { $gt: ISODate("2015-01-20 00:00:00") } },它就可以工作了。但我想让它从last_viewed上取下来。

我找到了另一个SO线程,该线程已通过使用$cmp运算符解决了此问题。

聚合的最后一部分是:

[
    { /* $match, $unwind, $project, $unwind as before */ },
    { $project: {
        'model': 1,
        'discussions': 1,
        'last_viewed': 1,
        'compare': {
            $cmp: [ '$discussions.dt_create', '$last_viewed' ]
        }
    } },
    { $match: { 'compare': { $gt: 0 } } }
]

聚合框架很棒,但它在解决问题时采用了完全不同的方法。希望这能帮助到任何人!

我会不回答这个问题,以防其他人有更好的答案/方法。如果这个答案已经被投票支持了足够多次,我会接受这个。