首页 > 解决方案 > Mongoose:带过滤的无限滚动

问题描述

我有这两个模型:

用户.js

const UserSchema = new Schema({
  profile: {
    type: Schema.Types.ObjectId,
    ref: "profiles",
  },
  following: [
    {
      type: Schema.Types.ObjectId,
      ref: "users",
    },
  ],
});

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

Profile.js

const ProfileSchema = new Schema({
  videoURL: {
    type: String,
  },
});

module.exports = Profile = mongoose.model("profiles", ProfileSchema);

下面是一个User文档示例:

{
  "following":  [
    {
      "profile":{
        "videoURL":"video_url_1"
      }
    },
    {
      "profile":{
        "videoURL":"video_url_2"
      }
    },
    {
      "profile":{}
    },
    {
      "profile":{
        "videoURL":"video_url_3"
      }
    },
    {
      "profile":{
        "videoURL":"video_url_4"
      }
    },
    {
      "profile":{
        "videoURL":"video_url_5"
      }
    },
    {
      "profile":{}
    },
    {
      "profile":{
        "videoURL":"video_url_6"
      }
    }
  ]
}

我正在尝试实现连接用户跟随的用户视频的无限滚动。

这意味着,我将不得不过滤 user.following.profile.videoURL WHERE videoURL

假设,我将通过两个视频加载两个视频:

通常,无限滚动很容易,因为我只需按存储顺序 2 x 2 加载文档,而无需对任何字段进行过滤。
示例:在无限滚动中两两显示关注的用户

User.findById(user_id).populate({
    path: "following",
    options: {
      skip: 2 * page,
      limit: 2,
    },
  });

但是,现在我必须对每个 follow_user.profile.video 执行过滤,然后两个两个返回。而且我看不到如何同时执行过滤和无限滚动。


注意:根据文档

一般来说,没有办法根据故事作者的属性制作 populate() 过滤故事。例如,以下查询不会返回任何结果,即使作者已填充。

const story = await Story.
  findOne({ 'author.name': 'Ian Fleming' }).
  populate('author').
  exec();
story; // null

所以我想,我没有办法使用 populate 来过滤user.followers,基于每个user.follower.profile.videoURL

标签: node.jsmongodbmongoosenosqlmern

解决方案


我不确定填充方法是否可行,但您可以尝试聚合管道,

  • $match user_id(健康)状况
  • $lookup集合中的聚合管道users用于以下操作
  • $match以下身份条件
  • $lookupprofile为_following.profile
  • $match videoURL应该存在
  • $project显示profile字段并使用获取第一个元素$arrayElemAt
  • $slice做分页following
let page = 0;
let limit = 2;
let skip = limit * page;

User.aggregate([
  { $match: { _id: mongoose.Types.ObjectId(user_id) } },
  {
    $lookup: {
      from: "users",
      let: { following: "$following" },
      pipeline: [
        { $match: { $expr: { $in: ["$_id", "$$following"] } } },
        {
          $lookup: {
            from: "profiles",
            localField: "profile",
            foreignField: "_id",
            as: "profile"
          }
        },
        { $match: { "profile.videoURL": { $exists: true } } },
        {
          $project: {
            profile: { $arrayElemAt: ["$profile", 0] }
          }
        }
      ],
      as: "following"
    }
  },
  {
    $addFields: {
      following: {
        $slice: ["$following", skip, limit]
      }
    }
  }
])

操场


建议:

您可以改进架构设计,

  • 删除配置文件模式并在用户集合中添加配置文件对象,因此您可以使用填充方法轻松实现您的要求,
  • 将匹配条件放在以下填充videoURL
const UserSchema = new Schema({
    profile: {
      type: {
         videoURL: {
           type: String
         }
      }
    },
    following: [
      {
        type: Schema.Types.ObjectId,
        ref: "users"
      }
    ]
});

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

User.findById(user_id).populate({
  path: "following",
  match: {
    "profile.videoURL": { $ne: null }
  },
  options: {
    skip: 2 * page,
    limit: 2,
  }
});

推荐阅读