【问题标题】:Is there a way to find documents in nested array in mongoDB有没有办法在 mongoDB 的嵌套数组中查找文档
【发布时间】:2020-05-07 21:23:34
【问题描述】:

const userSchema = new Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    posts: [{
        type: Schema.Types.ObjectId,
        ref: 'Post'
    }],
    friends: [{
        type: Schema.Types.ObjectId,
        ref: 'User'
    }],
});

// Exporting the schema so it can be accessed by requiring it.
module.exports = mongoose.model('User', userSchema);

如你所见,我得到了这个用户模式,它有一个朋友数组和一个帖子数组。

User.findById(userId).then(result => {
        Post.find(query).then(posts => {
            res.status(200).json(posts)
        }).catch(err => {
            if (!err.statusCode) {
                err.statusCode = 500;
            }
            next(err);
        })
    });

是否有任何查询可以适合上面的 find() 以获取用户朋友的所有帖子?

【问题讨论】:

  • 您可以将 Post 架构添加到问题中吗?

标签: javascript arrays node.js mongodb mongoose


【解决方案1】:

如果在帖子模型中您有一个指向用户模型的链接,即某个标识帖子作者的字段,您可以使用 for 循环来搜索用户朋友发布的帖子。

我不知道这是否是最好的解决方案,但我希望它有所帮助。

作为提示,您应该使用异步语法而不是 Promise,这有助于纠正错误。

async function getFriendsPosts(req,res){
  /*in this array we will store the 
    posts of the user's friends */
  let posts = [];
  
    try{
          //we check if the user exists
          let user = User.findById(req.params.id);
          //if it doesn't exist we will send a message
          if(!user) res.status(404).send("User not Found");
          else{
              /* here we compare the id of the friends with the id of
               the friends with the "creator" field in the post model*/
              for await(let friend of user.friends){
                for await(let creator of Post.find()){
                    /* if there is a match we send 
                       it to the post array*/
                    if(friend._id.equals(creator._id)){
                      posts.push(creator);
                    }
                }
              }
              
              /*finally we send the array with the posts*/
              res.send(posts);
          }
    
    
    }catch(err){
      res.status(500).send("Internal Server Error");
    }


}

【讨论】:

    【解决方案2】:

    如果我认为 Post Schema 是这样的

    {
        title: String,
        content: String,
        owner: { type: Schema.Types.ObjectId, ref: 'User'}
    }
    

    然后我们可以使用聚合管道来获取某个用户的好友帖子

    类似的东西

    db.users.aggregate([
      {
        $match: {
          _id: "userId1" // this should be of type ObjectId, you need to convert req.params.id to ObjectId (something like: mongoose.Types.ObjectId(req.params.id) instead of 'userId1')
        }
      },
      {
        $lookup: {
          from: "posts",
          let: {
            friendsIDs: "$friends"
          },
          pipeline: [
            {
              $match: {
                $expr: {
                  $in: ["$owner", "$$friendsIDs"]
                }
              }
            }
          ],
          as: "friendsPosts"
        }
      }
    ])
    

    你可以在这里测试Mongo Playground

    请随意用您的真实用户和帖子 ID 替换此链接中的这些 'userId1'、'userId2'、...、'postId1、'postId2'、..

    通过这种方式,您在一次查询而不是两次查询中获得了某个用户的好友帖子

    那么函数就是这样的

    User.aggregate([
      {
        $match: {
          _id: mongoose.Types.ObjectId(req.params.id)
        }
      },
      {
        $lookup: {
          from: "posts", // this should be the posts collection name, It may be 'Post' not 'posts', check it
          let: {
            friendsIDs: "$friends"
          },
          pipeline: [
            {
              $match: {
                $expr: {
                  $in: ["$owner", "$$friendsIDs"]
                }
              }
            }
          ],
          as: "friendsPosts"
        }
      }
    ]).then(result => {
        // the aggregate pipeline is returning an array
        // but we are sure it will be an array of only one element as we are searching for only one user, so we can use result[0]
    
        result = result || []; // double check the result array
        result[0] = result[0] || {}; // double check the user object
        var posts = result[0].friendsPosts; // here is the friends posts array
    
        // return the posts array
        res.json(posts);
    })
    

    希望对你有帮助


    更新

    如果我们需要对firendsPosts进行排序,然后限制它们

    我们可以使用下面的

    db.users.aggregate([
      {
        $match: {
          _id: "userId1"
        }
      },
      {
        $lookup: {
          from: "posts",
          let: {
            friendsIDs: "$friends"
          },
          pipeline: [
            {
              $match: {
                $expr: {
                  $in: [
                    "$owner",
                    "$$friendsIDs"
                  ]
                }
              }
            }
          ],
          as: "friendsPosts"
        }
      },
      {
        $unwind: "$friendsPosts" // unwind the array to get a stream of documents
      },
      {
        $sort: {
          "friendsPosts.createdAt": 1 // then sort the posts by the createdAt Date in ascending order
        }
      },
      {
        $group: { // then group the posts again after sorting
          _id: "$_id",
          friendsPosts: {
            $push: "$friendsPosts"
          }
        }
      },
      {
        $project: { 
          friendsPosts: {
            $slice: ["$friendsPosts", 2] // this is to limit the posts
          }
        }
      }
    ])
    

    你可以在这里测试Mongo Playground 2

    【讨论】:

    • 有没有办法对friendsPosts进行限制和排序
    • 是的,有,我已经更新了我的答案,你能再检查一下看看最后的更新吗?
    • 太棒了!还有一件事,这可以说是我只想获取在特定日期之后创建的帖子吗?
    • 我们可以在 $sort 之后和 $group 之前使用另一个 $match 阶段来获取在某个日期之后创建的帖子,试试这个mongoplayground.net/p/rdP4ZmiMJdk
    猜你喜欢
    • 2019-02-20
    • 1970-01-01
    • 2021-11-12
    • 2016-07-01
    • 2019-09-24
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    • 2019-03-24
    相关资源
    最近更新 更多