美文网首页
thinkphp6的mongodb聚合操作使用笔记!

thinkphp6的mongodb聚合操作使用笔记!

作者: DragonersLi | 来源:发表于2021-09-29 16:22 被阅读0次

    宝塔安装mongodb后,在使用的php版本开启mongodb扩展。composer安装topthink/think-mongo发现没这个包,README说请使用最新版本的topthink/think-orm本仓库不再维护更新,发现项目composer.json已经有这个包了。

    [InvalidArgumentException]                                                                                                                         
      Could not find package topthink/think-mongo. 
    It was however found via repository search, which indicates a consistency issue with the repository. 
    

    mysql表结构:team_award团队奖励

    CREATE TABLE `pjh_user_team_award` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT ' ID',
      `uid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT ' 用户ID',
      `pid` text COLLATE utf8mb4_unicode_ci COMMENT '递归上级ID,形如:,508,',
      `upid` int(11) unsigned DEFAULT '0' COMMENT ' 直推上级ID',
      `tpid` int(11) unsigned DEFAULT '0' COMMENT ' 顶级上级ID',
      `mobile` varchar(11) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户手机号',
      `nickname` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '用户微信昵称',
      `vip` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否会员',
      `award` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '奖励',
      `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '金额',
      `cycle` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '周期(年月例如:202012)',
      `team_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '本周期团队总金额',
      `status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT ' 状态【0:未发奖励;1:已发奖励】',
      `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT ' 创建时间',
      `update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT ' 更新时间',
      PRIMARY KEY (`id`) USING BTREE,
      UNIQUE KEY `uni_uid_cycle` (`uid`,`cycle`) USING BTREE, 
      KEY `upid` (`upid`) USING BTREE,
      KEY `tpid` (`tpid`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT
    
    

    mongodb第一版设计文档结构:team_award团队奖励
    把mysql数据导入mongodb后,代码从mysql连接改成mongodb,然后加索引

    字段参考mysql表结构
    uid  int型  用户ID
    pid  string型  递归存储用户上级ID
    upid int型 上级ID
    tpid int型 顶级ID
    mine_money  float型 个人金额
    team_money float型  团队金额(包含个人金额)
    team_award  float型  团队奖励
    cycle  int型  当前年月(每月统计一次)
    status  int型  奖励发放状态【0:未发放;1:已发放】
    create_time int型 创建时间
    update_time int型 更新时间
    

    mongodb第二版设计文档结构:team_award团队奖励 但是考虑用户在增加,每个月一条用户信息,积累下来也是很大数据量。mongodb可以存储数组形式的子文档,改写成每个用户每年只有一条记录,把12个月份的数据变化的字段存储成子文档形式。thinkphp的mongodb文档资料太少,做个笔记,方便下次遇到同样问题,快速回忆。
    mysql中递归用户所有上级ID组成字符串 ,1403,1308,542,518,508,
    mongodb查询的时候字段类型严格区分。
    mongodb用正则匹配查询很慢,存数组建索引
    每条记录如:【id=>每一条记录,upid=>如果是第一个则存储,tpid=>如果是最后一个则存储

    字段参考mysql表结构
    uid  int型  用户ID
    pid  array型  【存储3个字段,递归上级ID,首尾各一个字段,再加上所有的ID分割成单独一条记录】
          id int型  每个ID
          upid int型 上级ID
          tpid int型 顶级ID
    cycle  array型 【存储8个字段,】    
          mine_money  float型 个人金额
          team_money float型  团队金额(包含个人金额)
          team_award  float型  团队奖励
          team_level  int型 团队等级
          cycle  int型  当前年月(每月统计一次)
          status  int型  奖励发放状态【0:未发放;1:已发放】
          create_time int型 创建时间
          update_time int型 更新时间
    create_time int型 创建时间
    update_time int型 更新时间
    
    
    mongodb聚合查询后台数据列表

    config/database.php配置mongodb数据库。然后mysql数据转存mongodb,用脚本循环读取数据批量插入mongodb

        // 数据库连接配置信息
        'connections'     => [
            'mysql' => [... ],
    
            // 更多的数据库配置信息
            'mongo' => [
                // 数据库类型
                'type'              => env('mongo.type', 'mongo'),
                // 服务器地址
                'hostname'          => env('mongo.hostname', '127.0.0.1'),
                // 数据库名
                'database'          => env('mongo.database', 'test'),
                // 用户名
                'username'          => env('mongo.username', 'root'), #
                // 密码
                'password'          => env('mongo.password', 'root'),#
                // 端口
                'hostport'          =>  env('mongo.hostport', 27017),
    
                // 数据库编码默认采用utf8
                'charset'           => env('mongo.charset', 'utf8mb4'),
                // 数据库表前缀
                'prefix'            => env('mongo.prefix', ''),
                //是否开启debug
                'debug'            => env('mongo.debug', true),
            ],
    

    连接操作:新建模型UserTeamAwardMongo指定连接名称和数据表名,mongodb可以插入数据时自动创建文档集合。所以表名不能固定,动态生成。

    <?php
    namespace app\common\model;
    use think\facade\{Db};
    class UserTeamAwardMongo extends BaseModel
    {
    
        protected $connection = 'mongo';//连接名称
        #protected $name = 'user_team_award';//连接数据表 
    
        /**
         * 动态表名每年一张表
         * 实例化的时候可以传递表名方便开发和测试
         */
        public function __construct($name = '',$cycle = 0){
            $this->name  = $name ? $name : 'user_team_award_'.date('Y');
            $this->cycle = intval($cycle ? $cycle : date('Ym',strtotime(' -1 month')));
        }
    
        /**
         * 集合文档名称
         *  Db::connect('mongo')->table(表全名)
         * @return mixed
         */
        public function  collection(){ 
            return Db::connect($this->connection)->name($this->name);
        }
    
        /**
         * 聚合操作
         * @param array $pipeline
         * @param false $bool【为真统计用,只返回一条记录;为假时返回全部记录】
         * @return int|mixed
         */
        public  function mongo($pipeline = [],$bool = false){
             $data = $this->collection()->cmd([
                        'aggregate'=>$this->name,
                        'pipeline'=>$pipeline,
                        'explain'=>false,
                    ]);
             return $bool ?  (!empty($data[0]) ?$data[0]: 0) : $data;
        }
    
    }
    

    thinkphp6下mongodb聚合操作使用:
    设计的文档主要是每月统计一次用户的团队奖励,奖励规则是月初统计上个月一整月的用户佣金,该用户的团队(无限子集包含自己)佣金,根据该团队佣金等级规则计算出团队奖励,并发放!

    aggregate带where条件的sum统计求和

    #原生写法   
    > db.award.aggregate([ { $match: { cycle: 202108,"pid.id":508 } }, { $group : { _id : null,  total : {$sum : "$money"} } } ])
    { "_id" : null, "total" : 57185.11 }
    > 
    #tp框架写法
    $this->model->where(['pid.id'=>508,'cycle'=>202108]) ->aggregate('sum','money');//统计求和金额#dd($this->model->getLastSql()); //打印sql 
    
    #打印sql
    db.runCommand({
    "aggregate":"team_award",
    "allowDiskUse":true,
    "pipeline":[{
    "$match":{"$and":[{"cycle":202108},{"pid.id":517}]}},
    {"$group":{"_id":null,"aggregate":{"$sum":"$money"}}
    }],
    "cursor":{}
    });
    

    查询某个用户的历史记录

    $this->model->collection()->withoutField('_id')->field('uid,pid,cycle')->where(['uid'=>508])->find();
    $this->model->mongo([
        [
            '$match'=>['uid' => 508],
        ],
        [
            '$unwind'=>'$cycle',
        ],
        [
            '$project'=>[
                    '_id'=>0,
                    'my_money'=>'$cycle.mine_money',
                    'team_money'=>'$cycle.team_money',
                    'team_award'=>'$cycle.team_award',
                    'team_level'=>'$cycle.team_level',
                    'cycle'=>'$cycle.cycle',
                    'status'=>'$cycle.status',
                    'create_time'=>'$cycle.create_time',
                    'update_time'=>'$cycle.update_time'
            ]
        ],
        [
            '$sort'=>['cycle'=>-1],
        ],
    
    ]);
    

    统计金额和记录数

    //聚合统计
    $this->model->mongo([
        [
            '$match'=>["uid"=>508], //自己 
            #'$match'=>["pid.upid"=>508], //直推下级 
            #'$match'=>["pid:id"=>508], //团队 
                   # new \MongoDB\BSON\Regex(",$uid,",'i');//团队,//where
        ],
        [
             //拆分子文档 
             '$unwind'=>'$cycle',
        ],
        [
            //子文档的字段值where匹配,当期自己佣金大于0的记录
            '$match'=>['cycle.cycle'=>202108,'cycle.mine_money'=>['$gt'=>0]],
        ],
        [
            //group分组:_id为null匹配所有记录
            //total为新起名的字段:$sum值为子文档字段表示求和该字段符合条件的记录,类似sum
            //count为新起名的字段:$sum值为1表示统计复合条件的记录总数,类似count
            '$group'=>['_id'=>null,'total'=>['$sum'=>'$cycle.mine_money'],'count'=>['$sum'=>1]],
        ],
        [
            //$project类似关系型数据库的字段过滤和起别名,字段值为0不显示为1显示;别名=>'$字段'
            '$project'=>['_id'=>0,'total'=>1,'count'=>1] //返回总金额和复合记录的总数
        ],
    
    ],1);
     
    
    
    
    #打印sql
    db.cmd({"aggregate":"team_award","pipeline":[{"$match":{"uid":508}},{"$unwind":"$cycle"},{"$project":{"_id":0,"mine_money":"$cycle.mine_money","team_money":"$cycle.team_money","team_award":"$cycle.team_award","team_level":"$cycle.team_level","cycle":"$cycle.cycle","status":"$cycle.status","create_time":"$cycle.create_time","update_time":"$cycle.update_time"}},{"$sort":{"cycle":-1}}],"explain":false});
    #原生sql查询
    db.team_award.aggregate( 
    {'$match':{'cycle.cycle':{'$eq':202108},'cycle.team_award':{"$gt":0}}},
    {'$unwind':'$cycle'},
    {'$match':{'cycle.cycle':{'$eq':202108},'cycle.team_award':{"$gt":0}}},
    {'$project':{"_id":'$cycle.cycle','uid':1,'team_award':'$cycle.team_award'}},
    {"$sort":{'cycle':-1}}
    )
    

    统计某年所有月份发放金额和人数

    $this->model->mongo([
        [
            '$match'=>['cycle.team_award'=>['$gt'=>0]],
        ],
        [
             '$unwind'=>'$cycle',
        ],
        [
            '$group'=>['_id'=>'$cycle.cycle','total'=>['$sum'=>'$cycle.team_award'],'count'=>['$sum'=>1]],
        ],
        [
            '$project'=>['_id'=>0,'cycle'=>'$_id','total'=>1,'count'=>1]
        ],
        [
            '$sort'=>['cycle'=>-1]
        ],
    ]);
    

    查看统计某期发放金额和人数总数和列表

    //某期符合条件的总数
    $match['cycle.team_award'] = ['$gt'=>0];
    $match['cycle.cycle'] = 202108;
    $count = $this->model->mongo([
        [
            '$match'=>$match,
        ],
        [
             '$unwind'=>'$cycle',
        ],
        [
            '$match'=>$match,
        ],
        [
            '$group'=>['_id'=>null,'total'=>['$sum'=>'$cycle.team_award'],'count'=>['$sum'=>1]],
        ],
        [
            '$project'=>['_id'=>0,'total'=>1,'count'=>1]
        ]
    ],1);
    
    //某期复合条件的列表
    $this->model->mongo([
        [
            '$match'=>$match,
        ],
        [
             '$unwind'=>'$cycle',
        ],
        [
            '$match'=>$match,
        ],
        [
            '$project'=>[
                '_id'=>0,
                'uid'=>'$uid',
                'cycle'=>'$cycle.cycle',
                'mine_money'=>'$cycle.mine_money',
                'team_money'=>'$cycle.team_money',
                'team_level'=>'$cycle.team_level',
                'team_award'=>'$cycle.team_award',
                'status'=>'$cycle.status',
                'create_time'=>'$cycle.create_time'
            ]
        ],
        [
            '$sort'=>['uid'=>1]
        ],
        [
            '$skip'=>($page-1)*10,
        ],
        [
            '$limit'=>10,
        ],
    ]);
    

    更新子文档数组字段

    //更新子文档一条记录
    $this->model->collection()
    ->where(['uid'=>508,'cycle.cycle'=>202108,'cycle.status'=>0])
    ->update(['cycle.$.status'=>1,'cycle.$.update_time'=>time()]);
    
    
    //更新子文档所有记录
    $this->model->collection()
    ->where(['uid'=>508,'cycle.cycle'=>202108,'cycle.status'=>0])
    ->update(['cycle.$[].status'=>1,'cycle.$[].update_time'=>time()]);
    
    

    相关文章

      网友评论

          本文标题:thinkphp6的mongodb聚合操作使用笔记!

          本文链接:https://www.haomeiwen.com/subject/htonyxtx.html