【问题标题】:Mongodb Aggregation with future documentsMongodb 与未来文档的聚合
【发布时间】:2017-06-18 06:51:41
【问题描述】:

我正在寻找有关如何在 MongoDB 中以最佳方式处理此类查询的一些指导。

我有一个购买数据库,每个数据库都有一个course 属性以及该人购买的日期。

我想要的是某人购买初始产品后发生的购买清单。

所以 - 这里有一些伪查询:

// 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: 'wes@example.com', course: 'a', purchaseDate: 10 },
{ email: 'wes@example.com', course: 'b', purchaseDate: 20 }, // match
{ email: 'wes@example.com', course: 'c', purchaseDate: 5 }, // not
{ email: 'nancy@example.com', course: 'a', purchaseDate: 5 },
{ email: 'nancy@example.com', course: 'c', purchaseDate: 6 }, // match
{ email: 'nancy@example.com', course: 'b', purchaseDate: 10 }, // match
{ email: 'nancy@example.com', course: 'd', purchaseDate: 1 }, // not

【问题讨论】:

  • 你能发一份样本文件吗?
  • 刚刚发布了一个例子-谢谢
  • 我建议不要像您描述的那样解决问题,而是执行以下操作 1. 分组,以便每个用户拥有一个文档以及他们购买的所有课程 2. 过滤所有未购买的用户course x 这不会给你所有课程之后课程但会让你更接近

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

我建议制作一个类似的 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 和电子邮件!= courseA。

替代方法是将 .aggregate() 与 .project() 和 .match 一起使用,但您会遇到从不同管道步骤获取 courseATimeStamp 的问题

编辑:
我假设 course 是一个数组...如果 course 字段是字符串,则可以应用此解决方案

...
// 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
}

注意:因为reduce()只有在你发出不止一次的时候才会被调用,你可能会得到很奇怪的结果:

  • 如果您只发出一个,结果值将是您发出的值
  • 如果发出多个,结果值将是 reduce() 中过滤后的数组

(如果您发出一个数字,并且您的 reduce 也是一个数字 - 就像项目总数,这很有意义)

所以,如果你需要清理结果,你可能需要使用 mongoose 提供的 finalize()。

【讨论】:

  • 什么是this.course?它如何找到其他课程?
  • 在地图函数中表示当前文档。所以this.course就是“course”字段,如果这是一个数组,可以映射过来
【解决方案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 }}
])

【讨论】:

    猜你喜欢
    • 2015-05-29
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 2020-12-05
    • 1970-01-01
    • 2015-07-05
    • 2013-09-18
    • 1970-01-01
    相关资源
    最近更新 更多