有2个系列电影和排名电影。这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
名称[电影收集条件]的集合排名电影中选择每个查询[分页]中的所有前10个项目。
例如:
电影收藏
[{
"_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
}]
}]
排名电影收藏
[{
"_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
名称作为Crime,Thriller。查询应该从排名电影中获取前10项,其中m_genres
名称为Crime,Thriller。我能如何实现这一目标?
我的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);
});
答案 0 :(得分:1)
实际上这"仍然是"使用.aggregate()
和$lookup
加入数据的最佳方法。
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"相关数据。完整地在the Answer to your previous question中解释了这一点。
但这就是这三个阶段的结果:
{
"$lookup" : {
"from" : "movies",
"as" : "movie",
"localField" : "movie",
"foreignField" : "_id",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"m_genres.name" : {
"$in" : [
"Action",
"Crime"
]
}
}
}
}
当然,我们$sort
为了使匹配的结果符合正确的顺序。然后,只需使用$project
选择所需字段(请注意,$addFields
在此处不起作用,因为我们希望"覆盖" )并为分页执行$skip
和$limit
。
尝试这样做"客户端"因为.populate()
几乎是不可能的,在实际情况下几乎是不可能的。问题当然是按类型选择"需要发生"之前"你真的开始"分页"。
所以可能发生的唯一方法是使用相对较小的" "电影"的列表,因为你确实需要在"类型"上进行选择。首先,然后执行查询的其余部分。
"完全不切实际"方式看起来像:
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
ids
的{{1}}参数来断开请求的BSON限制。
另一方面,汇总声明不会有相同的限制。