在mongoid / mongo中组合两个表的结果

时间:2018-01-23 11:52:47

标签: ruby-on-rails ruby mongodb mongoid

大家好,将两个mongoid查询的结果结合起来的最佳方法是什么。

我的问题是我想知道活跃用户,用户可以发送信件和通知,如果他发送信件或通知被认为是活动的,则它们都是单独的表和用户。我想知道的是每月有多少活跃用户。

现在我能想到的就是这样做

Letter.collection.aggregate([
                       { '$match': {}.merge(opts) },
                       { '$sort': { 'created_at': 1 } },
                       {
                         '$group': {
                           _id: '$customer_id',
                           first_notif_sent: {
                             '$first': {
                               'day': { '$dayOfMonth': '$created_at' },
                               'month': { '$month': '$created_at' },
                               'year': { '$year': '$created_at' }
                             }
                           }
                         }
                       }])
Notification.collection.aggregate([
                       { '$match': {}.merge(opts) },
                       { '$sort': { 'created_at': 1 } },
                       {
                         '$group': {
                           _id: '$customer_id',
                           first_notif_sent: {
                             '$first': {
                               'day': { '$dayOfMonth': '$created_at' },
                               'month': { '$month': '$created_at' },
                               'year': { '$year': '$created_at' }
                             }
                           }
                         }
                       }])

我正在寻找的是获得最短的日期,然后结合结果并获得计数。现在我可以得到结果并循环遍历每个结果并创建一个新列表。但是我想知道是否有办法直接在mongo中进行。

修改

对于字母

  def self.get_active(tenant_id)
    map = %{
      function() {
        emit(this.customer_id, new Date(this.created_at))
      }
    }

    reduce = %{
      function(key, values) {
        return new Date(Math.min.apply(null, values))
      }
    }
    where(tenant_id: tenant_id).map_reduce(map, reduce).out(reduce: "#{tenant_id}_letter_notification")
  end

通知

def self.get_active(tenant_id)
    map = %{
      function() {
        emit(this.customer_id, new Date(this.updated_at))
      }
    }

    reduce = %{
      function(key, values) {
        return new Date(Math.min.apply(null, values))
      }
    }
    where(tenant_id: tenant_id, transferred: true).map_reduce(map, reduce).out(reduce: "#{tenant_id}_outgoing_letter_standing_order_balance")
  end

这就是我想要的,其中一个原因是,查找不适用于我的mongo版本。

1 个答案:

答案 0 :(得分:2)

  

客户创建了一个新通知或一封新信件,我想在第一个创建的第一个通知中创建。

让我们首先解决这个问题。给出文档架构的示例如下:

Letter集合中的文档架构:

{ _id: <ObjectId>,
  customer_id: <integer>,
  created_at: <date> }

并且,Notification集合中的文档架构:

{ _id: <ObjectId>,
  customer_id: <integer>,
  created_at: <date> }

您可以使用aggregation pipeline $lookup加入这两个集合。例如,使用mongo shell

db.letter.aggregate([
    {"$group":{"_id":"$customer_id", tmp1:{"$max":"$created_at"}}}, 
    {"$lookup":{from:"notification", 
              localField:"_id",
              foreignField:"customer_id", 
              as:"notifications"}}, 
    {"$project":{customer_id:"$_id", 
               _id:0,
               latest_letter:"$tmp1", 
               latest_notification: {"$max":"$notifications.created_at"}}},
    {"$addFields":{"latest": 
                {"$cond":[{"$gt":["$latest_letter", "$latest_notification"]}, 
                 "$latest_letter", 
                 "$latest_notification"]}}},
    {"$sort":{latest:-1}}
], {cursor:{batchSize:100}})

上述aggregation pipeline的输出是来自created_atLetter的{​​{1}}字段的排序顺序的客户列表。示例输出文档:

Notification
  

我想知道的是每月有多少活跃用户

要实现此目的,您只需使用$sort替换上述聚合管道的最后一个阶段($group)即可。例如:

  {
    "customer_id": 0,
    "latest_letter": ISODate("2017-12-19T07:00:08.818Z"),
    "latest_notification": ISODate("2018-01-26T13:43:56.353Z"),
    "latest": ISODate("2018-01-26T13:43:56.353Z")
  },
  {
    "customer_id": 4,
    "latest_letter": ISODate("2018-01-04T18:55:26.264Z"),
    "latest_notification": ISODate("2018-01-25T02:05:19.035Z"),
    "latest": ISODate("2018-01-25T02:05:19.035Z")
  }, ...

示例输出如下:

db.letter.aggregate([
    {"$group":{"_id":"$customer_id", tmp1:{$max:"$created_at"}}}, 
    {"$lookup":{from:"notification", 
              localField:"_id",
              foreignField:"customer_id", 
              as:"notifications"}}, 
    {"$project":{customer_id:"$_id", 
               _id:0,
               latest_letter:"$tmp1", 
               latest_notification: {"$max":"$notifications.created_at"}}},
    {"$addFields":{"latest": 
                {"$cond":[{"$gt":["$latest_letter", "$latest_notification"]}, 
                 "$latest_letter", 
                 "$latest_notification"]}}},
    {"$group":{_id:{month:{"$month": "$latest"}, 
                  year:{"$year": "$latest"}}, 
             active_users: {"$sum": "$customer_id"}
            }
    }
],{cursor:{batchSize:10}})