Node.js 中 Mongoose 如何查询嵌套数组的指定日期范围数据
Mongoose嵌套数组日期查询的过滤问题与实现方案
Schema定义
用户存储的用户模型结构如下:
const User = mongoose.model( "User", new mongoose.Schema({ email: String, password: String, name: String, days: [ { day: Date, data: { average_score: {type: mongoose.Schema.Types.Decimal128, default: 0 } } } ] }) );
其中days数组内的day字段统一以ISO格式存储,例如2020-12-29T00:00:00.000Z。
问题表现
最初编写的查询逻辑希望筛选指定用户、且日期在指定范围内的数组元素:
User.find({ "_id": getUserId(req), "days.day":{ "$gte": new Date("2021-01-02T00:00:00.000Z"), "$lt": new Date("2021-01-04T00:00:00.000Z") }}, function (err, result) { if (err){ res.status(400).send({data: { message: err }}); return; } res.status(200).send({data: { message: result }}); } )
实际运行结果:查询返回了匹配用户的全部days数组数据,没有只保留符合日期条件的片段。
问题本质:普通Mongoose
find查询的数组字段条件,仅判断文档内是否存在满足匹配规则的数组元素,不会对数组本身做裁剪,因此会返回完整的数组内容。
可落地的实现方案
使用聚合管道完成查询,通过$match匹配对应用户,再通过$project搭配$filter运算符逐元素过滤days数组,可精准返回符合条件的数组片段,实现代码如下:
let itsArray = Array.isArray(req.body.day) let first = itsArray ? new Date(new Date(req.body.day[0]).toISOString()) : null let second = itsArray ? new Date(new Date(req.body.day[1]).toISOString()) : null let theDay = itsArray ? null : new Date(req.body.day) let now = new Date() theDay = itsArray ? null : new Date(new Date(theDay).toISOString()) // 单日查询过滤规则:匹配完全相等的日期 const objectProject = { $project: { days: { $filter: { input: "$days", as: "index", cond:{ $eq: [ "$$index.day", theDay ] }, } } }} // 范围查询过滤规则:匹配两个日期之间的闭区间元素 const arrayProject = { $project: { days: { $filter: { input: "$days", as: "index", cond: {$and: [ { $gte: [ "$$index.day", first ] }, { $lte: [ "$$index.day", second ] } ]} } } }} await User.aggregate([ {$match:{_id: ObjectID(getUserId(req))}}, itsArray ? arrayProject : objectProject ]) .project({'days.day':1, 'days.data':1}) .then(result => {console.log(result)})
该方案通过$match、$project、$filter的聚合组合,可得到符合预期的查询结果。
内容的提问来源于stack exchange,提问作者Mr. Stefan
相关产品推荐
相关产品推荐

