猫鼬:如何同时基于模型字段和关联模型字段进行过滤?

Mongoose: How to filter based on both a model field and associated model fields at the same time?

我有型号User:

const UserSchema = new Schema({
  profile: {
    type: Schema.Types.ObjectId,
    ref: "profiles",
  },
  country: {
    type: String,
    required: true,
  },
});

module.exports = User = mongoose.model("users", UserSchema);

我有型号Profile:

const ProfileSchema = new Schema({
  user: {
    type: Schema.Types.ObjectId,
    ref: "users",
  },
  institution: {
    type: Schema.Types.ObjectId,
    ref: "institutions",
  },
  videoURL: {
    type: String,
  },
});

您可能已经注意到,这两个模型通过分别在 profileuser 字段中相互引用而相互关联。

我正在尝试编写一个函数来获取某个国家/地区上传视频的用户的机构列表,然后使用 institution_search_pattern:

过滤该列表
const getListOfInstitutionsOfUsersWhoUploadedVideosByCountry = function getListOfInstitutionsOfUsersOfWhoUploadedVideosByCountry(
  country_name,
  institution_search_pattern
)

但是,这似乎是一项昂贵的操作,我将不得不:

  1. Search users of country_name by using mongoose find with a field filter and populate the profile.institution and profile.videoURL fields
  2. Filter with normal javascript functions on the returned array of users by user.profile.videoURL where videoURL is not undefined or null
  3. Create an array of the list of institutions of those users by using user.profile.institution
  4. Filter again in the created array with the institution_search_pattern provided by the user by using a fuzzy search module
  5. Send back in response the filtered institutions list

有没有一种方法可以使用 mongoose 查询执行所有这些操作,而无需使用 javascript 函数或模块进行过滤?
例如,我可以根据配置文件模型字段在用户模型上使用过滤器吗?
换句话说,在一个查询中:

  1. 使用 country_name
  2. 过滤 User.country
  3. 按 Profile.videoURL 过滤,其中 videoURL 不是未定义或 null
  4. 按 Profile.institution 过滤 (1)

(1): 按模式过滤机构意味着:

  1. 用户发送字符串search_pattern
  2. 我使用该字符串对机构字段执行模糊搜索。

.

更新 基于评论:“删除了所有 institutions 相关代码。”

const country_name = "India";

const users = await UserSchema.aggregate([
    {
        $match: { country: country_name }
    },
    {
        $lookup: {
            from: "profiles",
            let: { profiles_id: "$profile" },
            pipeline: [
                {
                    $match: {
                        videoURL: { $nin: [undefined, null] },
                        $expr: { $eq: ["$_id", "$$profiles_id"] }
                    }
                }
            ],
            as: "profiles"
        }
    },
    { $unwind: "$profiles" }
]);

为什么要在 profiles 中维护 users 引用?它是多余的。相反,仅在 users collection 中引用 profiles。您提到的所有任务都可以在单个查询中执行。检查此查询(根据您的要求精确更改):

const country_name = "India";
const institution_pattern = /^Insti/;

const users = await UserSchema.aggregate([
    {
        $match: { country: country_name }
    },
    {
        $lookup: {
            from: "profiles",
            let: { profiles_id: "$profile" },
            pipeline: [
                {
                    $match: {
                        videoURL: { $nin: [undefined, null] },
                        $expr: { $eq: ["$_id", "$$profiles_id"] }
                    }
                },
                {
                    $lookup: {
                        from: "institutions",
                        localField: "institution",
                        foreignField: "_id",
                        as: "institution"
                    }
                },
                { $unwind: "$institution" }
            ],
            as: "profiles"
        }
    },
    { $unwind: "$profiles" },
    {
        $match: {
            "profiles.institution.name": {
                $regex: institution_pattern,
                $options: "i"
            }
        }
    }
]);

输出

{
    "_id" : ObjectId("604cb4c36b2dcb17e8b152b8"),
    "profile" : ObjectId("604cb4b16b2dcb17e8b152b5"),
    "country" : "India",
    "profiles" : {
        "_id" : ObjectId("604cb4b16b2dcb17e8b152b5"),
        "institution" : {
            "_id" : ObjectId("604cb49a6b2dcb17e8b152b2"),
            "name" : "Institute 1"
        },
        "videoURL" : "http://abc1.xyz"
    }
}

测试数据:

usres collection:

/* 1 createdAt:3/13/2021, 6:19:07 PM*/
{
    "_id" : ObjectId("604cb4c36b2dcb17e8b152b8"),
    "profile" : ObjectId("604cb4b16b2dcb17e8b152b5"),
    "country" : "India"
},

/* 2 createdAt:3/13/2021, 6:19:07 PM*/
{
    "_id" : ObjectId("604cb4c36b2dcb17e8b152b9"),
    "profile" : ObjectId("604cb4b16b2dcb17e8b152b6"),
    "country" : "India"
},

/* 3 createdAt:3/13/2021, 6:19:07 PM*/
{
    "_id" : ObjectId("604cb4c36b2dcb17e8b152ba"),
    "profile" : ObjectId("604cb4b16b2dcb17e8b152b7"),
    "country" : "U.S"
}

profiles collection

/* 1 createdAt:3/13/2021, 6:18:49 PM*/
{
    "_id" : ObjectId("604cb4b16b2dcb17e8b152b5"),
    "institution" : ObjectId("604cb49a6b2dcb17e8b152b2"),
    "videoURL" : "http://abc1.xyz"
},

/* 2 createdAt:3/13/2021, 6:18:49 PM*/
{
    "_id" : ObjectId("604cb4b16b2dcb17e8b152b6"),
    "institution" : ObjectId("604cb49a6b2dcb17e8b152b3")
},

/* 3 createdAt:3/13/2021, 6:18:49 PM*/
{
    "_id" : ObjectId("604cb4b16b2dcb17e8b152b7"),
    "institution" : ObjectId("604cb49a6b2dcb17e8b152b4"),
    "videoURL" : "http://abc3.xyz"
}

institutions collection:

/* 1 createdAt:3/13/2021, 6:18:26 PM*/
{
    "_id" : ObjectId("604cb49a6b2dcb17e8b152b2"),
    "name" : "Institute 1"
},

/* 2 createdAt:3/13/2021, 6:18:26 PM*/
{
    "_id" : ObjectId("604cb49a6b2dcb17e8b152b3"),
    "name" : "Institute 2"
},

/* 3 createdAt:3/13/2021, 6:18:26 PM*/
{
    "_id" : ObjectId("604cb49a6b2dcb17e8b152b4"),
    "name" : "Institute 3"
}