2016-10-13 3 views
1

Я пытаюсь использовать агрегацию 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' } 

Любая помощь очень ценится.

ответ

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; 

Вам не нужно добавить {$ существует: {$ или: [истина, ложь]}, просто ничего не запрашивать, если вы не получите истинное или ложное добавить.

Я не проверял код на ошибку синтаксиса, но логически он будет работать.

+0

Решение все в порядке, но я старался избегать этого 'if-then-else' материала ради краткости. Благодарю. –

+0

Я объединил свой ответ с именем из @chridam: 'var match = {user: req.user._id}; if (['true', 'false'] .indexOf (req.query.cancelled)! == -1) match.cancelled = {$ существует: JSON.parse (req.query.cancelled)}; ' –

0

Я бы реструктурировать трубопровода в $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() 
    // ... 
}); 
+0

Спасибо. Не знаю, полезно ли запрашивать подписку на основе даты с миллисекундами: D Вы можете использовать dot-синтаксис, кстати, например, 'match. $ Match.cancelled. $ Exists = true'. –

Смежные вопросы