有一个名为Movie的模式,其中包含有关电影的信息。
电影模态
var mongoose = require('mongoose');
var movieSchema = new mongoose.Schema({
m_tmdb_id: {
type: Number,
unique: true,
index: true
},
m_backdrop_path: {
type: String,
},
m_budget: {
type: Number,
},
m_homepage: {
type: String
},
m_imdb_id: {
type: String,
},
m_original_language: {
type: String
},
m_original_title: {
type: String
},
m_poster_path: {
type: String
},
m_poster_key: {
type: String
},
m_release_date: {
type: Date
},
m_revenue: {
type: Number
},
m_runtime: {
type: Number
},
m_title: {
type: String
},
m_genres: {
type: Array
},
created_at: {
type: Date
},
updated_at: {
type: Date,
default: Date.now
}
});
var MovieModel = mongoose.model('Movie', movieSchema);
module.exports = {
movie: MovieModel
}
我需要从具有不同条件的电影集合中的每个查询[分页]中选择10个项目。我在我的API中添加了3个条件[基于类别名称,发布日期,语言]。
Js代码
router.post('/movies', function(req, res, next) {
var perPage = parseInt(req.query.limit);
var page = req.query.page;
var datefrom = new Date();
var dateto = new Date();
var generNames = req.body.generNames;
dateto.setMonth(dateto.getMonth() - 2);
var queryOptions = {
$and: [{
'm_release_date': {
$lte: datefrom,
$gte: dateto
}
}, {
"m_genres.name": {
$in: generNames
}
}, {
'm_original_language': 'en'
}, ]
};
Movie
.find(queryOptions)
.select('_id m_tmdb_id m_poster_path m_original_title')
.sort('-m_release_date')
.limit(perPage)
.skip(perPage * page)
.exec(function(err, movies) {
if (movies) {
return res.status(200).json(movies);
}
}).catch(function(error) {
return res.status(500).json(error);
});
});
我需要再添加一个条件,条件是从集合电影中选择发布日期为[年:2003、2004、2010等]的年份中的日期为[m_release_date]的项目。我该怎么做?
enter code here
例:
电影收藏
[
{
"_id": "59420dff3d729440f200bccc",
"m_tmdb_id": 453651,
"m_original_title": "PIETRO",
"m_poster_path": "/3sTFUZorLGOU06A7P3XxjLVKKGD.jpg",
"m_release_date": "2017-07-14T00:00:00.000Z",
"m_runtime": 8,
"m_genres": [{
"id": 18,
"name": "Drama"
}]
},
{
"_id": "594602610772b119e788edab",
"m_tmdb_id": 425136,
"m_original_title": "Bad Dads",
"m_poster_path": null,
"m_release_date": "2017-07-14T00:00:00.000Z",
"m_runtime": 0,
"m_credits_cast": [],
"m_genres": [{
"id": 35,
"name": "Comedy"
}]
},
{
"_id": "59587747d282843883df755e",
"m_tmdb_id": 364733,
"m_original_title": "Blind",
"m_poster_path": "/cXyObe5aB63ueOndEXxXabgAvIi.jpg",
"m_release_date": "2017-07-14T00:00:00.000Z",
"m_runtime": 105,
"m_genres": [{
"id": 18,
"name": "Drama"
}]
},
{
"_id": "595d93f9c69ab66c4f48254f",
"m_tmdb_id": 308149,
"m_original_title": "The Beautiful Ones",
"m_poster_path": "/kjy1obH5Oy1IsjTViYVJDQufeZP.jpg",
"m_release_date": "2017-07-14T00:00:00.000Z",
"m_runtime": 94,
"m_genres": [{
"id": 18,
"name": "Drama"
}]
},
{
"_id": "59420de63d729440f200bcc7",
"m_tmdb_id": 460006,
"m_original_title": "Черная вода",
"m_poster_path": "/kpiLwx8MGGWgZMMHUnvydZkya0H.jpg",
"m_release_date": "2017-07-13T00:00:00.000Z",
"m_runtime": 0,
"m_genres": []
},
{
"_id": "594602390772b119e788eda3",
"m_tmdb_id": 281338,
"m_original_title": "War for the Planet of the Apes",
"m_poster_path": "/y52mjaCLoJJzxfcDDlksKDngiDx.jpg",
"m_release_date": "2017-07-13T00:00:00.000Z",
"m_runtime": 142,
"m_genres": [{
"id": 28,
"name": "Action"
}
]
}
]
API请求
最佳答案
修复数据以提高效率
老实说,最高效的方法是在数据中为m_release_year
创建一个新字段。然后,向查询提供$in
条件代替日期范围就变得很简单,但这当然可以使用索引。
因此,有了这样的字段,启动查询的代码将变为:
// Just to simulate the request
const req = {
body: {
"generNames": ["Action"],
"selectedYear": ["2003,2004,2005,2017"]
}
}
// Your selectedYear input looks wrong. So correcting from a single string
// to an actual array of integers
function fixYearSelection(input) {
return [].concat.apply([],input.map(e => e.split(",") )).map(e => parseInt(e) ).sort()
}
// Outputs like this - [ 2003, 2004, 2005, 2017 ]
let yearSelection = fixYearSelection(req.body.selectedYear);
Movie.find({
"m_release_year": { "$in": yearSelection },
"m_genres.name": { "$in": req.body.generNames },
"m_original_language": "en"
})
.select('_id m_tmdb_id m_poster_path m_original_title')
.sort('-m_release_date')
.limit(perPage)
.skip(perPage * page)
.exec(function(err, movies) {
在数据中将新字段放置在
mongo
shell中很简单:let ops = [];
db.movies.find({ "m_release_year": { "$exists": false } }).forEach( doc => {
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "m_release_year": doc.m_release_date.getUTCFullYear() } }
});
if ( ops.length >= 1000 ) {
db.movies.bulkWrite(ops);
ops = [];
}
});
if ( ops.length > 0 ) {
db.movies.bulkWrite(ops);
ops = [];
}
这将迭代集合中的所有项目并“提取”年份信息,然后将其写入新字段。然后,创建与查询选择中使用的字段匹配的索引是明智的。
强制计算
否则,您基本上将“强制进行计算”,并且没有数据库可以有效地做到这一点。 MongoDB中的两种方法使用的是
$where
或$redact
,其中“后期”应始终优先使用前一种,因为至少$redact
使用的是本机编码操作进行比较,而不是对JavaScript进行评估$where
,运行速度慢得多。// Just to simulate the request
const req = {
body: {
"generNames": ["Action"],
"selectedYear": ["2003,2004,2005,2017"]
}
}
// Your selectedYear input looks wrong. So correcting from a single string
// to an actual array of integers
function fixYearSelection(input) {
return [].concat.apply([],input.map(e => e.split(",") )).map(e => parseInt(e) ).sort()
}
// Outputs like this - [ 2003, 2004, 2005, 2017 ]
let yearSelection = fixYearSelection(req.body.selectedYear);
/*
* Not stored, so we try to "guestimate" the reasonable "range" to at
* least give some query condtion on the date and not search everything
*/
var startDate = new Date(0),
startDate = new Date(startDate.setUTCFullYear(yearSelection[0])),
endDate = new Date(0),
endDate = new Date(endDate.setUTCFullYear(yearSelection.slice(-1)[0]+1));
// Helper to switch our $redact "if" based on supported MongoDB
const version = "3.4";
function makeIfCondition() {
return ( version === "3.4" )
? { "$in": [ { "$year": "$m_release_date" }, yearSelection ] }
: { "$or": yearSelection.map(y =>
({ "$eq": [{ "$year": "$m_release_date" }, y })
) };
}
然后使用
$redact
:Movie.aggregate(
[
{ "$match": {
"m_release_date": {
"$gte": startDate, "$lt": endDate
},
"m_genres.name": { "$in": req.body.generNames },
"m_original_language": "en"
}},
{ "$redact": {
"$cond": {
"if": makeIfCondition(),
"then": "$$KEEP",
"else": "$$PRUNE"
}
}},
{ "$sort": { "m_release_date": -1 } },
{ "$project": {
"m_tmdb_id": 1,
"m_poster_path": 1,
"m_original_title": 1
}},
{ "$skip": perPage * page },
{ "$limit": perPage }
],
(err,movies) => {
}
)
或通过
$where
:Movie.find({
"m_release_date": {
"$gte": startDate, "$lt": endDate
},
"m_genres.name": { "$in": req.body.generNames },
"m_original_language": "en",
"$where": function() {
return yearSelection.indexOf(this.m_release_date.getUTCFullYear()) !== -1
}
})
.select('_id m_tmdb_id m_poster_path m_original_title')
.sort('-m_release_date')
.limit(perPage)
.skip(perPage * page)
.exec(function(err, movies) {
基本逻辑是取而代之的是从
$year
字段中按.getUTCFullYear()
或m_release_date
提取当年,并将其与yearSelection
列表进行比较,以便仅返回匹配的年份。对于
$redact
的使用,对于最新版本(3.4及更高版本),最有效的方式是通过$in
进行实际比较,或者使用$or
中的值有效地将.map()
应用于条件数组,而不是应用直接将数组作为参数。结论
如果要定期查询集合中的实际数据,此处的一般建议是将其包含在集合中。使用实际值后,您可以在字段上放置索引,常规查询运算符可以使用这些值并利用索引。
在不将“年”的值放入集合中的情况下,需要将随后的“计算”应用于所有可能的条目,以确定哪个匹配项。因此效率不高。
即使在此示例中,我们也尝试通过至少基于给定的条目(假定从最小到最大)抛出日期的“可能范围”来“重新获得”某种效率。但是,当然选择中有“未使用的年份”,但是总比不提供任何内容并且仅选择计算结果要好。
关于javascript - 在Mongodb中查找基于Year的文档,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45139789/