MongoDB aggregate

本文介绍MongoDB中聚合查询的应用技巧,包括复杂条件筛选、关联查询、数组拆分、排序及分页等操作,并探讨了如何处理数据统计时遇到的空值问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

聚合查询

// aggregate里的各种查询可以组合使用

// 聚合查询
Entry.aggregate([ {
        //  $match 筛选,和find查询条件一致
        $match: { 
            $or: [{'showcase.type': CONSTANT.contestType.STANDARD}],
            'showcase.deleted': false,
            contest: mongoose.Types.ObjectId(req.params.contestId) // 筛选id的话,需要使用object进行转换
            // **需要和数据库中的字段类型一致 ObjectId parseInt...
        }
    },{
        // $lookup 可以多次调用
        $lookup: {
            from: 'trainingDocuments', //要查询的表名字
            localField: 'article', //在当前表(Entry)里的对应字段
            foreignField: '_id', //trainingDocuments表里article对应的字段
            as: 'articleDetail' //结果会在Entry表里添加一个articleDetail字段,内容是trainingDocuments表里查到的那条数据,新的articleDetail会被放在数组中,可以用$unwind拆开这个数组
        }
    }, {
        // $unwind 拆开数组
        $unwind: '$articleDetail' //如果articleDetail是数组,就会把数组拆开
    }, {
        $lookup: {
            from: 'files',
            localField: 'articleDetail.coverImage', //嵌套查询,使用嵌套查询条件'articleDetail.coverImage'时注意,要用$unwind把articleDetail拆开才能这样查询
            foreignField: '_id',
            as: 'articleDetail.coverImage'
        }
    }, {
        $unwind: '$articleDetail.coverImage'
    },{
        // $sort 排序
        $sort: {'createTime': -1} // 按指定字段排序
    },{
        // $limit 限制显示条数
        $limit: parseInt(limit * page) // 限制显示条数,结合$skip可以做分页
    },{
        // $skip 查询跳过多少条数据
        $skip: parseInt(limit * (page - 1)) // 每次跳过多少条数据
    }{
        // $group 分组做数据统计 擅长:按日期查询所有数据库中真实存在的数据统计; 不擅长:需要显示每周或者每天的数据(数据库中的数据 && 当天/月 为空为0的数据)数据统计
        $group: {
            _id: { // _id 查询条件
                article: '$article',
                name: '$name'
            },
            accessedTimes: {$sum: 1}, // $sum: 总共查询到几条数据
            firstAccessed: {$first: '$created'}, // $first:查询到的集合中第一条数据的created字段值
            lastAccessed: {$last: '$created'}, // $last:查询到的集合中最后一条数据的created字段值
            lastLocation: {$last: '$position'}, // $last:查询到的集合中最后一条数据的position字段值
            articleDetail: {$first: '$articleDetail'} // $first:查询到的集合中第一条数据的articleDetail字段值
        }
    }, {
        $project: { // 指定要显示的字段
            'articleDetail.title': 1,
            'articleDetail.coverImage.smallURL': 1,
            accessedTimes: 1,
            firstAccessed: 1,
            lastAccessed: 1,
            lastLocation: 1
        }
    }], (err, entries) => {
        // 查询之后回调
    }
);

针对 group/便 g r o u p , 经 常 有 统 计 需 求 , 按 周 统 计 每 天 的 数 据 , 但 是 当 某 天 数 据 为 空 / 零 时 便 不 会 被 group统计到,解决方案:

// 获得每周的起始时间
function _getWeeklyArray(startDate, endDate) {
    let resArr = [];
    let nextSunday = new Date(startDate.getTime() + ((7 - startDate.getDay()) * 24 * 60 * 60 * 1000));

    resArr.push({
        'weekStartTime': startDate,
        'weekEndTime': nextSunday
    });

    while (new Date(nextSunday.getTime() + (7 * 24 * 60 * 60 * 1000)) < endDate) {
        let lastSunday = new Date(nextSunday.getTime() + 24 * 60 * 60 * 1000);
        nextSunday = new Date(nextSunday.getTime() + (7 * 24 * 60 * 60 * 1000));
        resArr.push({
            'weekStartTime': lastSunday,
            'weekEndTime': nextSunday
        });
    }
    //when nextSunday < endDate,add reset timezone.
    if (nextSunday < endDate) {
        nextSunday = new Date(nextSunday.getTime() + 24 * 60 * 60 * 1000);
        resArr.push({
            'weekStartTime': nextSunday,
            'weekEndTime': endDate
        });
    }
    return resArr;
}

// 开始统计
let created = new Date(req.user.created).toJSON().split('T')[0],
    weekArray = driverInfoApiCtrl.getWeeklyArray(new Date(created.toString()), new Date());

Promise
.all(weekArray.map(weekDate => {
    return Order
        .aggregate([{
            $match: {
                created: {$gte: weekDate.weekStartTime, $lte: weekDate.weekEndTime}
            }
        }, {
            $group: {
                _id: {
                    year: {$year: '$created'},
                    yearMonthDay: {$dateToString: {format: "%Y-%m-%d", date: "$created"}}
                },
                trips: {$sum: 1},
                earns: {$push: '$transactionInfo.driverReceive'}
            }
        }])
        .then(orders => {
            let formatOrders = [];

            for (let i = new Date(weekDate.weekStartTime).getTime(); i <= new Date(weekDate.weekEndTime).getTime();) {
                formatOrders.push({
                    _id: new Date(i).toJSON().split('T')[0],
                    trips: 0,
                    earns: '0.00'
                });
                i += 24 * 60 * 60 * 1000;
            }

            formatOrders.forEach(formatOrder => {
                orders.forEach(order => {
                    if (formatOrder._id === order._id.yearMonthDay) {
                        formatOrder.trips = order.trips;
                        formatOrder.earns = _.sum(order.earns);
                    }
                });
            });

            return Promise.resolve(formatOrders);
        });
}))    

参考: MongoDB aggregate

MongoDB中,聚合(aggregate)是一种用于处理数据的功能,可以进行各种操作,如统计平均值、求和等。其中,使用aggregate函数是最常见的方法之一。通过使用aggregate函数,您可以将一系列操作(例如,$skip、$limit、$sort等)应用于数据集合来获取您所需的结果。 例如,如果您想跳过第一个文档并限制结果为2个文档,您可以使用以下代码: db.Ubisoft.aggregate([ { $skip: 1 }, { $limit: 2 } ]) 这将返回一个包含两个文档的结果集合,其中第一个文档被跳过了。 另外,如果您想按照时间字段进行降序排序,您可以使用以下代码: db.Ubisoft.aggregate([ { $sort: { time: -1 } } ]) 这将返回一个按照时间字段降序排列的结果集合。 通过使用聚合功能,您可以根据具体需求对MongoDB中的数据进行灵活处理和操作,以满足您的统计需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [MongoDB aggregate 运用篇个人总结](https://download.youkuaiyun.com/download/weixin_38590775/14837456)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [mongoDB中聚合(aggregate)的具体使用](https://blog.youkuaiyun.com/ycclydy/article/details/120861374)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值