根据对分页子项中父项的引用查找文档

有2个集合MovieRank Movie 。这2个集合有引用关系

Movie model

 var mongoose = require('mongoose'); var movieSchema = new mongoose.Schema({ m_tmdb_id: { type: Number, unique: true, index: true }, m_adult: { type: Boolean }, m_backdrop_path: { type: String, }, m_title: { type: Number }, m_genres: { type: Array } }); var MovieModel = mongoose.model('Movie', movieSchema); module.exports = { movie: MovieModel } 

Rank movie model

 var mongoose = require('mongoose'); var rankMovieSchema = new mongoose.Schema({ movie: { type: mongoose.Schema.Types.ObjectId, ref: 'Movie', unique: true }, rank: { type: Number }, count: { type: Number } }); var RankMovieModel = mongoose.model('RankMovie', rankMovieSchema); module.exports = { rankmovie: RankMovieModel } 

我需要从具有特定m_genres名称[电影收集条件]的收集等级电影的每个查询[分页]中select所有前10个项目。

例如:

电影collections

 [{ "_id": ObjectId("5930ea4806fb9e365e0d55b5"), "m_tmdb_id": 888724, "m_imdb_id": "sd2806", "m_title": "Mechanic: Resurrection", "m_genres": [{ "name": "Action", "id": 28 }, { "name": "Crime", "id": 80 }, { "name": "Thriller", "id": 53 }] }, { "_id": ObjectId("583e3bd69e2ea90c69c5a254"), "m_tmdb_id": 23924, "m_imdb_id": "tt3d2806", "m_title": "Inter", "m_genres": [{ "name": "Drama", "id": 32 }, { "name": "Crime", "id": 80 }] }, { "_id": ObjectId("5930eaa706fb9e365e0d55c8"), "m_tmdb_id": 378924, "m_imdb_id": "sdstt3522806", "m_title": "Spiderman", "m_genres": [{ "name": "Action", "id": 28 }, { "name": "Crime", "id": 80 }, { "name": "Thriller", "id": 53 }] }] 

排名电影collections

 [{ "_id": ObjectId("59398daaad49f32c20115422"), "movie": ObjectId("5930ea4806fb9e365e0d55b5"), "count": 5 }, { "_id": ObjectId("59397f7095f0b91ffbdeb1f8"), "movie": ObjectId("583e3bd69e2ea90c69c5a254"), "count": 1 }, { "_id": ObjectId("59397f6695f0b91ffbdeb1f6"), "movie": ObjectId("5930eaa706fb9e365e0d55c8"), "count": 5 }] 

如果我给m_genres名称作为犯罪,惊悚片。查询应该从排名电影有m_genres名称犯罪,惊悚片前十名项目。我可以做到这一点?

我的JS代码

 RankMovie .find(queryOptions) .limit(10) .skip(1 * 10) .populate('movie', 'm_tmdb_id m_title m_generes ') .sort('-count') .exec(function(err, movies) { if(err){ return res.status(404).json(err); } return res.status(200).json(movies); }).catch(function(error) { return res.status(500).json(error); }); 

其实这个“仍然”有最好的方法使用.aggregate()$lookup来join数据。

 var wantedGenres = ["Action","Crime"]; var page = 1; RankMovie.aggregate([ { "$lookup": { "from": Movie.collection.name, "localField": "movie", "foreignField": "_id", "as": "movie" }}, { "$unwind": "$movie" }, { "$match": { "movie.m_genres.name": { "$in": wantedGenres } }}, { "$sort": { "count": -1 } }, { "$project": { "movie": { "m_title": "$movie.m_title", "m_tmdb_id": "$movie.m_tmdb_id", "m_genres": "$movie.m_genres", }, "count": 1 }}, { "$skip": 10*page }, { "$limit": 10 }, ]) 

由于$unwind$match获得了与$lookup自身的组合,并且只返回与$match匹配给定条件的项目,而不是“ALL”相关的数据,所以效率很高。 这个在上一个问题的答案中有详细解释。

但是这三个阶段发生了什么:

  { "$lookup" : { "from" : "movies", "as" : "movie", "localField" : "movie", "foreignField" : "_id", "unwinding" : { "preserveNullAndEmptyArrays" : false }, "matching" : { "m_genres.name" : { "$in" : [ "Action", "Crime" ] } } } } 

当然,然后我们$sort ,以正确和想要的顺序具有匹配的结果。 然后,只需要使用$project来select想要的字段( 请注意$addFields在这里不起作用,因为我们想要“覆盖” ),并执行$skip$limit的分页。

试图做这个“客户端”作为.populate()确实是不可能的,实际上在实际情况下是不可能的。 当然,“stream派select”需要在“实际开始”“分页”之前发生。

所以可能发生的唯一方法是用“相对较小”的“电影”列表,因为你确实需要首先在“stream派”上进行select,然后进行其余的查询。

“完全不切实际”的方式看起来像这样:

 var wantedGenres = ["Action","Crime"]; var page = 1; Movie.find({ "m_genres.name": { "$in": wantedGenres } }) .select({ "_id": 1 }) .exec() .then(movies => RankMovie.find({ "movie": { "$in": movies.map(m => m._id) }) .populate('movie', 'm_tmdb_id m_title m_genres') .sort('-count') .skip(10*page) .limit(10) .exec() ).then(rankings => { // deal with response }) .catch(err => res.status(500).json(err)) 

但是,当然,在发出查询之前,通过电线将“ALL”匹配的"movies"拉到客户端。 并且可能会根据请求使用$in参数打破BSON限制。

另一方面,合并声明不会有同样的限制。