Агрегация MongoDB по документам с объектами - PullRequest
1 голос
/ 19 апреля 2020

У меня есть коллекция MongoDB с документами, которые выглядят так:

{
    'date': '2020-04-01', 
    'income': 1936.20, 
    'num_orders': 2.00,
    'avg_order_amount': 968.10
    'orders': [
        {
            'type': 'direct'
            'income': 1218.60, 
            'num_products': 3.00,
            'avg_product_price': 406.20
            'products': [ 
                { 
                    'name': 'product_1', 
                    'amount': 1.00
                }, 
                { 
                    'name': 'product_2', 
                    'amount': 2.00
                } 
            ] 
        },
        {
            'type': 'online'
            'income': 717.60, 
            'num_products': 4.00,
            'avg_product_price': 179.40
            'products': [ 
                { 
                    'name': 'product_2', 
                    'amount': 4.00
                } 
            ] 
        }
    ]
},
{
    'date': '2020-04-01', 
    'income': 2616.90, 
    'num_orders': 2.00,
    'avg_order_amount': 1308.45
    'orders': [
        {
            'type': 'direct'
            'income': 624.30, 
            'num_products': 2.00,
            'avg_product_price': 312.15
            'products': [  
                { 
                    'name': 'product_2', 
                    'amount': 2.00
                } 
            ] 
        },
        {
            'type': 'online'
            'income': 1992.6, 
            'num_products': 8.00,
            'avg_product_price': 249.08
            'products': [ 
                { 
                    'name': 'product_2', 
                    'amount': 4.00
                },
                { 
                    'name': 'product_3', 
                    'amount': 4.00
                } 
            ] 
        }
    ]
}

Я хотел бы получить агрегацию, которая приведет к чему-то вроде этого:

{
    'date': '2020-04-01', 
    'income': 4553.10, 
    'num_orders': 4.00,
    'avg_order_amount': 1138.28
    'orders': [
        {
            'type': 'direct'
            'income': 1842.9, 
            'num_products': 5.00,
            'avg_product_price': 368.58
            'products': [ 
                { 
                    'name': 'product_1', 
                    'amount': 1.00
                }, 
                { 
                    'name': 'product_2', 
                    'amount': 4.00
                } 
            ] 
        },
        {
            'type': 'online'
            'income': 2710.2, 
            'num_products': 12.00,
            'avg_product_price': 225.85
            'products': [ 
                { 
                    'name': 'product_2', 
                    'amount': 8.00
                },
                { 
                    'name': 'product_3', 
                    'amount': 4.00
                } 
            ] 
        }
    ]
}

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

Как мне сделать sh это?

Спасибо

1 Ответ

0 голосов
/ 19 апреля 2020

Вы можете использовать агрегацию ниже:

db.collection.aggregate([
    {
        $group: {
            _id: "$date",
            income: {  $sum: "$income" },
            num_orders: {  $sum: "$num_orders"  },
            avg_order_amount: {  $avg: "$avg_order_amount"  },
            orders: {  $push: "$orders"  },
            orderTypes: {  $push: "$orders.type" }
        }
    },
    {
        $project: {
            _id: 0,
            date: "$_id",
            income: 1,
            num_orders: 1,
            avg_order_amount: 1,
            orders: {
                $map: {
                    input: { $reduce: { input: "$orderTypes", initialValue: [], in: { $setUnion: [ "$$this", "$$value" ] } } },
                    as: "orderType",
                    in: {
                        $let: {
                            vars: { 
                                currentTypeOrders: { 
                                    $filter: { 
                                        input: { $reduce: { input: "$orders", initialValue: [], in: { $concatArrays: [ "$$this", "$$value" ] } } }, 
                                        cond: { $eq: [ "$$this.type", "$$orderType" ] } 
                                    }
                                }                                
                            },
                            in: {
                                avg_product_price: { "$avg": "$$currentTypeOrders.avg_product_price" },
                                income: { "$sum": "$$currentTypeOrders.income" },
                                num_products: { "$sum": "$$currentTypeOrders.num_products" },
                                type: "$$orderType",
                                products: {
                                    $let: {
                                        vars: {
                                            products: { $reduce: { input: "$$currentTypeOrders.products", initialValue: [], in: { $concatArrays: [ "$$this", "$$value" ] } } }
                                        },
                                        in: {
                                            $map: {
                                                input: { $setUnion: [ "$$products.name", [] ] },
                                                as: "product",
                                                in: {
                                                    $let: {
                                                        vars: {
                                                            currentProducts: { $filter: { input: "$$products", cond: { $eq: [ "$$this.name", "$$product" ] } } }
                                                        },
                                                        in: {
                                                            name: "$$product",
                                                            amount: { $sum: "$$currentProducts.amount" }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
])

Обычно мы можем использовать операторы $sum и $avg для верхнего уровня как часть оператора $ group .

Это становится более проблематичным, когда мы хотим вычислить такие агрегаты для вложенных массивов. Вы можете использовать $ reduce вместе с $ setUnion , чтобы сгладить массив массивов и получить уникальные значения, такие как direct, online.

Получив их, вы можете использовать $ let и $ filter , чтобы получить только те заказы, которые относятся к определенной категории c, и снова использовать $sum, $avg для вычислить значения для вложенного массива.

Один и тот же трюк должен быть выполнен дважды, поскольку массив products вложен в элементы другого массива.

Пн go Детская площадка

...