增强typecho的搜索功能

栏目: Python · 发布时间: 8年前

内容简介:增强typecho的搜索功能

科学空间是使用typecho程序搭建的博客,侧边栏提供了搜索功能,然而搜索功能仅仅是基于字符串的全匹配查找,因此导致很多合理的查询都没法得到结果,比如“2018天象”、“新词算法”都没法给出结果,原因就是文章中都不包含这些字符串。

于是就萌生了加强搜索功能的想法,之前也有读者建议过这个事情。这两天搜索了一下,本来计划用 Python 下的Whoosh库来建立一个全文检索引擎,但感觉整合和后期维护的工作量太大,还是放弃了。后来想到在typecho自身的搜索上加强,在公司同事(大佬)的帮助下,完成了这个改进。

由于是直接修改typecho源文件实现的改进,因此如果typecho升级后就可能被覆盖,因此在这里做个备忘。

I. 探索

通过在 Github 检索我发现,typecho的搜索功能是在 var/Widget/Archive.php 中实现的,具体代码大概在1185~1192行:

if (!$hasPushed) {
            $searchQuery = '%' . str_replace(' ', '%', $keywords) . '%';
            /** 搜索无法进入隐私项保护归档 */

            $select->where('table.contents.password IS NULL')
            ->where('table.contents.title LIKE ? OR table.contents.text LIKE ?', $searchQuery, $searchQuery)
            ->where('table.contents.type = ?', 'post');
        }

可见,搜索结果是通过在 SQL 中匹配keywords返回的,其中%是SQL中的通配符。因此我们还发现,如果我们输入查询语句是自带空格的话,那么空格也会被替换成通配符,这样搜索起来就灵活一点。

因此很自然的一个想法是,不管查询语句有没有空格,我们人工对查询语句进行分词,然后用通配符连接分词结果,从而实现在没有空格的情况下也更灵活搜索。这确实是我实践的第一个思路。然而这样做存在的问题是:尽管进行了分词,然而还是要匹配完所有的词才出结果,如果有一个词在博客中从未出现过,那么就匹配不到了。于是要想更好,那么需要考虑每个词都只是候选词而不是必选词的做法。

II. 实践

为了实现上述目的,我用Python写了个http接口,放到服务器上,这个http接口负责分词并生成SQL语句,然后改写上述代码为

if (!$hasPushed) {
             $url = 'http://127.0.0.1:7777/token?text=' . $keywords;
             $searchQuery = file_get_contents($url);

             /** 搜索无法进入隐私项保护归档 */
             $select->where('table.contents.password IS NULL')
             ->where($searchQuery . ' > 0')
             ->where('table.contents.type = ?', 'post')
             ->order($searchQuery, Typecho_Db::SORT_DESC);
         }

其中接口 http://127.0.0.1:7777/token?text= 是Python程序:

#! -*- coding:utf-8 -*-

import bottle
import jieba
jieba.initialize()

def convert(s):
    ws = jieba.cut(s)
    search = []
    for i in ws:
        search.append('2*SIGN(INSTR(table.contents.title, "%s"))'%i)
        search.append('SIGN(INSTR(table.contents.text, "%s"))'%i)
    return '(%s)'%(' + '.join(search))

@bottle.route('/token', method='GET')
def token_home():
    text = bottle.request.GET.get('text')
    if not text:
        text = ''
    return convert(text)

if __name__ == '__main__':
    bottle.run(host='0.0.0.0', port=7777, server='gunicorn')

这个Python程序返回SQL语句的算分部分,具体算法是:先分词,如果文章标题中包含一个词,那么加2分,如果文章内容包含一个词,加1分,最后算个总分,用到的函数SIGN、INSTR等,大家百度一下就知道了。这里推荐一下,用bottle这个轻量级的库写http接口是非常方便的~

还有要修改的是:因为我们修改的 php 部分,用了 order($searchQuery, Typecho_Db::SORT_DESC); 来希望按分数降序排列。然而这不会直接生效,因为typecho中默认全部按时间降序排列,因此我们还要修改同一个文件的1396~1397行,将原来是

$select->order('table.contents.created', Typecho_Db::SORT_DESC)
        ->page($this->_currentPage, $this->parameter->pageSize);

改为

if (strpos($select, 'INSTR') === false) {
             $select->page($this->_currentPage, $this->parameter->pageSize)
             ->order('table.contents.created', Typecho_Db::SORT_DESC);
         } else {
             $select->page($this->_currentPage, $this->parameter->pageSize);
         }

大概意思是判断一下是不是搜索语句,如果是的话,那么就不按时间排列;如果不是的话,就按时间排列。直接去掉按时间排列是不行的,因为这一句也包含了首页的输出,首页的输出必须按照时间排序。

III. 结语

为什么要用这种Python和PHP结合的方案,而不纯写成PHP版?没错,写成纯PHP也可以,结巴分词的确也有PHP版,然而最重要的问题是我不会PHP!而且PHP版的结巴也需要额外配置,略麻烦。像这样用Python对我来说就简单多了,如果有什么要改进的,修改Python脚本即可。

最后,也许有使用者会担心这么粗暴的解决方法会不会存在效率问题,事实上,如果文章多达几十万的话,那么上述做法肯定有很严重的效率问题,然而对于一个只有几百篇文章的博客来说,这个问题并不需要考虑了。

终于可以用更自由地搜索了~欢迎大家更多的建议。

转载到请包括本文地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Art and Science of Java

The Art and Science of Java

Eric Roberts / Addison-Wesley / 2007-3-1 / USD 121.60

In The Art and Science of Java, Stanford professor and well-known leader in CS Education Eric Roberts emphasizes the student-friendly exposition that led to the success of The Art and Science of C. By......一起来看看 《The Art and Science of Java》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具