【问题标题】:Group records based on another collection with date range relation基于具有日期范围关系的另一个集合对记录进行分组
【发布时间】:2018-09-01 15:10:35
【问题描述】:

场景:

我有一个集合来保存 sessionLogs,其中包含以下属性:

  • 用户ID
  • 设备类型
  • 开始日期
  • 结束日期

我还有另一个 actionLogs 集合:

  • 用户ID
  • 设备类型
  • createdAt
  • 动作

我不能在 actionLogs 上引用 sessionLog,或者在 sessionLog 记录中的子集合上添加 actionLogs...这是因为应用程序的性质。 (我认为对于当前的问题没有必要解释这个)

问题:

我需要获取具有特定 actionLog 的 sessionLogs 的 LIST。 (以及其他过滤器,如日期、用户等...)

我如何仅通过执行查询来获得这样的结果?

我认为该组应该使用日期周期(startDate、endDate)、userId 和 deviceType。

例子:

会话日志

  • 用户 ID:x |设备类型:ios |开始日期:2018-3-21 | endDate 2018-3-22

与该 SessionLog 相关的操作日志

  • 用户 ID:x |设备类型:ios |日期:2018-3-21 |行动:X
  • 用户 ID:x |设备类型:ios |日期:2018-3-22 |行动:是的

【问题讨论】:

  • 您能否为每个集合粘贴 2 个文档的样本? startDate 和 endDate 是 Date 对象还是字符串类型?
  • 我现在不在我的开发环境中复制和粘贴示例,抱歉。但是日期属性是日期对象,而不是字符串。
  • 没关系。如果我理解得很好,它应该是这样的吗? gist.github.com/felipsmartins/09b5b85e1bf1e9615a8772cd172e6f71
  • 是的,正是……

标签: mongodb mongodb-query


【解决方案1】:

好吧,如果我理解得很好,这将通过使用$lookup$unwind(可选)阶段来实现。
由于查找已为我们“合并/加入”集合,因此您可以通过$match 阶段查询任何条件,并通过$group 阶段aggregation pipeline 进行分组:

我们考虑以下集合:

var sessionsLog = [
    {

        'userId': "132",
        'deviceType': "ios",
        'startDate': ISODate('2018-02-23'),
        'endDate': ISODate('2018-02-28')
    },
    {

        'userId': "789",
        'deviceType': "android",
        'startDate': ISODate('2018-02-15'),
        'endDate': ISODate('2018-02-19')
    },
    {

        'userId': "225",
        'deviceType': "ios",
        'startDate': ISODate('2018-03-01'),
        'endDate': ISODate('2018-03-17')
    }
];

var actionsLog = [
    {

        'userId': '225',
        'deviceType': 'ios',
        'createdAt': ISODate('2018-03-03'),
        'action': 'X'
    },
    {

        'userId': '789',
        'deviceType': 'android',
        'createdAt': ISODate('2018-02-16'),
        'action': 'Y'
    },
    {

        'userId': '789',
        'deviceType': 'android',
        'createdAt': ISODate('2018-02-16'),
        'action': 'Z'
    }
];

db.actionLogs.insert(actionsLog); //via mongo shell...
db.sessionLogs.insert(sessionsLog); //via mongo sheell...

第 1 步:加入集合

这是$lookup 运算符派上用场的时候。在这里,我们将加入 userId 字段的集合,但您想要什么取决于您按字段“连接”集合!

db.sessionLogs.aggregate([
    {
        $lookup: {
            from: 'actionLogs',
            foreignField: 'userId',
            localField: 'userId',
            as: 'action_log'
        }
    }  
])

第 2 步:过滤文档

我需要获取具有特定操作日志的会话日志列表。 (以及其他过滤器,如日期、用户等...)

一旦我们加入了集合,就应该很容易使用 $match 运算符按特定条件获取/过滤文档。
但在此之前,我认为在$match 舞台之前添加$unwind 舞台确实会使工作变得更容易(但根本不需要),所以它会是:

db.sessionLogs.aggregate([
    {
        $lookup: {
            from: 'actionLogs',
            foreignField: 'userId',
            localField: 'userId',
            as: 'action_log'
        }
    },
    {
        $unwind: '$action_logs'
    }  
])

然后它返回:

  {
        "_id": ObjectId("5ab463233a856b4829f5e75d"),
        "userId": "789",
        "deviceType": "android",
        "startDate": ISODate("2018-02-15T00:00:00Z"),
        "endDate": ISODate("2018-02-19T00:00:00Z"),
        "action_log": {
            "_id": ObjectId("5ab463363a856b4829f5e760"),
            "userId": "789",
            "deviceType": "android",
            "createdAt": ISODate("2018-02-16T00:00:00Z"),
            "action": "Y"
        }
    },
    {
        "_id": ObjectId("5ab463233a856b4829f5e75d"),
        "userId": "789",
        "deviceType": "android",
        "startDate": ISODate("2018-02-15T00:00:00Z"),
        "endDate": ISODate("2018-02-19T00:00:00Z"),
        "action_log": {
            "_id": ObjectId("5ab463363a856b4829f5e761"),
            "userId": "789",
            "deviceType": "android",
            "createdAt": ISODate("2018-02-16T00:00:00Z"),
            "action": "Z"
        }
    },
    {
        "_id": ObjectId("5ab463233a856b4829f5e75e"),
        "userId": "225",
        "deviceType": "ios",
        "startDate": ISODate("2018-03-01T00:00:00Z"),
        "endDate": ISODate("2018-03-17T00:00:00Z"),
        "action_log": {
            "_id": ObjectId("5ab463363a856b4829f5e75f"),
            "userId": "225",
            "deviceType": "ios",
            "createdAt": ISODate("2018-03-03T00:00:00Z"),
            "action": "X"
        }
    }

现在我们可以像这样过滤文档(action 等于 X):

db.sessionLogs.aggregate([
    {
        $lookup: {
            from: 'actionLogs',
            foreignField: 'userId',
            localField: 'userId',
            as: 'action_log'
        }
    },
    {
        $unwind: '$action_log'
    },
    {
        $match: {'action_log.action': 'X' }
    }

])

同样,通过为聚合管道添加$group 阶段进行分组应该很容易!

旁注:

  • $lookup$unwind 仅适用于 mongodb 3.2+
  • 请注意,在 pipeline 中添加 $lookup 阶段的工作方式类似于左外连接。
  • 不要将上述示例视为“规则”,这只是一个指导方针,您很可能会根据自己的要求进行更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    • 2018-12-27
    相关资源
    最近更新 更多