1. 要求
- 对文章内容进行关键词检索,找到符合的关键词的文章
- 对数据库表优化(针对大型项目),进行分表读写数据(本次创建32个表)
2. 思路
- 创建32张数据表(art0-31),包含字段(art_id,guid,cat_id,title,content,pubtime),根据需要自己可以添加
- 创建索引表(index_art),包含字段(guid,id,content)这块可以把数据表名称也加上,好定位到那张表,根据需要可以添加,这里要把content创建全文索引
- 这里如何让索引表和数据表进行关联,因为每个数据表都有自增长art_id,都是从1开始,无法和索引表进行关联,所以需要引入uuid,即每个文章都有一个自己uuid,索引表也对应文章的uuid
- 创建了32张数据表,那么如何实现减轻数据库压力呢,怎么把数据随机插入到表中,这里使用了对uuid的处理,即先对uuid进行md5加密处理转行成16进制,然后利用substr提取前两位字符,再用hexdec函数将其转换成10机制,让最后结果和32取余数,余数和art拼接就是插入的表名
- 当插入数据时,还要同时插入到索引表中(最好用数据库事务处理),因为文章内容可能是中文,英文或者混合,这里需要判断下,如果是含有中文就需要对中文进行处理,通过中文分词字典,分出词语,并用空格分开,英文的话直接插入(自带空格)
- 当读取修改删除数据时,都要用到uuid,所以就不能用自增长id了(跳转页面时的参数)。
- 当提交检索时,也要对语句进行判断处理,然后用全文索引语句,在index_art索引表中搜索,存在数据,取得uuid,然后利用uuid找到对应的表,取得数据
代码
- 数据表结构:
//artadd.php
//获取guid
$uuid4 = Uuid::uuid4();
$index['guid']=$art['guid']=$uuid4->toString();
//获取uuid,md5加密之后再获取前两位数字,最后转换成10进制,
$num=hexdec(substr(md5($uuid4->toString()),0,2));
$yu=$num%32;
$table='art'.$yu;
~
~
//当插入成功后,在索引表中添加数据
//检查内容是什么类型(1->英文,2->中文)
$cont=isWhat($art['content']);//自己封装的函数
if($cont==1){
$index['content']=$art['content'];
}elseif($cont==2){
//利用fenci中文字典更改内容
//实例化字典
$se=new segment();
//调用方法
$index['content'] = $se->get_keyword($art['content']);
}else{
//利用fenci中文字典更改内容
//实例化字典
$se=new segment();
//调用方法
$index['content'] = $se->get_keyword($art['content']);
$words=split_en_str($art['content'],false);
$index['content'].=" ".implode(' ',$words);
}
//插入index_art索引表中
inUp('index_art',$index,'insert');//自己封装的插入函数
- 修改数据(删除比较简单就不写了)
//artedit.php
//获取到需要改的数据
$num=hexdec(substr(md5($_GET['id']),0,2));
$yu=$num%32;
$table='art'.$yu;
$sql="select guid,title,tags,content,cat_id from ".$table." where guid='".$_GET['id']."'";
$art=find($sql);
~
~
//当有post提交时
$num=hexdec(substr(md5($_POST['guid']),0,2));
$yu=$num%32;
$table='art'.$yu;
//更新成功后把索引文件中的content字段进行更新
//检查内容是什么类型(1->英文,2->中文)
$cont=isWhat($art['content']);
if($cont==1){
$index['content']=$art['content'];
}elseif($cont==2){
//利用fenci中文字典更改内容
//实例化字典
$se=new segment();
//调用方法
$index['content'] = $se->get_keyword($art['content']);
}else{
//利用fenci中文字典更改内容
//实例化字典
$se=new segment();
//调用方法
$index['content'] = $se->get_keyword($art['content']);
$words=split_en_str($art['content'],false);
$index['content'].=" ".implode(' ',$words);
}
//插入index_art索引表中
inUp('index_art',$index,'update',"guid='".$_POST['guid']."'");
查数据
//artlist.php
//分页操作
$num='';
for($i=0;$i<32;$i++){
$sql="select count(*) from art".$i;
$num+=find($sql)['count(*)'];
}
$cnt=10;
$curr=isset($_GET['page'])? $_GET['page'] : '1';
//调用分页函数,进行分页
$page=getPage($num,$cnt,$curr);//自己封装的函数
//获取art表的所有文章
$rs=[];
for($i=0;$i<32;$i++){
$sql="select title,guid,art_id,pubtime,catname from art".$i." left join cat on cat.cat_id=art".$i.".cat_id";
//echo $sql.'<br>';
$a=getAll($sql);
foreach($a as $v){
$rs[]=$v;
}
}
$rs=array_slice($rs,($curr-1)*$cnt,$cnt);//html页面去遍历$rs
~
~
//artlist.html
//遍历文章
<?php foreach($rs as $k=>$v){?>
<tr>
<td><?php echo ($k+1);?></td>
<td><?php echo date('Y-m-d',$v['pubtime'])?></td>
<td><a href="#"><?php echo $v['title']?></a></td>
<td><?php echo $v['catname']?></td>
<td>
<a href="./artdel.php?id=<?php echo $v['guid']?>">删除</a>|
<a href="./artedit.php?id=<?php echo $v['guid']?>">修改</a>
</td>
</tr>
<?php } ?>
//分页
<?php if(isset($page)){ ?>
<div id="pagebar" align='center' style='font-size:20px'>
共<?php echo $num;?>条
<?php foreach($page as $k=>$v){
if($k==$curr){
?>
<span>[<?php echo $k;?>]</span>
<?php }else{?>
<a href='artlist.php?<?php echo $v; ?>'><?php echo $k; ?></a>
<?php }
}
?>
</div>
<?php }?>
当提交查询时
//index.php
//检查内容是什么类型(1->英文,2->中文)
$cont=isWhat($_POST['search']);//自己封装函数
if($cont==1){
$search=$_POST['search'];
}elseif($cont==2){
//利用fenci中文字典更改内容
//实例化字典
$se=new segment();
//调用方法
$search = $se->get_keyword($_POST['search']);
}else{
//利用fenci中文字典更改内容
//实例化字典
$se=new segment();
//调用方法
$search = $se->get_keyword($_POST['search']);
$words=split_en_str($_POST['search'],false);
$search.=" ".implode(' ',$words);
}
//利用全文索引的语句查询
$sql="select guid from index_art where MATCH(content) AGAINST('".$search."' IN BOOLEAN MODE)";
$rs=getAll($sql);
$art=[];
if(!empty($rs)){
foreach($rs as $v){
$num=hexdec(substr(md5($v['guid']),0,2));
$yu=$num%32;
$table='art'.$yu;
$sql="select guid,pubtime,title,content,catname from ".$table." inner join cat on $table.cat_id=cat.cat_id where guid='".$v['guid']."'";
//获取到当前guid的值
$one=find($sql);
//将关键字分割为数组
$key_word=explode(' ',$search);
//获取关键词出现的次数
$sql="select content from index_art where guid='".$v['guid']."'";
//var_dump(find($sql));die;
$content=find($sql)['content'];
$n='';
foreach($key_word as $v1){
$n+=substr_count($content,$v1);
//将数据中的关键词高亮显示
$one['content']=preg_replace("/($v1)/i","<font color=red><b>\\1</b></font>",$one['content']);
}
$one['num']=$n;
//将num插入到$one中
$art[]=$one;
//利用冒泡法根据num排序
bubble_sort($art);
}
//页面去遍历$art
}
使用到封装的函数
/**
*
*分页函数
*当$max>=5默认显示5个条页码,<5正常显示
*@param int $num文章数
*@param int $cnt每页显示条数
*@param int $curr当前页
*@return array 返回拼接的每页的url参数
*/
function getPage($num,$cnt,$curr){
//获取最大页码
$max=ceil($num/$cnt);
//判断显示页码的最左边的位置
$left=max($curr-2,1);
//最右边页码
$right=min($left+4,$max);
//存在一种情况是,当共9页,当前处于8页,页面只会显示4个分页,所以需要根据$right重新定义下left
$left=max($right-4,1);
//将获取到的页码放到数组中返回,因为对于index页面,存在两个查询,一个时根据栏目查询(有参数cat_id),一个查询吃总的栏目,所以需要用http_build_query()函数来保留原有的参数
for($i=$left;$i<=$right;$i++){
$_GET['page']=$i;//模拟url输出格式是?page=$i,与原有的参数拼接
$page[$i]=http_build_query($_GET);
}
return $page;
}
/**
*
*判断字符串时全英文,全中文,或者都有
*@param string $str1 需要检查的字符串
*@return int 英文->1 中文->2 混合->3
*/
function isWhat($str1){
$strA= trim($str1);
$lenA= strlen($strA); //检测字符串实际长度
$lenB= mb_strlen($strA, "utf-8"); //文件的编码方式要是UTF8
if($lenA=== $lenB) {
return"1";//全英文
}else {
if($lenA% $lenB== 0) {
return"2";//全中文
}else {
return"3";//中英混合
}
}
}
/**
*
*匹配英文单词
*@param string $str 需要匹配的字符
*@param bool $distinct 是否去除重复值
*@return array 返回所有单词的索引数组
*/
function split_en_str($str,$distinct=true) {
preg_match_all('/([a-zA-Z]+)/',$str,$match);
if ($distinct == true) {
$match[1] = array_unique($match[1]);
}
sort($match[1]);
return $match[1];
}
/**
*
*冒泡法排序
*
*
*/
function bubble_sort(& $arr){
$number=count($arr);
for($i=0;$i<$number-1;$i++){
for($j=0;$j<$number-1-$i;$j++){
if($arr[$j]['num']<$arr[$j+1]['num']){
$tmp=$arr[$j];
$arr[$j]=$arr[$j+1];
$arr[$j+1]=$tmp;
}
}
return $arr;
}
}