Mongoose条件查询 - 类似语句时的情况

时间:2017-01-05 19:47:03

标签: node.js mongodb mongoose

我想创建一个查询,该查询将返回给定年份的收入集合。我的架构是这样的:

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语句。我可以在猫鼬中使用什么?

1 个答案:

答案 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
}