MongoDB查询外键字段为数组时的查找

6

我已经在互联网和StackOverflow上搜索了,但是我找不到答案甚至问题。

我有两个集合,一个是报告,另一个是用户。 我希望我的查询返回所有报告,并指示指定的用户是否将该报告添加到其数组中作为收藏夹。

报告集合

{ _id: 1, name:"Report One"}
{ _id: 2, name:"Report Two"}
{ _id: 3, name:"Report Three"}

用户集合

{_id: 1, name:"Mike", favorites: [1,3]}
{_id: 2, name:"Tim", favorites: [2,3]}

当users.name="Mike"时期望的结果

{ _id: 1, name:"Report One", favorite: true}
{ _id: 2, name:"Report Two", favorite: false}
{ _id: 3, name:"Report Three", favorite: true}

我能找到的所有答案都在本地字段(reports)上使用$unwind,但在这种情况下,本地字段不是一个数组。外部字段是数组。
我该如何展开外部字段?是否有更好的方法来处理这个问题?
我在网上看到有人建议创建另一个集合favorites,其中包含:
{ _id: 1, userId: 1, reportId: 1 }
{ _id: 2, userId: 1, reportId: 3 }
{ _id: 3, userId: 2, reportId: 2 }
{ _id: 4, userId: 2, reportId: 3 }

这种方法似乎是不必要的。按照逻辑应该可以很简单地连接到外部数组中的ID,对吧?

2个回答

9
你可以使用自定义管道的lookup,它会给你01个结果,然后使用size将数组转换为单个布尔值:
db.reports.aggregate([
    {
        $lookup: {
            from: "users",
            let: { report_id: "$_id" },
            pipeline: [
                {
                    $match: {
                        $expr: {
                            $and: [
                                { $eq: [ "$name", "Mike" ] },
                                { $in: [ "$$report_id", "$favorites" ] }
                            ]
                        }
                    }
                }
            ],
            as: "users"
        }
    },
    {
        $project: {
            _id: 1,
            name: 1,
            favorite: { $eq: [ { $size: "$users" }, 1 ] }
        }
    }
])

如果你需要使用低于3.6版本的MongoDB,你可以使用常规的$lookup,然后使用$filter来仅获取那些nameMike的用户:

db.reports.aggregate([
    {
        $lookup: {
            from: "users",
            localField: "_id",
            foreignField: "favorites",
            as: "users"
        }
    },
    {
        $project: {
            _id: 1,
            name: 1,
            favorite: { $eq: [ { $size: { $filter: { input: "$users", as: "u", cond: { $eq: [ "$$u.name", "Mike" ] } } } }, 1 ] }
        }
    }
])

我应该提到我正在使用MongoDB 4。这很有道理。如果所有这些_id都是ObjectId,我需要做一些不同的事情吗? - Michael Cox
不需要,你只需要在两个集合中都有ObjectIds。 - mickl
@mickl 如果 foreignField 是一个对象数组,而不仅仅是字符串呢?在这种情况下,如果 favorites 同时包含 nameid,会怎么样? - Abe Caymo
@AbeCaymo,你能否开一个新的问题并在这里链接它? - mickl

0
    "_id" : ObjectId("611fc392cfadfbba65d4f4bd"),
    "t_name" : "Bahadur",
    "t_age" : "22",
    "trch" : "java",
    "StudentsDetails" : [
        {
            "_id" : ObjectId("611fc41ccfadfbba65d4f4be"),
            "s_name" : "Asin",
            "s_age" : "18",
            "trch" : "java",
            "tsid" : ObjectId("611fc392cfadfbba65d4f4bd")
        },
        {
            "_id" : ObjectId("611fc8f1a815fb2c737ae31f"),
            "s_name" : "sonu",
            "s_age" : "18",
            "tsid" : ObjectId("611fc392cfadfbba65d4f4bd")
        },
        {
            "_id" : ObjectId("611fc915a815fb2c737ae320"),
            "s_name" : "monu",
            "s_age" : "19",
            "tsid" : ObjectId("611fc392cfadfbba65d4f4bd")
        }
    ]
}

创建训练师集合 创建学者集合

//query

db.Trainer.aggregate(
    [`enter code here`
        {`enter code here`
            $lookup:`enter code here`
            {`enter code here`
                from: "scholar",`enter code here`
                localField: "_id",`enter code here`
                foreignField: "tsid",`enter code here`
                as: "StudentsDetails"`enter code here`
            }`enter code here`
        }`enter code here`
    ]`enter code here`
).pretty();

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