增强typecho的搜索功能

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

内容简介:增强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脚本即可。

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

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

转载到请包括本文地址:

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

查看所有标签

猜你喜欢:

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

Mastering Flask

Mastering Flask

Jack Stouffer / Packt Publishing / 2015-9-30 / USD 49.99

Work with scalable Flask application structures to create complex web apps Discover the most powerful Flask extensions and learn how to create one Deploy your application to real-world platforms......一起来看看 《Mastering Flask》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具