宝塔安装
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型 更新时间

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()]);
网友评论