我有一个包含分析数据的集合(目前拥有超过1000万个文档)。从clicks
收集文档的 例子:重新设计数据库以消除聚合
{
serverId: 'srv1',
dateVisited: '2014-12-24',
campaignId: 'c1',
...
landingpageClicks: [
{...},
{...}
],
offerTrackings: [
{
amount: 10
},
{
amount: 22
}
{
amount: 18
}
]
}
我需要拉从这个集合的报告。用户可以请求通过多个字段进行分组,例如按日期组,然后通过serverId
,然后通过campaignId
和报告应该是这样的:
2014-12-24 | 50 lp clicks | 21 offer clicks | $600 // srv1 + srv2
srv1 | 20 lp clicks | 11 offer clicks | $400 // campaign1 + campaign2
campaign1 | 10 lp clicks | 6 offer clicks | $100
campaign2 | 10 lp clicks | 5 offer clicks | $300
srv2 | 30 lp clicks | 10 offer clicks | $200 // campaign3 + campaign4
campaign3 | 20 lp clicks | 4 offer clicks | $100
campaign4 | 10 lp clicks | 6 offer clicks | $100
目前我使用下面的查询拉报告,但它是极其缓慢:
db.clicks.aggregate([
{$match: {'_id.dateVisited': '2014-12-24'}},
{$group:{
_id: '$_id.dateVisited',
totalLandingpageClicksCount: {$sum: '$value.landingpageClicksCount'},
totalOfferTrackingsCount: {$sum: '$value.offerTrackingsCount'},
totalOfferTrackingsAmount: {$sum: '$value.offerTrackingsAmount'}
}}
])
我的想法是为每个可能的字段组合创建单独的集合,并使用find({<search + group fields>})
而不是聚合。即如果用户请求特定日期间隔,由serverId
分组,然后由campaignId
报告,下面的查询将被使用:
//example of doc in dateVisited_serverId collection
{
_id: {
dateVisited: '...',
serverId: '..'
},
value: {
<counts>
}
}
// get stats for date, grouped by serverId
db.dateVisited_serverId.find({
'_id.dateVisited': {
'$gte': dateFrom,
'$lte': dateTo
}
})
//example of doc in dateVisited_serverId_campaignId collection
{
_id: {
dateVisited: '...',
serverId: '..',
campaignId: '..'
},
value: {
<counts>
}
}
// get stats for date, grouped by serverId and then by campaignId
db.dateVisited_serverId_campaignId.find({
dateVisited: {
'$gte': dateFrom,
'$lte': dateTo
},
serverId: {$in: [<server ids from previous query>]}
})
它的工作,但clicks
收集有18场,所以我必须要产生集合来实现我的想法。
这样我需要为我的数据库找到另一个设计。
[更新]真正的文件的例子:
{
"_id": {
"dateVisited": ISODate("2014-11-05T00:00:00.0Z"),
"campaignId": "4c29dc888be98a9488e6876133852c72",
"landingpageId": "c5557aedab04ad1444b0ee28b5ddaab9",
"offerId": null,
"trafficAccountId": "84d06369b9872e9a2685483b7a532a10",
"serverId": "32",
"browser": "Safari",
"platform": "Android",
"c1": "chat",
"c2": "au",
"c3": "12b-ad1a",
"c4": "mtv2",
"city": "Perth",
"country": "Australia",
"deviceType": "mobile",
"isp": "Telstra Internet",
"netspeedId": NumberLong(3),
"set": ""
},
"value": {
"lpCount": 2,
"offersCount": 0,
"grandConversionCount": 0,
"grandConversionAmount": 0
}
}
一天有多少份文件?为什么在'_id.dateVisited'上进行分组而不是在'null'或其他常量值上进行分组 - 因为'$ match',所有文档都属于同一个组。与最简单的情况相比,展示更复杂/更逼真的例子会很好,因为它对流水线有很大的影响。为什么你不使用日期字段的日期?比起一个字符串,它会更快。你有什么指标,具体是什么?你需要'_id.dateVisited'上的索引。你能给我们解释一个缓慢的聚合吗? – wdberkeley
〜10 000每天。在'_id.dateVisited'上分组 - 因为我需要按日期分组。在实际收集日期字段中使用。我有'_id.dateVisited'索引 - $匹配工作相当快。但是如果用户选择1年作为日期范围,那么$ group必须处理'365 * 1000 =〜3,650,000'个文档。这是非常缓慢的 –