Mongoose:带过滤的无限滚动

问题描述

我有这两个模型:

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":{
        "videoURL":"video_url_6"
      }
    }
  ]
}

我正在尝试实现用户视频的无限滚动,然后是连接的用户

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

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

  • 响应 1:["video_url_1","video_url_2"]
  • 响应 2:["video_url_3","video_url_4"]
  • 响应 3:["video_url_5","video_url_6"]

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

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

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


注意:根据documentation

通常,无法根据故事作者的属性使 populate() 过滤故事。例如,即使填充了作者,以下查询也不会返回任何结果。

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

所以我想,我无法使用 populate 来过滤基于 user.followersuser.follower.profile.videoURL,基于每个 const logout = async (req,res) => { const refToken = req.body.refreshToken; try { const payload = jwt.verify(refToken,process.env.REFRESH_TOKEN_SECRET); req.user = payload.user; // Verify that the token matches the token in redis const getResult = redis.GET(req.user,(err,result) => { if (err) { console.error(err.message); return res.status(500).send('Server Error'); } if (refToken !== result) { return res.status(401).send('Not Authorized'); } console.log(result); return result; }); const deleted = await redis.DEL(req.user,result) => { console.log('executed'); if (err) { console.error(err.message); return res.status(500).send('Server Error'); } return result; }); if (deleted === 1) { return res.status(401).json('User has been logged out'); } } catch (err) { console.error(err.message); if (err.message === 'jwt expired') { return res.status(401).json({ error: 'Token expired' }); } return res.status(500).send('Server Error'); } };

解决方法

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

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

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

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

  • $match user_id 条件
  • $lookupusers 集合中带有聚合管道以供关注
  • $match 以下 id 条件
  • $lookup with profile for 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 方法轻松实现您的要求,
  • 将匹配条件放入以下填充 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,}
});