Есть ли способ найти документы во вложенном массиве в mongoDB - PullRequest
0 голосов
/ 08 мая 2020

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 () выше, чтобы получить все сообщения друзей пользователя?

Ответы [ 2 ]

1 голос
/ 08 мая 2020

Если в модели публикации у вас есть ссылка на модель пользователя, то есть какое-то поле, которое идентифицирует, кто написал сообщение, вы можете использовать для l oop для поиска сообщений, сделанных друзьями пользователя.

Не знаю, лучшее ли это решение, но надеюсь, что оно поможет.

В качестве совета: вы должны использовать асинхронный синтаксис вместо обещаний, это помогает при исправлении ошибок.

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


}
0 голосов
/ 08 мая 2020

Если я предполагаю, что схема сообщений похожа на эту

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

вы можете протестировать здесь Пн go Площадка

не стесняйтесь заменять эти '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
      }
    }
  }
])

вы можете проверить это здесь Пн go Детская площадка 2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...