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;

如果我通过上述查询参数,它可以完美地工作,但如果没有指定参数(或者如果它不等于truefalse),则无法找到模型。我尝试了很多东西而不是错误的行(在$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 的任何值的主要内容,如果未能解决你的问题,请参考以下文章

rmongodb: $exists 给出空结果

如何将sql查询转换为mongodb查询中的exists

MongoDB,如何将查找和排序与聚合中的 $cond 结合起来?

MongoDB,如何将查找和排序与聚合中的 $cond 结合起来?

Mongodb匹配聚合不适用于日期

$match mongodb聚合框架_mongoose中的多条件[重复]