У меня есть коллекция событий, структура которых такова:
{
"_id" : ObjectId("537b3ff288f4ca2f471afcae"),
"Name" : "PREMISES MAP DELETED",
"ScreenName" : "AccessPointActivity",
"Timestamp" : NumberLong("1392113758000"),
"EventParams" : "null",
"TracInfo" : {
"ApplicationId" : "fa41f204bfc711e3b9f9c8cbb8c502c4",
"DeviceId" : "2_1VafJVPu4yfdbMWO1XGROjK6iQZhq4hAVCQL837W",
"UserId" : "pawan",
"SessionId" : "a8UHE16mowNwNGyuLXbW",
"WiFiAP" : "null",
"WiFiStrength" : 0,
"BluetoothID" : "null",
"BluetoothStrength" : 0,
"NetworkType" : "null",
"NetworkSubType" : "null",
"NetworkCarrier" : "Idea",
"Age" : 43,
"Gender" : "Female",
"OSVersion" : "16",
"Manufacturer" : "samsung",
"Resolution" : "600*976",
"Platform" : "Android",
"Latitude" : 40.42,
"Longitude" : -74,
"City" : "Monmouth County",
"CityLowerCase" : "monmouth county",
"Country" : "United States",
"CountryLowerCase" : "united states",
"Region" : "New Jersey",
"RegionLowerCase" : "new jersey",
"Time_zone" : "null",
"PinCode" : "07732",
"Locale" : ", Paradise Trailer Park",
"Accuracy" : 0,
"Timestamp" : NumberLong("1392113758000")
}
}
их много событий на разных экранах.
Мой ожидаемый результат выглядит следующим образом:
{
ApplicationId:"fa41f204bfc711e3b9f9c8cbb8c502c4",
EventName:"PREMISES MAP DELETED",
Eventcount:300,
ScreenviewCount:20,
DeviceCount:10,
UserCount:3
}
EventCount: это счет EventName
ScreenviewCount: это количество различных значений screenName за сеанс
DeviceCount: это количество отдельных устройств.
UserCount: это счет отдельного userCount
Их будет несколько событий на нескольких экранах (ScreenName).
В настоящее время я использую следующий подход:
Используя агрегацию, чтобы получить каждое имя события, и оно подсчитывает, например:
{
_id:
{
ApplicationId:"fa41f204bfc711e3b9f9c8cbb8c502c4",
EventName:"PREMISES MAP DELETED"
}
EventCount:300
}
Для каждого имени события из приведенного выше результата агрегирования я вызываю следующие запросы в цикле while, пока у агрегационного вывода не будет документов:
a) Отличительный запрос с использованием eventName из результата агрегации для подсчета количества просмотров (при сборе событий).
b) Различный запрос eventName из результата агрегации для подсчета устройств (при сборе событий).
c) Различный запрос eventName из результата агрегации для подсчета пользователей (при сборе событий).
И проблема в том, что он медленный, поскольку у него есть три разных запроса на каждый результат выхода агрегации.
Это их способ сделать это в одном вызове агрегации или что-то еще.
Заранее спасибо!!!
В общем случае, что вы, кажется, пропустили, состоит в том, что для получения "разных" значений различных полей в документе под итогами "события" вы можете использовать оператор $addToSet
.
"Набор" по определению имеет все значения "unique/different", поэтому вы просто хотите сохранить все эти возможные значения в "наборе" для вашего уровня группировки, а затем получить "размер" массива, который то, что делает оператор $size
введенный в MongoDB 2.6.
db.collection.aggregate([
{ "$group": {
"_id": {
"ApplicationId": "$TracInfo.ApplicationId",
"EventName": "$Name",
},
"oScreenViewCount": {
"$addToSet": {
"ScreenName": "$ScreenName",
"SessionId": "$TracInfo.SessionId",
}
},
"oDeviceCount": { "$addToSet": "$TracInfo.DeviceId" },
"oUserCount": { "$addToSet": "$TracInfo.UserId" },
"oEventcount": { "$sum": 1 }
}},
{ "$project": {
"_id": 0,
"ApplicationId": "$_id.ApplicationId",
"EventName": "$_id.EventName",
"EventCount": "$oEventCount",
"ScreenViewCount": { "$size": "$oScreenViewCount" },
"DeviceCount": { "$size": "$oDeviceCount" },
"UserCount": { "$size": "$oUserCount" }
}}
])
Версии pre MongoDB 2.6 требуют немного больше работы, используя $unwind
и $group
для подсчета массивов:
db.collection.aggregate([
{ "$group": {
"_id": {
"ApplicationId": "$TracInfo.ApplicationId",
"EventName": "$Name",
},
"oScreenviewCount": {
"$addToSet": {
"ScreenName": "$ScreenName",
"SessionId": "$TracInfo.SessionId",
}
},
"oDeviceCount": { "$addToSet": "$TracInfo.DeviceId" },
"oUserCount": { "$addToSet": "$TracInfo.UserId" },
"oEventcount": { "$sum": 1 }
}},
{ "$unwind": "$oScreeenviewCount" },
{ "$group": {
"_id": "$_id",
"oScreenviewCount": { "$sum": 1 },
"oDeviceCount": { "$first": "$oDeviceCount" },
"oUserCount": { "$first": "$oUserCount" },
"oEventcount": { "$first": "$oEventCount" }
}},
{ "$unwind": "$oDeviceCount" },
{ "$group": {
"_id": "$_id",
"oScreenviewCount": { "$first": "$oScreenViewCount" },
"oDeviceCount": { "$sum": "$oDeviceCount" },
"oUserCount": { "$first": "$oUserCount" },
"oEventcount": { "$first": "$oEventCount" }
}},
{ "$unwind": "$oUserCount" },
{ "$group": {
"_id": "$_id",
"oScreenviewCount": { "$first": "$oScreenViewCount" },
"oDeviceCount": { "$first": "$oDeviceCount" },
"oUserCount": { "$sum": "$oUserCount" },
"oEventcount": { "$first": "$oEventCount" }
}},
{ "$project": {
"_id": 0,
"ApplicationId": "$_id.ApplicationId",
"EventName": "$_id.EventName",
"EventCount": "$oEventCount",
"ScreenViewCount": "$oScreenViewCount",
"DeviceCount": "$oDeviceCount",
"UserCount": "$oUserCount"
}}
])
Конечное использование $project
во втором листинге и все общее использование префиксных имен "o" на самом деле просто для того, чтобы скомпоновать результат в конце и убедиться, что порядок выходных полей такой же, как в вашем примере.
Как общий отказ от ответственности, ваш вопрос не содержит информации для определения точных полей или комбинаций, которые используются для этих итогов, но принципы и подход являются обоснованными и должны быть достаточно близкими к одной и той же реализации.
По существу, вы получаете "отличные" значения внутри "группы", используя $addToSet
для любого поля или комбинации, а затем вы определяете "счет" этих "наборов" любыми способами, доступными для вас.
Гораздо лучше, чем выпуск многих запросов и объединение результатов в клиентский код.