• 用Sphinx 建立搜索引擎


    1.       介绍

    实际上 sphinx的网站上的title 说的很清楚,这个是一个 “免费开源的SQL 全文索引搜索引擎”。当然,它不是一个完整的搜索引擎,只提供索引 查询接口。所以,学习sphinx 主要是要学习:如何建立索引,如何调用查询接口。

    他的作者只有一个人,但是,功能的确非常强大。目前,支持下面的特性:

    高速索引(10M/s, 主流cpu配置)

    高速查询(2-4G 文本,大概只要0.1s)

    排序采用的BM25 短语相似度 相结合的排序方法,而非向量空间模型。

    提供分布式搜索功能

    灵活的查询接口,支持布尔、短语、词语相似度等多种检索模式;

    可以支持单字节(GBK)和 utf8 编码的数据源

    当然,sphinx 也有很多的缺点:

    1.       分词比较难搞定,sphinx C 写的,要嵌入分词算法,还是要做很多的工作。

    一般现在用分词不是很准确的 LibMMSeg 进行切分。当然,分词不准确一般不会非常的影响搜索的体验。所以没有必要过于强调这一点。如果你要做的不是一个非常精致的搜索,sphinx 肯定是够用了。

     

    2.       大多数开源搜索都基本上基于纯文本搜索的理论。并没有对文本的结构进行深入的挖掘。比如pagerankhtml 标签标定的关键字,锚文字 等。如果,你要做一个比较好的垂直搜索引擎,可以考虑引入pagerank的机制,当然,存C sphinx 肯定会降低你的开发效率。

    说了这样多,只是想说和lucunece 相比,可扩展性差点。但是效率非常高。
     

    2.       安装
      http://faq.phpwind.net/answer-567

    上面这个链接中有一个很好的教程,一步一步的做下去就能安装成功了。下面补充一点东西:

     

    安装searchd 服务要用下面的命令:

    Searchd -–install –c /path/to/config

    注意 install 前面是两个 -”,c前面是一个 -

     

    还要一点要注意配置一个 stopwords 。方法是,建立一个stopwords 的列表,每个字用空格分开来,然后在每个index 的选项里面加一个 stopwords = /path/to/stopwords.txt

     

    3.       索引

    索引的技巧也在 http://faq.phpwind.net/answer-567 有一个例子,这个是一个典型的增量索引的例子。当然,增量索引的配置方法不只是一种。下面谈谈怎么更新增量索引。

     

    1.       10分钟,建立一次增量索引。千万不要每10分钟就合并一次索引,实际上,合并索引也非常的消耗时间。

    2.       每天的晚上可以更新全部的索引。

    增量索引不能反映删除 修改的内容,所以,建议主索引要经常的更新。一般来说,对于100万数据左右的情况,这样的方法都是可以的。

     

    但是,如果数据量还要大的话,就采用分段索引。一个索引 是基本上不会改动的索引,比如,一年前的帖子了。一个索引是最近一年的帖子,最后一个索引是增量索引。虽然这样,会同时 要从三个索引里面搜索,影响了性能,但是,可以保证近期的比如 10W数据 很容易的被更新。而一年前的数据可以考虑一个月索引更新一次。对三个索引,还可以设置权重,增量索引设置最大,因为是最新的内容,往往是热点,大家比较关注的,可以靠前点。近一年的数据,排名第二,最后一年以外的数据,基本过时,可以排后面一些。

     

     

    4.       搜索

    搜索其实从手册中已经很明白的说明了。

    下面要谈一下可能会出现问题的地方:

     

    1.搜索的模式:

    一般来说,比较常用的两种模式是:SPH_MATCH_ALL SPH_MATCH_ANY 但是 coreseek 分装的中文搜索有比较严重的问题,特别是 SPH_MATCH_ANY 是有bug的(我在GBK模式下发现是有bug的,这里有讨论:http://www.coreseek.cn/forum/2_171_0.html ),所以我选择用:SPH_MATCH_EXTENDED 模式来代替。如果是或者的关系,可以这样做:

    buildKeywords 先分词,然后再组合成一个查询表达式,进行查询。详细的看后面代码中的例子。

     

       2.搜索索引的参数:
       query 函数允许传递多个索引,不同的索引用 “|” 分开,但是,加亮函数只要传递主索引就好了,千万不要传多个。否则会返回false 。加亮函数中,还会传递一个关键字,如果是多个,就用 | 隔开。

    下面是我为verycms 写的一个简单的 搜索调用接口,提供大家参考:

    <?php
    class Cms_Search_API
    {
        
    private $host;

        
    private $port;

        
    private $db;

        
    private $indexs;

        
    private $searchd;

        
    private $keywords;

        
    private $total;

        
    function __construct($db = null, $host = 'localhost', $port = 3312, $indexs = "cmsindex|addcmsindex")
        {
            
    if ($db === null) {
                
    global $db;
            }
            
    $this->host = $host;
            
    $this->port = $port;
            
    $this->db = $db;
            
    $this->indexs = $indexs;
            
    $index = explode("|", $this->indexs);
            
    $this->mainIndex = $index[0];
            
    $this->searchd = new SphinxClient();
            
    $this->searchd->setServer($this->host, $this->port);
            
    $this->searchd->setMatchMode(SPH_MATCH_EXTENDED);
        }

        
    function setLimits($page = 1, $limit = 10)
        {
            
    $offset = ($page - 1* $limit;
            
    $this->searchd->setLimits($offset, $limit, 1000);
            
    return $this;
        }
        
        
    function setWhere($field, $value)
        {
            
    if (!is_array($value)) {
                
    $value = array($value);
            }
            
    $this->searchd->setFilter($field, $value);
            
    return $this;
        }

        
    /**
         * set how to sort the search result.
         *
         * @param int $mode SPH_SORT_ATTR_DESC or SPH_SORT_ATTR_ASC
         * @param string $sortby sort field.
         
    */
        
    function setSortMode($sortby = 'postdate', $mode = SPH_SORT_ATTR_DESC)
        {
            
    $this->searchd->setSortMode(SPH_SORT_ATTR_DESC, $sortby);
            
    return $this;
        }

        
    function find($query)
        {
            
    $result = $this->searchd->query($this->prepare($query), $this->indexs);
            
    $ids = array_keys($result['matches']);
            
    unset($result['matches']);
            
    $this->total = $result['total'];
            
    if (empty($ids)) {
                
    return false;
            }
            
    $data = $this->fetchContents($ids);
            
    return  $data;
        }
        
        
    function getTotal()
        {
            
    return $this->total;
        }
        
        
    function getList($query, $page = 1, $sortby = null, $limit = 10, $sort_mode = SPH_SORT_ATTR_DESC)
        {
            
    $this->setLimits($page);
            
    if ($sortby) {
                
    $sort_mode = empty($sort_mode? SPH_SORT_ATTR_DESC : $sort_mode;
                
    $this->setSortMode($sortby, $sort_mode);
            }
            
    if ($result = $this->find($query)) {
                
    $this->buildExcerpts($result, "content");
                
    $this->buildExcerpts($result, "title");
            }
            
    return $result;
        }

        
    function buildExcerpts(&$data, $field = "content")
        {
            
    $contents = array();
            
    foreach ($data as $item)
            {
                
    $item = trim(strip_tags($item[$field]));
                
    $contents[] = preg_replace("/\s+/", "", $item);
            }
            
    $contents = $this->searchd->buildExcerpts($contents, $this->mainIndex, $this->keywords);
            
    foreach ($data as $k => &$v) {
                
    $v[$field= $contents[$k];
            }
            
    return $data;
        }

        
    /**
         * fetch content from database;
         *
         * @param array $ids  tids
         * @return array  search result
         
    */
        
    private function fetchContents($ids)
        {
            
    $q = "SELECT th.tid,th.cid, th.title, th.postdate, th.hits, t.content, t.author, th.linkurl , th.url 
                    FROM cms_contentindex th
                    LEFT JOIN cms_content1 t
                    USING (tid)
                    WHERE th.tid in (
    " . implode(",", $ids. ")";
            
            
    $result = $this->db->query($q);
            
    $data = array();
            
            
    $score = array_flip($ids);
            
    $score_sort = array();
            
    while ($line = $this->db->fetch_array($result))
            {
                
    $line['score'= $score[$line['tid']];
                
    $data[] = $line;
                
    $score_sort[] = $line['score'];
            }
            
    array_multisort($score_sort, SORT_ASC, SORT_NUMERIC, $data);
            
    return $data;
        }

        
    /**
         * prepare the search query, and then use SPH_MATCH_EXTENDED mode to query.
         *
         * @param string $query string to search
         * @return string of extend style.
         
    */
        
    private function prepare($query)
        {
            
    $keywords = $this->searchd->buildKeywords($query, $this->mainIndex, false);
            
    $query = array();
            
    foreach ($keywords as $key) {
                
    $query[] = $key["tokenized"];
            }
            
            
    $query = implode("|", $query);
            
    $query = iconv("utf-8", "gbk", $query);
            
    $this->keywords = $query;
            
    return $query;
        }
    }
    ?>

    下面是调用的例子:

    <?php
    require_once "global.php";
    require_once "require/cms_search_api.php";

    $search = new Cms_Search_API($db);
    $page = 1;
    $sortby = 'postdate'//hits or null
    $search->setWhere("cid", 5);
    $result = $search->getList("提升6-12个月宝宝记忆力的游戏", $page, $sortby);
    print_r($result);
    ?>

     

  • 相关阅读:
    投票通过,PHP 8 确认引入 Union Types 2.0
    Laravel 菜鸟的晋级之路
    给公司写的composer包开发的规范
    Swoft 源码剖析
    听说PHP的生成器yield处理大量数据杠杠的
    读懂JWT的使用,你就会用PHP如何实现了
    python标准库及其它应用
    python常用算法题
    python迭代器实例
    python生成器实例
  • 原文地址:https://www.cnblogs.com/niniwzw/p/1582630.html
Copyright © 2020-2023  润新知