Mongoose中按用户名及日期范围查询数组内Exercise对象
解决Mongoose中按用户名查询并过滤子数组日期范围的问题
我来帮你搞定这个问题!首先得明确你之前的错误出在哪:你把日期范围的查询条件错误地放到了find方法的投影参数(第二个参数)里,还错误拆分了查询条件为多个参数,这才触发了Unsupported projection option的报错——MongoDB的投影参数不支持$gte/$lte这类查询操作符。
错误原因拆解
你之前的find调用格式完全不符合规范:
// 错误写法:把查询条件拆成多个参数,还将$gte/$lte错误放在投影逻辑里 Exerciser.find( { "username": name }, { "exercises.date": { "$gte": from }}, { "exercises.date": { "$lte": to }} )
find的正确参数顺序是:(查询条件, 投影规则, 选项配置),所有匹配文档的逻辑都应该放在第一个参数里;而如果要过滤子数组的元素,需要用投影里的数组操作符或者聚合管道来实现。
最简解决方案:用$filter投影筛选子数组
如果你只想返回指定用户的文档,且只保留exercises中日期在from到to之间的条目,用$filter投影是最直接的方案(MongoDB 3.2+支持,Mongoose完全兼容):
let user = await Exerciser.find( { username: name }, // 第一步:匹配目标用户名 { username: 1, // 保留username字段 exercises: { $filter: { input: "$exercises", // 要筛选的目标数组 as: "exercise", // 数组元素的别名 cond: { $and: [ { $gte: ["$$exercise.date", new Date(from)] }, // 日期大于等于起始值 { $lte: ["$$exercise.date", new Date(to)] } // 日期小于等于结束值 ] } } } } ).exec((err, data) => { if (err) { res.json({ Error: "Data not found" }); return done(err); } else { res.json(data); return done(null, data); } });
额外优化:修正Schema的日期默认值
你的Schema里date的默认值写法有个小坑:default: new Date()会在Schema定义的瞬间生成一个固定日期,而不是每次创建文档时取当前时间。改成Date.now才是正确的写法:
let ExerciserSchema = new Schema({ username: { type: String, required: true }, exercises: [{ desc: String, duration: Number, date: { type: Date, default: Date.now } // 每次创建文档时自动生成当前时间 }] });
关于日期格式的说明
你看到错误里的毫秒数是MongoDB存储Date对象的内部格式,完全不用在意——只要你查询时传入的是合法的Date对象(比如new Date("2018-05-01T00:00:00.000Z")),MongoDB会自动完成格式匹配,和你console.log看到的ISO格式没有冲突。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

