在mongodb聚合中将多个对象合并为一个对象
Posted
技术标签:
【中文标题】在mongodb聚合中将多个对象合并为一个对象【英文标题】:Merging multiple objects into a single object in the mongodb aggregation 【发布时间】:2021-12-18 05:05:51 【问题描述】:这就是我所处的场景。我有以下一种由聚合阶段返回的数组。为简单起见,删除了所有不必要的道具。该数组已经按日期排序,这意味着 clocked_in_at 属性将按照每个对象的创建时间排序。
[
"_id": "618192d4654484639c47fa2d",
"clocked_out_at": "2021-11-05T10:00:00.000Z",
"clocked_in_at": "2021-11-05T03:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90" // this is here as a string
,
"_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-05T11:00:00.000Z",
"clocked_in_at": "2021-11-05T04:00:00.000Z",
"visitor_id": "6182e4cea8b52121d01dff1b"
,
"_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-05T12:00:00.000Z",
"clocked_in_at": "2021-11-05T05:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
,
"_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-06T13:00:00.000Z",
"clocked_in_at": "2021-11-06T06:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
]
因此您可以看到第一个、第三个和最后一个对象来自同一个访问者,而第二个对象来自另一个访问者。所以我基本上需要的是根据visitor_id和日期/时间合并数组中的所有对象,并在同一天从数组中的最后一个现有值设置clocked_out_at值,如果这有意义的话。基本上,我们需要根据 clocked_in_at 值将它们分别分组。如果同一访问者处于不同的 clocked_in_at 日期,则它们仍应位于两个对象中。
所以预期的输出是这样的:
[
"_id": "618192d4654484639c47fa2d",
"clocked_out_at": "2021-11-05T12:00:00.000Z",
"clocked_in_at": "2021-11-05T03:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
,
"_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-05T11:00:00.000Z",
"clocked_in_at": "2021-11-05T04:00:00.000Z",
"visitor_id": "6182e4cea8b52121d01dff1b"
,
"_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-06T13:00:00.000Z",
"clocked_in_at": "2021-11-06T06:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
,
]
所以,在这里您可以看到原始数组中的第一个和第三个对象被合并了。因为他们对clocked_in_at 和visitor_id 有相同的日期(不考虑时间)。即使最后一个对象来自同一个visitor_id,它也没有合并,因为它是在11月6日,第二个对象很明显,它没有被合并,因为它有一个完全不同的visitor_id。
请注意,原始数组中第三个对象的clocked_out_at值被合并到结果合并对象中,即结果数组中第一个对象的clocked_out_at。
我不太确定这样做的可能性,但我很想知道我们是否有任何解决方案。我希望像$mergeObjects 或$group 这样的东西。我尝试了他们,但没有运气。
感谢您的宝贵时间,谢谢!
【问题讨论】:
【参考方案1】:如果clocked_in_at
和clocked_out_at
是字符串,我们可以使用$toDate 首先将它们转换为日期(这将使订购更容易)。如果它们已经是日期,我们可以跳过这一步。
然后我们可以$project 将每个打卡和打卡时间放入包含日期和值的对象数组中。 $dateTrunc 用于将clocked_in_at
和clocked_out_at
转换为天,然后$unwind 新创建的datetime
字段。现在我们可以每天$group ("$datetime.day") and
visitor_id`保持$min 及时和$max 每天出时间。我们可以再次$project 清理对象结构:
db.collection.aggregate([
// (Assuming strings not dates) Convert to DateTime
"$addFields":
"clocked_in_at":
"$toDate": "$clocked_in_at"
,
"clocked_out_at":
"$toDate": "$clocked_out_at"
,
"$project":
"s_id": "$s_id",
"visitor_id": "$visitor_id",
"datetime": [
"day":
"$dateTrunc":
"date": "$clocked_in_at",
"unit": "day"
,
"in": "$clocked_in_at"
,
"day":
"$dateTrunc":
"date": "$clocked_out_at",
"unit": "day"
,
"out": "$clocked_out_at"
]
,
"$unwind": "$datetime"
,
"$group":
"_id":
"visitor_id": "$visitor_id",
"day": "$datetime.day"
,
"s_id":
"$first": "$s_id"
,
"clocked_in_at":
"$min": "$datetime.in"
,
"clocked_out_at":
"$max": "$datetime.out"
,
"$project":
"_id": "$s_id",
"clocked_out_at": "$clocked_out_at",
"clocked_in_at": "$clocked_in_at",
"visitor_id": "$_id.visitor_id"
])
[
"_id": "6182552fde30e84900ba33fd",
"clocked_in_at": ISODate("2021-11-06T06:00:00Z"),
"clocked_out_at": ISODate("2021-11-06T13:00:00Z"),
"visitor_id": "6166c10965959d147c69aa90"
,
"_id": "618192d4654484639c47fa2d",
"clocked_in_at": ISODate("2021-11-05T03:00:00Z"),
"clocked_out_at": ISODate("2021-11-05T12:00:00Z"),
"visitor_id": "6166c10965959d147c69aa90"
,
"_id": "6182552fde30e84900ba33fd",
"clocked_in_at": ISODate("2021-11-05T04:00:00Z"),
"clocked_out_at": ISODate("2021-11-05T11:00:00Z"),
"visitor_id": "6182e4cea8b52121d01dff1b"
]
mongoplayground
注意_id
在提供的示例中不是唯一的,因此该字段被修改为s_id
:
[
"s_id": "618192d4654484639c47fa2d",
"clocked_out_at": "2021-11-05T10:00:00.000Z",
"clocked_in_at": "2021-11-05T03:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
,
"s_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-05T11:00:00.000Z",
"clocked_in_at": "2021-11-05T04:00:00.000Z",
"visitor_id": "6182e4cea8b52121d01dff1b"
,
"s_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-05T12:00:00.000Z",
"clocked_in_at": "2021-11-05T05:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
,
"s_id": "6182552fde30e84900ba33fd",
"clocked_out_at": "2021-11-06T13:00:00.000Z",
"clocked_in_at": "2021-11-06T06:00:00.000Z",
"visitor_id": "6166c10965959d147c69aa90"
]
这意味着初始的$project 需要更新为:
"$project":
"s_id": "$_id", // <- grab `_id` instead of `s_id`
【讨论】:
【参考方案2】:使用$group
db.collection.aggregate([
"$group":
"_id":
"clocked_in_at":
$dateTrunc:
date: "$toDate": "$clocked_in_at" ,
unit: "day"
,
"visitor_id": "$visitor_id"
,
"max": "$max": "$clocked_out_at" ,
"min": "$min": "$clocked_in_at",
"id": "$first": "$id"
,
"$project":
_id: "$id",
"visitor_id": "$_id.visitor_id",
"clocked_out_at": "$max",
"clocked_in_at": "$min"
])
mongoplayground
【讨论】:
以上是关于在mongodb聚合中将多个对象合并为一个对象的主要内容,如果未能解决你的问题,请参考以下文章