Database

db={"foo":[{name:"amy",ts:ISODate("2020-01-01T00:00:20Z"),o1:1,o2:"X1"},{name:"amy",ts:ISODate("2020-01-01T00:00:30Z"),o1:2,o2:"X2"},{name:"amy",ts:ISODate("2020-01-01T00:00:58Z"),o1:3,o2:"X3"},{name:"amy",ts:ISODate("2020-01-01T00:01:15Z"),o1:31,o2:"X31"},{name:"amy",ts:ISODate("2020-01-01T00:01:30Z"),o1:32,o2:"X32"},{name:"amy",ts:ISODate("2020-01-01T00:02:00Z"),o1:4,o2:"X4"},{name:"amy",ts:ISODate("2020-01-01T00:02:40Z"),o1:5,o2:"X5"},{name:"amy",ts:ISODate("2020-01-01T00:04:00Z"),o1:65,o2:"X65"},{name:"amy",ts:ISODate("2020-01-01T00:04:10Z"),o1:75,o2:"X75"},{name:"amy",ts:ISODate("2020-01-01T00:20:35Z"),o1:86,o2:"X86"},{name:"amy",ts:ISODate("2020-01-01T00:20:36Z"),o1:96,o2:"X96"},{name:"bob",ts:ISODate("2020-01-01T00:00:30Z"),o1:7,o2:"X7"},{name:"bob",ts:ISODate("2020-01-01T00:01:30Z"),o1:8,o2:"X8"},{name:"bob",ts:ISODate("2020-01-01T00:01:35Z"),o1:9,o2:"X9"}]}

Query

db.foo.aggregate([/** If appropriate, start with a $match stage here to cut down the amount* of material, especially dates. You probably do not want 1 min buckets* of everything from day 1. For now, no $match.* Ensure everything going in date order....*/{$sort:{ts:1}},/** Group by name and push the whole doc (which is in date order) onto* array 'a':*/{$group:{_id:"$name",a:{$push:"$$CURRENT"}}},/** Next, iterate over 'a' using $reduce and rebuild it with our 1 min* buckets, then overwrite the old 'a' using $addFields:*/{$addFields:{a:{$let:{/** Get first element of a in prep for setting init value...*/vars:{sd:{$arrayElemAt:["$a",0]}},in:{$reduce:{input:"$a",initialValue:{prev:"$$sd",/** the whole doc*/last:"$$sd.ts",/** last anchor date, e.g. start of 60 interval*/accum:[]},in:{$cond:[/** If the next ts < 60000 millis beyond anchor..*/{$lt:[{$subtract:["$$this.ts","$$value.last"]},60000]},/** then capture it as prev but let last anchor* and accumulated hits carry forward unchanged*/{prev:"$$this",last:"$$value.last",/** carry*/accum:"$$value.accum"/** carry*/},/** else capture it AND reset the anchor AND append the* previous value to the accum array (because at the this* point we "overran" the 60 interval). Note that* $concatArrays wants arrays, not docs as inputs so we* must wrap $$value.prev (which is a $$CURRENT doc) with []:*/{prev:"$$this",last:"$$this.ts",/** reset last to this one*/accum:{$concatArrays:["$$value.accum",["$$value.prev"]]}}]}}}}}}},/** Our use of $reduce will always leave the very last value (which* we always take) "dangling" in prev, so here we simply do one more concat.* We also take the oppty to both "lift" 'a.accum' to just 'a' and in so* doing get rid of 'prev' and 'last':*/{$addFields:{a:{$concatArrays:["$a.accum",["$a.prev"]]}}}])

Result