首页 > 解决方案 > 如何根据引用的子文档值过滤 MongoDB/Mongoose 查询

问题描述

因此,我目前正在克隆一些应用程序概念(用于学习目的),并且我已经构建了一个基本的社交媒体应用程序,该应用程序具有用户、帖子,并允许用户在不想共享数据时阻止用户。但我的问题是,我可以使用引用的文档值/数据(填充用户的被阻止用户数组)来过滤掉父文档(帖子)吗?希望这是有道理的。

到目前为止,这里是模式(当然是精简的):

// User Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;    
const UserSchema = new Schema({
    email: {
        type: String,
        required: [true, "Please provide an email"],
        unique: true
    },
    password: {
        type: String,
        minlength: 8
    },
    blockedUsers: {
        type: [{
            type: Schema.Types.ObjectId,
            ref: 'User'
        }]
    }
});

// Post Schema
const PostSchema = new Schema({
    author: {
        type: Schema.Types.ObjectId,
        ref: 'User'
    },
    dateCreated: {
        type: Date,
        default: Date.now()
    },
    body: {
        type: String,
        required: true,
        trim: true,
        maxlength: 1200
    }
});

const User = mongoose.model('User', UserSchema);
const Post = mongoose.model('Post', PostSchema);

然后我的查询到目前为止只过滤掉了子文档,并且在我的测试中将null作者返回到我的帖子中,这不完全是我希望的功能。澄清一下,它仍然返回被查询的正确帖子,但它不填充“作者”字段,而是将作者字段设置为null

router.get('/p/:postID', catchAsyncErr(async (req, res, next) => {
    // Find post and make sure the user requesting the post is not blocked by the author
    const post = await Post.findById(req.params.postID).populate({
        path: 'author',
        select: 'blockedUsers',
        match: {
            blockedUsers: {
                $ne: req.user.id // The user ID stored in the JSON Web token
            }
        }
    });

    res.status(200).json({
        status: 'Success',
        statusCode: 200,
        message: `Post "${req.params.postID}" found`,
        data: post
    })
}));

我尝试使用聚合,老实说,我仍在尝试完全理解这个$lookup概念。但我有一种奇怪的感觉,那应该是我测试更多的地方......

基本上,如果作者(用户)在“blockedUsers”列表中有请求用户,我真的想隐藏帖子并返回 404。我知道我可以使用常规的 if/else 语句来做到这一点,但我真的希望能够在查询中做到这一点。此外,如果您愿意,我希望能够在用户主页上实现同样的概念。如果他们被用户阻止,阻止他们的用户将不会在他们的提要中显示他们的帖子。

感谢您对此的任何见解!

标签: node.jsmongodbexpressmongoose

解决方案


你可以用它aggregation pipeline来做到这一点。您需要$lookup加入(填充)作者并$match过滤您的用户。

$match与您的第一个参数非常相似find()

$lookupjoin如果您熟悉,则类似于SQL。

要进行设置,$lookup您需要四个参数。

from:哪个是您的目标集合(这里是userslocalField:哪个是您的外键字段名称(这里authorforeignField:哪个是目标集合字段(这里_idas:加入后的名称(这里我使用author

这是片段:

const post = Post.aggregate([{
  $match: {
    _id: req.params.postID
  }
}, 
{
  $lookup: {
    from: 'users',
    localField: 'author',
    foreignField: '_id',
    as: 'author'
  }
}])

那么你post.author.blockedUsers应该工作正常。

如果要检索特定的阻塞用户,可以使用如下聚合:

const post = Post.aggregate([{
  $match: {
    _id: req.params.postID
  }
}, 
{
  $lookup: {
    from: 'users',
    'let': {authorId: "$author"},
    pipeline: [{
      $match: {
        $expr: {
          $and: [{
            $eq: ['$_id', '$$authorId']
          }, {
            $ne: ['$blockedUsers', req.user.id]
          }]
        }
      }
    }
    as: 'author'
  }
}])

推荐阅读