2017-02-01 5 views
4

Я ищу некоторые рекомендации о том, как наилучшим образом подойти к этому типу запросов в MongoDB.Mongodb Агрегация с будущими документами

У меня есть база покупок, каждая из которых имеет свойство course, а также дату, в которую человек приобрел.

Что я хочу, это список покупок, которые произошли после того, как кто-то купил исходный продукт.

Так что - некоторые pesudo запросы здесь:

// first find everyone who signed up for course A 
{ course: 'a' } 

затем

/* 
    out of those people, filter for those who in the future signed up 
    for another course 
*/ 

{ 
    course: { $in: ['b','c','d']}, 
    date: { $gt: $courseA.purchaseDate } 
} 

Является ли это возможно с агрегацией? Или я бы делал несколько вызовов БД для каждой начальной покупки, чтобы проверить, есть ли будущее?

Вот некоторые примеры того, что данные будут выглядеть для каждой покупки:

{ email: '[email protected]', course: 'a', purchaseDate: 10 }, 
{ email: '[email protected]', course: 'b', purchaseDate: 20 }, // match 
{ email: '[email protected]', course: 'c', purchaseDate: 5 }, // not 
{ email: '[email protected]', course: 'a', purchaseDate: 5 }, 
{ email: '[email protected]', course: 'c', purchaseDate: 6 }, // match 
{ email: '[email protected]', course: 'b', purchaseDate: 10 }, // match 
{ email: '[email protected]', course: 'd', purchaseDate: 1 }, // not 
+0

Можете ли вы опубликовать образец документа? – joao

+0

Только что отправил пример - спасибо – wesbos

+0

вместо того, чтобы подойти к проблеме, как вы описали, я предлагаю сделать ниже 1. группа, чтобы у вас был один документ на каждого пользователя и все приобретенные им курсы. 2. Отфильтровать всех пользователей, которые НЕ купили курс 'x' это не даст вам всех курсов * после * курса, но подойдет вам ближе – rrag

ответ

0

Я предлагаю, сделать MapReduce как:

const options = { 
    query: { course: 'courseA' }, // filter only the A course purchased 
    map: function(){ 
    // iterate over course to get the couseA timeStamp 

    // for every course you may have here, compare to couseATime 
    this.course.forEach(function(course){ 
     // extract all the timeStamps 
    }) 

    // if courseTime > couseATime then emit 
    // emit({_id:this._id, email:this.email}, 1) 
    }, 
    reduce: function(key, val){ // key will be {_id:'', email:''} 
    return val // not important 
    } 
} 

Model 
    .mapReduce(options) 
    .then(function(docs){ 
    }) 

Вы должны быть в состоянии получить все _id & электронная почта людей, которые купили курс! = курсА после даты покупки.

Альтернативой будет использовать .aggregate() с .project() и .match, но вы будете иметь проблемы, приобретающие courseATimeStamp из различных трубопроводов шаги

EDIT:
Я предполагал, конечно, был массив ... если поле, конечно, если строка, это решение может быть применен

... 
// Emit every course with the same key (_id + email) 
map(){ 
    emit({_id: this._id, email:this.email}, {course: this.course, date: this.couse.date}) 
}, 

reduce(key, values){ 
    // key is {_id:'', email:''} and is unique 
    // values is an array of {course:'', date:''} 
    var aDate 
    var dates = [] 

    // Find other dates 
    values.forEach(function(val){ 
    // isolate course A date => aDate = ... 
    // populate dates[] with date + course name 
    }) 

    // dates should be => [{courseB: 10, couseC: 15 ...}] 
    // The mean used to saved the couse + date is up to you 

    // filter dates $gt aDate 
    dates = dates.filter(....) 

    return dates 
} 

Примечание: Из-за уменьшение() вызываются только тогда, когда вы испускаете более чем один раз, вы можете иметь так странно результат:

  • Если вы испускают только один, результирующее значение будет на ваши emited значения
  • Если вы испускают больше, чем один, то результирующее значение будет отфильтрованный массив в уменьшить()

(это сделать Сенс, если вы испускаете номер, а также уменьшаете число - как и общее количество)

Итак, если вам нужно очистить результаты, вы можете использовать финализацию(), предоставленную mongoose.

+0

Что такое' this.course'? Как найти другие курсы? – wesbos

+0

в функции карты представляют собой текущий документ. Итак, this.course - поле «курс», если это массив, вы можете отобразить более –

+0

. Но другие курсы - это разные документы – wesbos

2

Разобрался ответ с некоторой помощью более на Twitter

.aggregate([ 
    // Project the target purchasedate 
    { $match: { course: { $in: ['JS3', 'RDX', 'WTF', 'RFB', 'ES6']}}}, 
    { $project: { 
    email: 1, amount: 1, course: 1, purchaseDate: 1, 
    target: { 
     $cond: { 
     if: { $eq: ['$course', 'JS3'] }, 
     then: "$purchaseDate", 
     else: 0, 
     } 
    } 
    }}, 
    // Group records by email, storing the target and the highest encountered purchase date 
    { $group: { 
    _id: '$email', 
    courses: { $push: '$course'}, 
    amounts: { $push: '$amount'}, 
    count: { $sum: 1 }, 
    spent: { $sum: '$amount' }, 
    target: { $first: '$target'}, 
    max: { $max: '$purchaseDate'} 
    }}, 
    // // Check if the highest encountered date is greater then the target 
    { $project: { 
    email: 1, course: 1, amounts: 1, spent: 1, count: 1, courses: 1, target:1, max: 1, 
    selected: { $cond: [{ $gt: ['$max', '$target']}, true, false] } 
    }}, 
    // Filter out the non-matches 
    { $match: { 
    target: { $gt: 0 }, 
    selected: true, 
    spent: { $gt: 0 }, 
    }}, 
    { $sort: { spent: -1 }} 
]) 
0

Вот другая альтернатива.

$lookup путем присоединения к другой коллекции на email и $redact Документов на основе массива purchaseDate и courses.

db.purchases.aggregate({ 
    $match: { 
     course: 'a' 
    } 
}, { 
    $lookup: { 
     from: "purchases", 
     localField: "email", 
     foreignField: "email", 
     as: "userCourses" 
    } 
}, { 
    $redact: { 
     $cond: [{ 
      $or: [{ 
       $and: [{ 
        $gt: ["$purchaseDate", "$$ROOT.purchaseDate"] 
       }, { 
        course: { 
         $or: ['b', 'c', 'd'] 
        } 
       }] 
      }, { 
       $eq: ["$course", "$$ROOT.course"] 
      }] 
     }, "$$DESCEND", "$$PRUNE"] 
    } 
}); 

Пример вывода

{ 
     "email" : "[email protected]", 
     "course" : "a", 
     "purchaseDate" : 10, 
     "userCourses" : [ 
       { 
         "email" : "[email protected]", 
         "course" : "a", 
         "purchaseDate" : 10 
       }, 
       { 
         "email" : "[email protected]", 
         "course" : "b", 
         "purchaseDate" : 20 
       } 
     ] 
} 
{ 
     "email" : "[email protected]", 
     "course" : "a", 
     "purchaseDate" : 5, 
     "userCourses" : [ 
       { 
         "email" : "[email protected]", 
         "course" : "a", 
         "purchaseDate" : 5 
       }, 
       { 
         "email" : "[email protected]", 
         "course" : "c", 
         "purchaseDate" : 6 
       }, 
       { 
         "email" : "[email protected]", 
         "course" : "b", 
         "purchaseDate" : 10 
       } 
     ] 
}