我想创建一个查询,该查询将返回给定年份的收入集合。我的架构是这样的:
export const IncomeSchema = new mongoose.Schema({
name: { type: String, required: true },
amount: { type: Number, required: true },
amountAfterTax: { type: Number, required: false },
dateFrom: {
month: Number,
year: Number
},
dateTo: {
month: Number,
year: Number
},
isMonthly: { type: Boolean, required: true },
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
}, { toJSON: { virtuals: true } });
// somehow use the req.params.year
return DB.Incomes.find({ userId: req.user ).exec().then(incomes => {
let incomesForMonth = incomes.filter(income => !income.isMonthly ? (income.dateFrom.year== req.params.year)
: (income.dateFrom.year <= req.params.year && income.dateTo.year >= req.params.year)
});
问题是我希望查询以这种方式运行: - 如果收入是一个月,那么年份必须是相同的dateFrom.year - 如果收入!是月,则年份必须在dateFrom.year和dateTo.year之间
直到现在我从db返回所有内容并在内存中处理它们,这不是最好的解决方案。在SQL Server中,我将使用CASE WHEN语句。我可以在猫鼬中使用什么?
答案 0 :(得分:2)
不幸的是,对于您当前的版本,我无法想到任何具有模仿case语句/表达式的原生MongoDB运算符的执行 - 解决方案。
但是,对于MongoDB Server 3.4和mongoose&gt; = 4.7.3,有一个解决方案,您可以使用聚合框架,特别是新的 $addFields
阶段和此类查询的$switch
运算符。
$addFields
阶段相当于 $project
阶段,该阶段明确指定输入文档中的所有现有字段并向文档添加新字段。在您的情况下,您需要它来创建一个包含过滤器逻辑的新字段字段,然后在 $match
查询中使用。
$switch
运算符会评估一系列案例表达式。当它找到一个计算结果为true的表达式时, $switch
执行一个指定的表达式并突破控制流。
让我们用mongo shell示例来证明这一点:
填充测试集合:
db.test.insert([
{
name: "foo",
amount: 4,
amountAfterTax: 3.2,
dateFrom: {
month: 11,
year: 2016
},
dateTo: {
month: 2,
year: 2017
},
isMonthly: true,
userId: ObjectId("5864b49ab5a589b63ee298e8")
},
{
name: "test",
amount: 547.74,
amountAfterTax: 507.15,
dateFrom: {
month: 4,
year: 2016
},
dateTo: {
month: 4,
year: 2017
},
isMonthly: true,
userId: ObjectId("5864b49ab5a589b63ee298e8")
},
{
name: "bar",
amount: 56,
amountAfterTax: 47.54,
dateFrom: {
month: 5,
year: 2016
},
dateTo: {
month: 7,
year: 2016
},
isMonthly: false,
userId: ObjectId("5864b49ab5a589b63ee298e8")
}
])
运行汇总查询
year = 2016;
db.test.aggregate([
{
"$addFields": {
"incomesForMonth": {
"$switch": {
"branches": [
{
"case": "$isMonthly", /* same as "case": { "$eq": [ "$isMonthly", true ] }, */
"then": { "$eq": [ "$dateFrom.year", year ] }
},
{
"case": { "$eq": [ "$isMonthly", false ] },
"then": {
"$and": [
{ "$lte": [ "$dateFrom.year", year ] },
{ "$gte": [ "$dateTo.year", year ] }
]
}
}
]
}
}
}
},
{ "$match": { "incomesForMonth": true } }
])
示例输出
/* 1 */
{
"_id" : ObjectId("586ea7bafedfbcfd0ed15f9a"),
"name" : "foo",
"amount" : 4.0,
"amountAfterTax" : 3.2,
"dateFrom" : {
"month" : 11.0,
"year" : 2016.0
},
"dateTo" : {
"month" : 2.0,
"year" : 2017.0
},
"isMonthly" : true,
"userId" : ObjectId("5864b49ab5a589b63ee298e8"),
"incomesForMonth" : true
}
/* 2 */
{
"_id" : ObjectId("586ea7bafedfbcfd0ed15f9b"),
"name" : "test",
"amount" : 547.74,
"amountAfterTax" : 507.15,
"dateFrom" : {
"month" : 4.0,
"year" : 2016.0
},
"dateTo" : {
"month" : 4.0,
"year" : 2017.0
},
"isMonthly" : true,
"userId" : ObjectId("5864b49ab5a589b63ee298e8"),
"incomesForMonth" : true
}
/* 3 */
{
"_id" : ObjectId("586ea7bafedfbcfd0ed15f9c"),
"name" : "bar",
"amount" : 56.0,
"amountAfterTax" : 47.54,
"dateFrom" : {
"month" : 5.0,
"year" : 2016.0
},
"dateTo" : {
"month" : 7.0,
"year" : 2016.0
},
"isMonthly" : false,
"userId" : ObjectId("5864b49ab5a589b63ee298e8"),
"incomesForMonth" : true
}