合并 mongoid/mongo 中两个表的结果

Combining results of two tables in mongoid/mongo

大家好,什么是合并两个 mongoid 查询结果的最佳方式。

我的问题是我想知道活跃用户,用户可以发送信件和通知,两者是分开的 table 如果用户发送信件或通知被视为活跃.我想知道的是每个月有多少活跃用户。

目前我能想到的就是这样做

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 版本。

the customer created a new notification, or a new letter, and I would like to get the first created at of either.

让我们首先解决这个问题作为基础。给定文档架构示例如下:

Letter 集合中的文档架构:

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

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

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

您可以利用 aggregation pipeline $lookup to join the two collections. For example using 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 的输出是 LetterNotification 中按 created_at 字段排序的客户列表。示例输出文档:

  {
    "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")
  }, ...

What I want to know is how many active users were there per month

为了实现这个,你可以只替换最后一个阶段($sort) of the above aggregation pipeline with $group。例如:

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}})

示例输出如下:

  {
    "_id": {
      "month": 10,
      "year": 2017
    },
    "active_users": 9
  },
  {
    "_id": {
      "month": 1,
      "year": 2018
    },
    "active_users": 18
  },