简体   繁体   English

MongoDB聚合:$ exists的任何值

[英]MongoDB aggregation: any value of $exists

I'm trying to leverage MongoDB aggregation for a RESTful api, but got stuck with the following case. 我正在尝试将MongoDB聚合用于RESTful api,但是遇到以下情况。 Suppose, I have Subscriptions model, which looks like this: 假设我有Subscriptions模型,如下所示:

var mongoose = require('mongoose'),
  Schema = mongoose.Schema;

var SubscriptionSchema = new Schema({
  // ...
  cancelled: Date
});

This cancelled property can be either undefined , if the subscription is active, or become a Date of the user's cancel action. 如果订阅处于活动状态,则此cancelled属性可以是undefined ,也可以成为用户取消操作的Date

Now, I have a route GET /me/subscriptions , which aggregates subscriptions and has an optional query parameter: cancelled=true (show only cancelled) or cancelled=false (show only active). 现在,我有一条路由GET /me/subscriptions ,该路由聚合订阅并具有可选的查询参数: cancelled=true (仅显示已取消)或cancelled=false (仅显示活动)。 If not specified, should return any subscription (active or cancelled). 如果未指定,则应返回任何订阅(活动或已取消)。

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;

It works perfectly if I pass the above mentioned query parameter, but fails to find models if no parameter is specified (or if it is not equal to either true or false ). 如果我传递上述查询参数,它会完美地工作,但是如果未指定参数(或者它不等于truefalse ),则无法找到模型。 I have tried lots of stuff instead of the wrong line (in the $match pipeline): 我尝试了很多东西而不是错误的行(在$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

The only solution is see now is something like this, comparing against a value which is not undefined and not of type Date , but it seems way too hacky. 唯一的解决方案是现在看到类似这样的内容,将其与不是undefined且不是Date类型的值进行比较,但似乎太过分了。

cancelled: { $ne: 'some-impossible-value' }

Any help is very much appreciated. 很感谢任何形式的帮助。

I think a little tweaking will satisfy the condition as you want. 我认为稍作调整即可满足您想要的条件。

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;

You don't need to add { $exists: { $or: [ true, false ] } , just don't add anything to query if you don't get true or false. 您不需要添加{$ exists:{$ or:[true,false]},如果您没有得到true或false,则无需添加任何查询内容。

I haven't checked code for syntax error, but logically it will work. 我没有检查代码中的语法错误,但是从逻辑上讲它可以工作。

I would restructure the $match pipeline as follows (requires installing the momentjs library): 我将按如下方式重组$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()
    // ...
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM