猫鼬:无限滚动过滤

Mongoose: Infinite scroll with filtering

我有这两个型号:

User.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"
      }
    }
  ]
}

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

This means, I will have to filter user.following.profile.videoURL WHERE videoURL exists

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

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

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

但是,现在我必须对每个 followed_user.profile.video 和 return 两个两个地执行过滤。而且我不知道如何同时执行 BOTH 过滤和无限滚动。


注意: 根据 documentation

In general, there is no way to make populate() filter stories based on properties of the story's author. For example, the below query won't return any results, even though author is populated.

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

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

所以你想要的是 table 无限滚动和:

您可以选择给定的方法来解决您的问题 :

  1. 将数据(第一页)加载到网格中。
  2. 在列上设置过滤器
  3. 再次加载数据,这次使用过滤器。

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

  • $matchuser_id条件
  • $lookupusers 集合中使用聚合管道以供后续
  • $match以下id条件
  • $lookupprofile 对于 following.profile
  • $match videoURL 应该存在
  • $project 显示 profile 字段并使用 $arrayElemAt
  • 获取第一个元素
  • $slicefollowing
  • 中进行分页
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]
      }
    }
  }
])

Playground


建议:

您可以改进架构设计,

  • 删除配置文件架构并在用户集合中添加配置文件对象,这样您就可以使用填充方法轻松实现您的要求,
  • 将匹配条件放在下面 populate for videoURL exists
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,
  }
});