基于带有日期的动态日期键的猫鼬过滤器

时间:2019-08-29 11:32:52

标签: node.js mongodb mongoose

我创建了一个员工出勤应用程序,其中将出勤记录并存储在数据库中。我试图获得所有日期字段为"Present"的计数。数据像这样存储在数据库中:

"attendances": { <YYYY-MM-DD>: "value" } pair 
// The values being "Absent" or "Present" whatever the case may be. 

问题是,每当我尝试用0计算所有条目时,我都会得到"attendances": {"2019-08-28": "Present"}的值。

谁能帮助我找出问题所在?

//架构

const Schema = mongoose.Schema;
const employeeSchema = new Schema({
  name: String,
  department: String,
  origin: String,
  employDate: String,
  attendances: Object
});
module.exports= Employee = mongoose.model('Employee', employeeSchema);

route.js

router.get('/',(req,res)=>{
  Employee.collection.countDocuments({"attendances.date":"present"},(err,data)=>{
    if(err){
      res.status(500)
      res.send(err)
    }else{
      res.status(200)
      res.json(data)
    }
  })

})

//存储在MongoDB中的数据

  {
        "_id": "5d6565236574b1162c349d8f",
        "name": "Benjamin Hall",
        "department": "IT",
        "origin": "Texas",
        "employDate": "2019-08-27",
        "__v": 0,
        "attendances": {
            "2019-08-28": "Sick"
        }
    },
    {
        "_id": "5d6367ee5b78d30c74be90e6",
        "name": "Joshua Jaccob",
        "department": "Marketing",
        "origin": "new york",
        "employDate": "2019-08-26",
        "__v": 0,
        "attendances": {
            "2019-08-26": "Present",
            "2019-08-27": "Sick"
        }
    },

2 个答案:

答案 0 :(得分:1)

如果要按嵌入式文档中的属性查找,则必须使用点符号

这将不起作用,因为您正在要求mongoo查找出勤对象等于给定对象的文档。

{ "attendances": {"2019-08-26": "Present"}}

这仅在数据库中的出勤对象仅包含

时有效
{ "attendances": {"2019-08-26": "Present"}}

这意味着您询问mongoo存储的对象是否等于给定的对象,它将返回false

 { "attendances": {"2019-08-26": "Present" , "2019-08-27": "Sick"}} ==  { "attendances": {"2019-08-26": "Present"}}

为此,您必须使用点符号

 Employee.collection.countDocuments({"attendances.2019-08-26":"Present"},(err,data)=>{
    if(err){
      res.status(500)
      res.send(err)
    }else{
      res.status(200)
      res.json(data)
    }
  })

答案 1 :(得分:0)

由于动态日期是嵌入式文档的一部分,因此,要使用正则表达式对该字段进行查询(不区分大小写的搜索),您基本上需要使用dot notation { "attendance.2019-08-28": /present/i },它是用{{ 3}}:

const date = "2019-08-28" // dynamic date
const query = {
    ["attendances." + date]: /present/i // computed property name
}

Employee.countDocuments(query, (err, data) => {
    if (err){
        res.status(500).send(err)
    } else{
        res.status(200).json(data)
    }
})

请注意,可以直接在Mongoose模型上访问computed property names函数。


对于日期范围查询,例如,您要返回最近30天的出勤人数,则需要 使用聚合框架进行查询,该框架公开了countDocuments()$objectToArray$filter之类的运算符,以供您计数。

上述运算符使您可以使用$size将出勤文档转换为键值对数组,然后可以根据过去30天的条件以及使用{{3 }}。要获得计数,请在过滤后的数组上使用$objectToArray运算符。

作为说明,在文档上应用$filter

{
    "2019-08-26": "Present",
    "2019-08-27": "Sick"
}

返回

[
    { "k": "2019-08-26", "v": "Present" },
    { "k": "2019-08-27", "v": "Sick" }
]

要过滤过去n天,您需要首先创建该范围内的日期列表,即

[
    "2019-08-27",
    "2019-08-26",
    "2019-08-25",
    ...
]

可以用

在JavaScript中完成
function formatDate(date) {
    var d = new Date(date),
        month = '' + (d.getMonth() + 1),
        day = '' + d.getDate(),
        year = d.getFullYear();

    if (month.length < 2) month = '0' + month;
    if (day.length < 2) day = '0' + day;

    return [year, month, day].join('-');
}


const listDatesForThePastDays = n => (
    Array(n)
        .fill(new Date())
        .map((today, i) => today - 8.64e7 * i)
        .map(formatDate)
)

此列表可以在$size中用作

{ "$filter": {
    "input": { "$objectToArray": "$attendances" },
    "cond": {
        "$and": [
            { "$in": ["$$this.k", listDatesForThePastDays(30)] },
            { "$eq": ["$$this.v", "Present"] }
        ]
    }
} }

并应用$objectToArray运算符以获取计数:

{ "$size": {
    "$filter": {
        "input": { "$objectToArray": "$attendances" },
        "cond": {
            "$and": [
                { "$in": ["$$this.k", listDatesForThePastDays(30)] },
                { "$eq": ["$$this.v", "Present"] }
            ]
        }
    }
} }

您的整体查询将类似于

function formatDate(date) {
    var d = new Date(date),
        month = '' + (d.getMonth() + 1),
        day = '' + d.getDate(),
        year = d.getFullYear();

    if (month.length < 2) month = '0' + month;
    if (day.length < 2) day = '0' + day;

    return [year, month, day].join('-');
}


const listDatesForThePastDays = n => (
    Array(n)
        .fill(new Date())
        .map((today, i) => today - 8.64e7 * i)
        .map(formatDate)
)

Employee.aggregate([
    { "$addFields": { 
        "numberofPresentAttendances": { 
            "$size": {
                "$filter": {
                    "input": { "$objectToArray": "$attendances" },
                    "cond": {
                        "$and": [
                            { "$in": ["$$this.k", listDatesForThePastDays(30)] },
                            { "$eq": ["$$this.v", "Present"] }
                        ]
                    }
                }
            }
        }
    } }
]).exec().
  .then(results => {
      console.log(results);
      // results will be an array of employee documents with an extra field numberofPresentAttendances
  })
  .catch(console.error)

要获取所有员工的人数,则需要将所有文档分组为

Employee.aggregate([
    { "$group": { 
        "_id": null,
        "totalPresent": {
            "$sum": { 
                "$size": {
                    "$filter": {
                        "input": { "$objectToArray": "$attendances" },
                        "cond": {
                            "$and": [
                                { "$in": ["$$this.k", listDatesForThePastDays(30)] },
                                { "$eq": ["$$this.v", "Present"] }
                            ]
                        }
                    }
                }
            }
        } 
    } }
]).exec()
.then(results => {
    console.log(results);
    // results will be an array of employee documents with an extra field numberofPresentAttendances
})
.catch(console.error)