简体   繁体   中英

Is there a way to find documents in nested array in mongoDB

 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);

As you can see I got this user schema that has a friends array and a posts array.

 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); }) });

Is there any query that can fit in the find() above in order to get all the posts of the user's friends?

If in the post model you have a link to the user model, that is, some field that identifies who wrote the post, you could use a for loop to search for posts made by the user's friends.

I don't know if this is the best solution but I hope it helps.

As a tip, you should use asynchronous syntax instead of promises, this helps when correcting errors.

 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"); } }

If I suppose that the Post Schema is something like that

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

then we can use aggregate pipeline to get the friends posts of some user

something like that

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"
    }
  }
])

you can test it here Mongo Playground

feel free to replace these 'userId1', 'userId2', ..., 'postId1, 'postId2', .. in this link with your real users and posts Ids

by this way, you got the friends posts of some user in one query rather than two queries

then the function will be something like that

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);
})

hope it helps


Update

If we need to sort the firendsPosts, and then limit them

we can use the following

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
      }
    }
  }
])

you can test it here Mongo Playground 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM