MongoDB-根据数组中是否存在值来计算文档之间的时间差?

MongoDB-根据数组中是否存在值来计算文档之间的时间差?,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我试图计算两个文档之间的时间差,但我不确定如何根据数组中是否存在值来计算 让我再详细解释一下。假设我在一个集合中有五个文档:A、B、C、D、E 每个文档都有参考键、时间戳和人员字段 persons数组中的每个元素都有personType字段以及其他字段: A: { referenceKey: 1, timestamp: ISODate, persons: [ { personType: "ALICE", ... }, { personType: "BOB"

我试图计算两个文档之间的时间差,但我不确定如何根据数组中是否存在值来计算

让我再详细解释一下。假设我在一个集合中有五个文档:
A、B、C、D、E

每个文档都有
参考键
时间戳
人员
字段
persons
数组中的每个元素都有
personType
字段以及其他字段:

A: { referenceKey: 1, timestamp: ISODate, persons: [ { personType: "ALICE", ... }, { personType: "BOB", ... } ] }
B: { referenceKey: 1, timestamp: ISODate, persons: [ { personType: "ALICE", ... }, { personType: "BOB", ... } ] }
C: { referenceKey: 1, timestamp: ISODate, persons: [ { personType: "BOB", ... } ] }
D: { referenceKey: 1, timestamp: ISODate, persons: [ { personType: "ALICE", ... }, { personType: "BOB", ... } ] }
E: { referenceKey: 1, timestamp: ISODate, persons: [ { personType: "BOB", ... } ] }
我想要实现的是计算类型为
ALICE
的人每次访问花费了多少时间
换句话说,这应该计算并返回一个时间差数组:

[{timeSpent:C.timestamp-A.timestamp},{timeSpent:E.timestamp-D.timestamp}]
以下是要测试的示例集合:

[
{
时间戳:ISODate(“2019-04-12T20:00:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
},
{
人物类型:“爱丽丝”
}
]
},
{
时间戳:ISODate(“2019-04-12T20:10:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
}
]
},
{
时间戳:ISODate(“2019-04-12T21:00:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
},
{
人物类型:“爱丽丝”
}
]
},
{
时间戳:ISODate(“2019-04-12T21:15:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
}
]
},
{
时间戳:ISODate(“2019-04-12T21:20:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
}
]
},
{
时间戳:ISODate(“2019-04-12T21:45:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
},
{
人物类型:“爱丽丝”
}
]
},
{
时间戳:ISODate(“2019-04-12T22:05:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
},
{
人物类型:“爱丽丝”
}
]
},
{
时间戳:ISODate(“2019-04-12T23:00:00.000Z”),
参考键:1,
人员:[
{
人物类型:“鲍勃”
}
]
},
{
时间戳:ISODate(“2019-04-12T18:30:00.000Z”),
参考键:2,
人员:[
{
人物类型:“鲍勃”
},
{
人物类型:“约翰”
}
]
}
]
我想我可以根据person type
ALICE
的存在,使用
$in
添加一个新的布尔字段
hasAlice
。但问题是每次访问都要计算花费的时间,所以我不能只使用
$reduce
来计算总时间。我是否可以通过
hasAlice
字段更改以某种方式使用
$group
,然后使用
$reduce

到目前为止,我已经尝试过(但失败了):

db.collection.aggregate([
{
“$match”:{//我也会按时间戳和引用键进行筛选,但这与问题无关
时间戳:{
“$gte”:ISODate(“2019-04-12T00:00:00.000Z”),
“$lt”:ISODate(“2019-04-12T23:59:00.000Z”)
},
参考键:1
}
},
{
“$project”:{
_id:0,
时间戳:1,
哈萨利斯:{
“$in”:[
“爱丽丝”,
“$persons.personType”
]
}
}
},
{
“$sort”:{
时间戳:1
}
}
])
我想要的是:

[
{timeSpent:10},//以分钟为单位
{timeSpent:15},
{timeSpent:75},
]
运行聚合时实际得到的信息:

[
{
“hasalise”:true,//1.访问开始
“时间戳”:ISODate(“2019-04-12T20:00:00Z”)
},
{
“hasalise”:false,//1.访问结束
“时间戳”:ISODate(“2019-04-12T20:10:00Z”)
},
{
“hasalise”:对,//2.访问开始
“时间戳”:ISODate(“2019-04-12T21:00:00Z”)
},
{
“hasAlice”:false,//2.访问结束
“时间戳”:ISODate(“2019-04-12T21:15:00Z”)
},
{
“hasAlice”:错,
“时间戳”:ISODate(“2019-04-12T21:20:00Z”)
},
{
“Hasalise”:对,//3.访问开始
“时间戳”:ISODate(“2019-04-12T21:45:00Z”)
},
{
“hasAlice”:正确,//注意:有一些误导性的文件,如这些文件(例如文件B)
“时间戳”:ISODate(“2019-04-12T22:05:00Z”)
},
{
“hasalise”:false,//3.访问结束
“时间戳”:ISODate(“2019-04-12T23:00:00Z”)
}
]
我不知道我的逻辑是否正确,或者我是否可以通过某种方式减少这些文档来计算每次访问所花费的时间。但任何帮助都是非常感谢的。
提前谢谢。

多亏了你,我终于弄明白了

诀窍是使用
$lookup
将集合与其自身左联接,然后从联接的集合中获取不包含person类型
ALICE
的第一个元素。加入集合中的此元素为我们提供每次访问的结束(即
leaveTimestamp

在此基础上,我们可以在每次就诊结束时进一步
$group
,只选择匹配文档的第一个
时间戳
,以便消除任何误导性文档(例如文档
B

以下是完整的
聚合
管道:

db.collection.aggregate([
  {
    "$match": {
      timestamp: {
        "$gte": ISODate("2019-04-12T00:00:00.000Z"),
        "$lt": ISODate("2019-04-12T23:59:00.000Z")
      },
      referenceKey: 1,
      persons: {
        "$elemMatch": {
          "personType": "ALICE"
        }
      }
    }
  },
  {
    "$project": {
      timestamp: 1,
      hasAlice: {
        "$in": [
          "ALICE",
          "$persons.personType"
        ]
      }
    }
  },
  {
    $lookup: {
      from: "collection",
      let: {
        root_id: "$_id"
      },
      pipeline: [
        {
          "$match": {
            timestamp: {
              "$gte": ISODate("2019-04-12T00:00:00.000Z"),
              "$lt": ISODate("2019-04-12T23:59:00.000Z")
            },
            referenceKey: 1
          }
        },
        {
          "$project": {
            timestamp: 1,
            hasAlice: {
              "$in": [
                "ALICE",
                "$persons.personType"
              ]
            }
          }
        },
        {
          $match: {
            $expr: {
              $gt: [
                "$_id",
                "$$root_id"
              ]
            }
          }
        }
      ],
      as: "tmp"
    }
  },
  {
    "$project": {
      _id: 1,
      timestamp: 1,
      tmp: {
        $filter: {
          input: "$tmp",
          as: "item",
          cond: {
            $eq: [
              "$$item.hasAlice",
              false
            ]
          }
        }
      }
    }
  },
  {
    "$project": {
      timestamp: 1,
      leaveTimestamp: {
        $first: "$tmp.timestamp"
      }
    }
  },
  {
    "$group": {
      "_id": "$leaveTimestamp",
      "timestamp": {
        "$min": "$timestamp"
      },
      leaveTimestamp: {
        $first: "$leaveTimestamp"
      }
    }
  },
  {
    $addFields: {
      "visitingTime": {
        $dateToString: {
          date: {
            $toDate: {
              $subtract: [
                "$leaveTimestamp",
                "$timestamp"
              ]
            }
          },
          format: "%H-%M-%S"
        }
      }
    }
  },
  {
    "$sort": {
      "timestamp": 1
    }
  }
])