MongoDB 聚合:$exists 的任何值
Posted
技术标签:
【中文标题】MongoDB 聚合:$exists 的任何值【英文标题】:MongoDB aggregation: any value of $exists 【发布时间】:2017-02-22 14:50:32 【问题描述】:我正在尝试将 MongoDB 聚合用于 RESTful api,但遇到了以下情况。假设,我有Subscriptions
模型,看起来像这样:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var SubscriptionSchema = new Schema(
// ...
cancelled: Date
);
如果订阅处于活动状态,此cancelled
属性可以是undefined
,也可以成为用户取消操作的Date
。
现在,我有一个路由GET /me/subscriptions
,它聚合订阅并有一个可选的查询参数:cancelled=true
(仅显示已取消)或cancelled=false
(仅显示有效)。如果未指定,则应返回任何订阅(有效或已取消)。
var express = require('express'),
router = express.Router(),
Subscription = require('../../models/subscription');
router.get('/me/subscriptions', function(req, res, next)
var cancelled = req.query.cancelled === 'true' ? $exists: true :
req.query.cancelled === 'false' ? $exists: false :
$exists: $or: [ true, false ] ; // wrong logic here
return Subscription.aggregate([
$match: user: req.user._id, cancelled: cancelled ,
$project:
// ...
])
.exec()
// ...
);
module.exports = router;
如果我通过上述查询参数,它可以完美地工作,但如果没有指定参数(或者如果它不等于true
或false
),则无法找到模型。我尝试了很多东西而不是错误的行(在$match
管道中):
cancelled:
cancelled: void 0
cancelled: $exists: $or: [ true, false ]
cancelled: $exists: $in: [ true, false ]
cancelled: $exists: [ true, false ] // obviously wrong, but hey
cancelled: null // obviously wrong, too
cancelled: $or: [ $exists: false , $exists: true ] // can't use $or here, but still, hey
现在看到的唯一解决方案是这样的,与不是undefined
且不是Date
类型的值进行比较,但它似乎太老套了。
cancelled: $ne: 'some-impossible-value'
非常感谢任何帮助。
【问题讨论】:
【参考方案1】:我将重组 $match
管道如下(需要安装 momentjs 库):
router.get('/me/subscriptions', function(req, res, next)
var match =
"$match":
"user": req.user._id,
"cancelled":
;
if (req.query.cancelled === 'true' || req.query.cancelled === 'false')
match["$match"]["cancelled"]["$exists"] = JSON.parse(req.query.cancelled);
else if(moment(req.query.cancelled, moment.ISO_8601, true).isValid())
match["$match"]["cancelled"] = moment(req.query.cancelled).toDate();
else
match["$match"]["cancelled"]["$exists"] = false;
return Subscription.aggregate([match,
"$project":
// ...
]).exec()
// ...
);
【讨论】:
谢谢。不知道基于毫秒的日期查询订阅是否有用:D 顺便说一下,您可以使用点语法,即match.$match.cancelled.$exists = true
。【参考方案2】:
我认为稍微调整一下就能满足你想要的条件。
var express = require('express'),
router = express.Router(),
Subscription = require('../../models/subscription');
router.get('/me/subscriptions', function(req, res, next)
var match_query = user: req.user._id;
if (req.query.cancelled === 'true')
match_query.cancelled = $exists:true;
else if(req.query.cancelled === 'false')
match_query.cancelled = $exists:false;
return Subscription.aggregate([
$match: match_query,
$project:
// ...
])
.exec()
// ...
);
module.exports = router;
您不需要添加 $exists: $or: [ true, false ] ,如果您没有得到 true 或 false ,则不要添加任何要查询的内容。
我没有检查代码是否有语法错误,但从逻辑上讲它会起作用。
【讨论】:
解决方案没问题,但为了简洁起见,我尽量避免使用if-then-else
的东西。谢谢。
我已将您的答案与@chridam 的答案结合起来:var match = user: req.user._id ; if ([ 'true', 'false' ].indexOf(req.query.cancelled) !== -1) match.cancelled = $exists: JSON.parse(req.query.cancelled) ;
以上是关于MongoDB 聚合:$exists 的任何值的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB,如何将查找和排序与聚合中的 $cond 结合起来?