中文分詞+數據庫分表

1. 要求

  1. 對文章內容進行關鍵詞檢索,找到符合的關鍵詞的文章
  2. 對數據庫表優化(針對大型項目),進行分表讀寫數據(本次創建32個表)

2. 思路

  1. 創建32張數據表(art0-31),包含字段(art_id,guid,cat_id,title,content,pubtime),根據需要自己可以添加
  2. 創建索引表(index_art),包含字段(guid,id,content)這塊可以把數據表名稱也加上,好定位到那張表,根據需要可以添加,這里要把content創建全文索引
  3. 這里如何讓索引表和數據表進行關聯,因為每個數據表都有自增長art_id,都是從1開始,無法和索引表進行關聯,所以需要引入uuid,即每個文章都有一個自己uuid,索引表也對應文章的uuid
  4. 創建了32張數據表,那么如何實現減輕數據庫壓力呢,怎么把數據隨機插入到表中,這里使用了對uuid的處理,即先對uuid進行md5加密處理轉行成16進制,然后利用substr提取前兩位字符,再用hexdec函數將其轉換成10機制,讓最后結果和32取余數,余數和art拼接就是插入的表名
  5. 當插入數據時,還要同時插入到索引表中(最好用數據庫事務處理),因為文章內容可能是中文,英文或者混合,這里需要判斷下,如果是含有中文就需要對中文進行處理,通過中文分詞字典,分出詞語,并用空格分開,英文的話直接插入(自帶空格)
  6. 當讀取修改刪除數據時,都要用到uuid,所以就不能用自增長id了(跳轉頁面時的參數)。
  7. 當提交檢索時,也要對語句進行判斷處理,然后用全文索引語句,在index_art索引表中搜索,存在數據,取得uuid,然后利用uuid找到對應的表,取得數據

代碼

  1. 數據表結構:
1 (4).png
3.png
//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');//自己封裝的插入函數
  1. 修改數據(刪除比較簡單就不寫了)
//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;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容