MongoDB Совокупный запрос для расчета ARPU, когда пользователь и модель платежа разделены - PullRequest
0 голосов
/ 26 декабря 2018

Я хочу написать агрегированный запрос для расчета ARPU (среднего дохода на пользователя), но я застрял на том, как использовать данные для отдельных моделей, я знаю, что могу присоединиться к моделям с использованием $lookup, но не могу получитьподсчет всех пользователей таким образом.

Это мои модели:

// User:
{
    _id,
    username: String,
    os: String
}

и

// Payment:
{
    _id,
    user: ObjectId,
    price: Number
}

Мне нужно рассчитать сумму всех платежей, деленную на количествовсе пользователи.

После этого мне нужно сгруппировать их по ОС, то есть сумму всех платежей, произведенных пользователями, у которых os='os1', делится на количество всех пользователей, у которых os='os1' для каждой ОС

РЕДАКТИРОВАТЬ

например, пример данных для пользователя:

{
    "_id" : ObjectId("5b77fdffcbbd830011dbc04e"),
    "username" : "user1",
    "os" : "android"
},
{
    "_id" : ObjectId("5b7856756aaf56001120816b"),
    "username" : "user2",
    "os" : "android"
},
{
    "_id" : ObjectId("5b824fa7234d1b0010310522"),
    "username" : "user3",
    "os" : "android"
},
{
    "_id" : ObjectId("5b8a242444074c0074b8a318"),
    "username" : "user4",
    "os" : "ios"
},
{
    "_id" : ObjectId("5b7801b0cbbd830011dbc050"),
    "username" : "user5",
    "os" : "ios"
}

, а пример данных для платежа:

{
    "_id" : ObjectId("5bab61b617df7f173037fb1b"),
    "user" : ObjectId("5b77fdffcbbd830011dbc04e"), // user1
    "price" : 5
},
{
    "_id" : ObjectId("5bad4f980ab23100119300ec"),
    "user" : ObjectId("5b77fdffcbbd830011dbc04e"), // user1
    "price" : 10
},
{
    "_id" : ObjectId("5bad525edeed4e0011286842"),
    "user" : ObjectId("5b7856756aaf56001120816b"), // user2
    "price" : 5
},
{
    "_id" : ObjectId("5bad525edeed4e0011286848"),
    "user" : ObjectId("5b8a242444074c0074b8a318"), // user4
    "price" : 5
},
{
    "_id" : ObjectId("5bad525edeed4e0011286849"),
    "user" : ObjectId("5b8a242444074c0074b8a318"), // user4
    "price" : 15
}

Мой ожидаемый результат - это число, котороедостигается с помощью совокупного запроса.

Изначально мне нужен средний доход на пользователя:

ARPU: {sum of all payments} / {count of all users} = 40 / 5 = 8

Если я смогу этого добиться, тогда мне нужно сгруппировать их для каждой ОС:

ios-ARPU: {sum of payments ios users made} / {count of ios users} = 20 / 2 = 10

android-ARPU: {sum of payments android users made} / {count of android usrs} = 20 / 3 = 6.67

  • Обратите внимание, что некоторые пользователи могут не осуществлять какие-либо платежи, но их необходимо учитывать при расчете ARPU.

Ответы [ 2 ]

0 голосов
/ 26 декабря 2018

Используйте использование $facet, чтобы получить оба набора вычислений в одном конвейере после этапа $lookup.

Следующее демонстрирует использование $facet для вышеуказанной операции агрегирования:

db.getCollection('Payment').aggregate([
    { "$lookup": { 
        "from": "users",
        "localField": "user",
        "foreignField": "_id",
        "as": "user"
    } },
    { "$addFields": {
        "user": { "$arrayElemAt": ["$user", 0] }
    } },
    { "$facet": {
        "users": [
            { "$group": {
                "_id": null,
                "ARPU": { "$avg": "$price" }
            } }
        ],
        "os": [
            { "$group": {
                "_id": "$user.os",
                "ARPU": { "$avg": "$price" }
            } },
            { "$group": {
                "_id": null,
                "avgs": {
                    "$push": {
                        "k": { "$concat": ["ARPU", "-", "$_id"] },
                        "v": "$ARPU"
                    }
                } 
            } },
            { "$replaceRoot": {
                "newRoot": { "$arrayToObject": "$avgs" }
            } }
        ]
    } }
])

Это даст для заданных образцов документов:

{
    "users" : [ 
        {
            "_id" : null,
            "ARPU" : 8.0
        }
    ],
    "os" : [ 
        {
            "ios-ARPU" : 10.0,
            "android-ARPU" : 6.66666666666667
        }
    ]
}
0 голосов
/ 26 декабря 2018

Вы можете начать с $ lookup , а затем использовать $ reduce для расчета totalPayments на пользователя.Затем вы можете $ group по null, чтобы получить один документ с общим количеством пользователей и общим количеством платежей.Чтобы получить ARPU, вам нужно $ делить на последнем шаге.

db.users.aggregate([
    {
        $lookup: {
            from: "payments",
            localField: "_id",
            foreignField: "user",
            as: "payments"
        }
    },
    {
        $project: {
            _id: 1,
            os: 1,
            totalPayments: {
                $reduce: {
                    input: "$payments",
                    initialValue: 0,
                    in: {
                        $add: [ "$$value", "$$this.price" ]
                    }
                }
            }
        }
    },
    {
        $group: {
            _id: null,
            totalUsers: { $sum: 1 },
            totalPayments: { $sum: "$totalPayments" }
        }
    },
    {
        $project: {
            arpu: { $divide: [ "$totalPayments", "$totalUsers" ] }
        }
    }
])

Выходы: { "arpu" : 8 }

Чтобы получить arpu per os, вам просто нужно $group$os:

{
    $group: {
        _id: "$os",
        totalUsers: { $sum: 1 },
        totalPayments: { $sum: "$totalPayments" }
    }
}

Отпечатки:

{ "_id" : "ios", "arpu" : 10 }
{ "_id" : "android", "arpu" : 6.666666666666667 }
...