在MongoDB中聚合和扁平化数组字段

3

我有一个模式:

var ProjectSchema = new Schema({
    name: {
        type: String,
        default: ''
    },
    topics: [{
        type: Schema.ObjectId,
        ref: 'Topic'
    }],
    user: {
        type: Schema.ObjectId,
        ref: 'User'
    }
});

我想要做的是获取所有项目中所有主题的数组。我不能直接查询主题并获得全部列表,因为有些主题未被分配,并且它们没有与项目的引用关联(为了避免双向引用)。因此,我需要查询项目并进行聚合。我的操作类似于:
Project.aggregate([{$project:{topics:1}}]);

但是这会给我一个带有topics字段的Project对象数组。我想要的是一个带有topic对象的数组。
我该如何做?

请发布示例输出 - karthick
1个回答

8
处理数组时,通常首先对数组成员应用 $unwind,然后使用 $group 查找不同的条目:
Project.aggregate(
    [
        { "$unwind": "$topics" },
        { "$group": { "_id": "$topics._id" } }
    ],
    function(err,docs) {

    }
)

但对于这种情况,可能更简单的方法是只使用.distinct(),它将执行与上述相同的操作,但仅返回结果数组而不是文档:

Project.distinct("topics._id",function(err,topics) {

});

等等,因为我知道你真正想问的是什么。你不需要 _id 值,而是你的 Topic 数据有一个像 "name" 的属性。

由于你的项是“引用”的并且在另一个集合中,你无法对另一个集合中文档的属性进行聚合管道或 .distinct() 操作。简而言之,“MongoDB 不执行连接”,而 mongoose 的 .populate() 不是连接,只是使用其他查询“模拟”它。

但当然,你可以从“Project”中找到“distinct”值,然后从“Topic”中获取信息,如下所示:

Project.distinct("topics._id",function(err,topics) {
    Topic.find({ "_id": { "$in": topics } },function(err,topics) {

    });
});

这很方便,因为.distinct()函数已经返回了一个适用于$in的数组。


1
感谢您提供详细的答案。只要我写了 Project.distinct("topics", function(err, topicIDs) {});,它几乎就可以工作了。 - Mike M
@MikeM 我的错,因为我通常在写作时会想到“嵌入”。当然,使用引用数组时,值只是数组中的ObjectId值,没有子属性,正如你所指出的。 - Blakes Seven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接