Получить несколько полей подсчета из одного запроса в MongoDB?

1

У меня есть коллекция событий, структура которых такова:

{
    "_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).

В настоящее время я использую следующий подход:

  1. Используя агрегацию, чтобы получить каждое имя события, и оно подсчитывает, например:

      {    
        _id:
        {
            ApplicationId:"fa41f204bfc711e3b9f9c8cbb8c502c4",
            EventName:"PREMISES MAP DELETED"    
        }
        EventCount:300    
    

    }

  2. Для каждого имени события из приведенного выше результата агрегирования я вызываю следующие запросы в цикле while, пока у агрегационного вывода не будет документов:

a) Отличительный запрос с использованием eventName из результата агрегации для подсчета количества просмотров (при сборе событий).

b) Различный запрос eventName из результата агрегации для подсчета устройств (при сборе событий).

c) Различный запрос eventName из результата агрегации для подсчета пользователей (при сборе событий).

И проблема в том, что он медленный, поскольку у него есть три разных запроса на каждый результат выхода агрегации.

Это их способ сделать это в одном вызове агрегации или что-то еще.

Заранее спасибо!!!

  • 0
    Выглядит возможно. Но очень неясно, что вы на самом деле запрашиваете для итогов. Я мог бы догадаться, отчасти, но я бы на самом деле угадал, что такое «вид экрана»? Это что-то значит для вас, но не ясно для всех нас. Лучше поделитесь кодом, который вы сейчас используете в своем вопросе, чтобы мы знали, какие поля используются для итогов.
  • 0
    Здравствуй! Нил Ланн: Я редактировал вопрос, надеюсь, теперь картина ясна. Спасибо!
Показать ещё 1 комментарий
Теги:
performance
aggregation-framework
mapreduce

1 ответ

6
Лучший ответ

В общем случае, что вы, кажется, пропустили, состоит в том, что для получения "разных" значений различных полей в документе под итогами "события" вы можете использовать оператор $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 для любого поля или комбинации, а затем вы определяете "счет" этих "наборов" любыми способами, доступными для вас.

Гораздо лучше, чем выпуск многих запросов и объединение результатов в клиентский код.

  • 0
    +1 @Neil, узнал новое использование $ addToSet в составной форме (oScreenviewCount)
  • 0
    @Neil Lunn m получает следующее сообщение об ошибке "исключение: аргумент $ size должен быть массивом, но имеет тип: EOO"
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню