首页 > 其他分享 >使用 Elasticsearch2.1 实现博客站内搜索

使用 Elasticsearch2.1 实现博客站内搜索

时间:2023-08-03 18:07:33浏览次数:52  
标签:syno 站内搜索 博客 JS ik elasticsearch Elasticsearch2.1 Elasticsearch type


一直以来,为了优化本博客站内搜索效果和速度,我使用 bing 的 site:

本着不折腾就浑身不自在的原则,我最终还是使用 Elasticsearch 搭建了自己的搜索服务。Elasticsearch 是一个基于 Lucene 构建的开源、分布式、RESTful 搜索引擎,很多大公司都在用,程序员的好伙伴 Github 的搜索也用的是它。本文记录我使用 Elasticsearch 搭建站内搜索的过程,目前支持中文分词、同义词、标题匹配优先等常见策略,请「点击这里」体验。

安装 Elasticsearch

部署 Elasticsearch 最简单的方法是使用 Elasticsearch Dockerfile。为了更彻底地折腾,我没有使用 Docker,好在手动安装过程也不复杂。

我的虚拟机和线上环境都是 Ubuntu 14.04.3 LTS,Elasticsearch 用的是目前最新的 2.1.1。一切开始之前,先要检查机器上是否装有 java 环境,如果没有可以通过以下命令安装:

sudo apt-get install openjdk-7-jre-headless

下载 Elasticsearch 2.1.1 压缩包并解压:

wget -c https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/zip/elasticsearch/2.1.1/elasticsearch-2.1.1.zip
unzip elasticsearch-2.1.1.zip

我将解压得到的 elasticsearch-2.1.1 目录重命名为 ~/es_root(名称及位置没有限制,可以将它挪到你认为合适的任何位置)。Elasticsearch 无需安装,直接可以运行:


SHELLcd



如果屏幕上没有打印错误信息,说明 Elasticsearch 服务已经成功启动。新建一个终端,用 curl 验证下:



SHELLcurl -XGET http://127.0.0.1:9200/?pretty

{
  "name" : "Goblyn",
  "cluster_name" : "elasticsearch",
  "version" : {
    "number" : "2.1.1",
    "build_hash" : "40e2c53a6b6c2972b3d13846e450e66f4375bd71",
    "build_timestamp" : "2015-12-15T13:05:55Z",
    "build_snapshot" : false,
    "lucene_version" : "5.3.1"
  },
  "tagline" : "You Know, for Search"



如果看到以上信息,说明一切正常,否则请根据屏幕上的错误信息查找原因。尽管 Elasticsearch 本身是用 java 写的,但它对外可以通过 RESTful 接口交互,十分方便。

默认情况下 Elasticsearch 的 RESTful 服务只有本机才能访问,也就是说无法从主机访问虚拟机中的服务。为了方便调试,可以修改 ~/es_root/config/elasticsearch.yml

network.bind_host: "0.0.0.0"
network.publish_host: _non_loopback:ipv4_

但线上环境切忌不要这样配置,否则任何人都可以通过这个接口修改你的数据。

安装 IK Analysis

Elasticsearch 自带的分词器会粗暴地把每个汉字直接分开,没有根据词库来分词。为了处理中文搜索,还需要安装中文分词插件。我使用的是 elasticsearch-analysis-ik,支持自定义词库。

首先,下载与 Elasticsearch 2.1.1 匹配的 elasticsearch-analysis-ik 插件。根据文档,当前需要使用 master 版:

wget -c https://github.com/medcl/elasticsearch-analysis-ik/archive/master.zip
unzip master.zip

解压后,进入插件源码目录编译:



SHELLsudo apt-get install maven cd


如果一切顺利,在 target/releases/ 目录下可以找到编好的文件。将其解压并拷到 ~/es_root

mkdir -p ~/es_root/plugins/ik/
unzip target/releases/elasticsearch-analysis-ik-1.6.2.zip -d ~/es_root/plugins/ik/

再将 elasticsearch-analysis-ik 的配置也拷贝到 ~/es_root

mkdir -p ~/es_root/config/ik
cp -r config/ik/* ~/es_root/config/ik/

elasticsearch-analysis-ik 的配置文件中很多都是词表,直接用文本编辑器打开就可以修改,改完记得保存为 utf-8 格式。

现在再启动 Elasticsearch 服务,如果看到类似下面这样的信息,说明 IK Analysis 插件已经装好了:

[plugins] [Libra] loaded [elasticsearch-analysis-ik]

配置同义词

Elasticsearch 自带一个名为 synonym 的同义词 filter。为了能让 IK 和 synonym 同时工作,我们需要定义新的 analyzer,用 IK 做 tokenizer,synonym 做 filter。听上去很复杂,实际上要做的只是加一段配置。

打开 ~/es_root/config/elasticsearch.yml


YAMLindex:
  analysis:
    analyzer:
      ik_syno:
          type: custom
          tokenizer: ik_max_word
          filter: [my_synonym_filter]
      ik_syno_smart:
          type: custom
          tokenizer: ik_smart
          filter: [my_synonym_filter]
    filter:
      my_synonym_filter:
          type: synonym
          synonyms_path: analysis/synonym.txt



以上配置定义了 ik_syno 和 ik_syno_smart 这两个新的 analyzer,分别对应 IK 的 ik_max_word 和 ik_smart 两种分词策略。根据 IK 的文档,二者区别如下:

  • ik_max_word:会将文本做最细粒度的拆分,例如「中华人民共和国国歌」会被拆分为「中华人民共和国、中华人民、中华、华人、人民共和国、人民、人、民、共和国、共和、和、国国、国歌」,会穷尽各种可能的组合;
  • ik_smart:会将文本做最粗粒度的拆分,例如「中华人民共和国国歌」会被拆分为「中华人民共和国、国歌」;

ik_syno 和 ik_syno_smart 都会使用 synonym filter 实现同义词转换。为了方便后续测试,建议创建 ~/es_root/config/analysis/synonym.txt

ua,user-agent,userAgent
js,javascript
internet explore=>ie

使用 JavaScript API

通过前面的示例,我们知道通过 curl 或者 Chrome 的 Postman 扩展能轻松地与 Elasticsearch 服务交互。为了更好与已有系统集成,我们还可以使用 Elasticsearch Client。Elasticsearch Client 只是将 RESTful 接口包装了一层,常见语言都有对应的实现(查看官方 Client),自己写一套也不难。

我的博客系统是 Node.js 写的,在项目里直接 npm install elasticsearch --save

无论进行什么操作,首先都需要实例化 Elasticsearch Client 对象:



JSvar elasticsearch = require('elasticsearch');

var client = new elasticsearch.Client({
    host: '10.211.55.23:9200', //服务 IP 和端口
    log: 'trace' //输出详细的调试信息



然后就可以调用 client 对象提供的各种方法了,client 对象拥有大量方法,请查看官方文档。这个库支持两种调用方式:callback 和 promise:


JS//callback
client.info({}, function(err, data){
    if(!err) {
        console.log('result:', data);
    } else {
        console.log('error:', err);
    }
});

//promise
client.info({}).then(function(data){
    console.log('result:', data);
}, function(err){
    console.log('error:', err);
});



为了节约篇幅,本文后续贴出的代码都采用 promise 写法,并且省略 then 函数。

全文搜索

到现在为止,所有准备工作都已经完成,马上就要大功告成了。在进行下一步之前,先简单介绍一下 Elasticsearch 几个名词:

Elasticsearch 集群可以包含多个索引(Index),每个索引可以包含多个类型(Type),每个类型可以包含多个文档(Document),每个文档可以包含多个字段(Field)。以下是 MySQL 和 Elasticsearch 的术语类比图,帮助理解:

MySQL

Elasticsearch

Database

Index

Table

Type

Row

Document

Column

Field

Schema

Mappping

Index

Everything Indexed by default

SQL

Query DSL

就像使用 MySQL 必须指定 Database 一样,要使用 Elasticsearch 首先需要创建 Index:

client.indices.create({index : 'test'});

这样就创建了一个名为 test


JSclient.indices.putMapping({
    index : 'test',
    type : 'article',
    body : {
        article: {
            properties: {
                title: {
                    type: 'string',
                    term_vector: 'with_positions_offsets',
                    analyzer: 'ik_syno',
                    search_analyzer: 'ik_syno',
                },
                content: {
                    type: 'string',
                    term_vector: 'with_positions_offsets',
                    analyzer: 'ik_syno',
                    search_analyzer: 'ik_syno',
                },
                slug: {
                    type: 'string',
                },
                tags: {
                    type: 'string',
                    index : 'not_analyzed',
                },
                update_date: {
                    type : 'date',
                    index : 'not_analyzed',
                }
            }
        }
    }
});


以上代码为 test 索引下的 article 类型指定了字段特征:title 和 content 字段使用 ik_syno 做为 analyzer,说明它使用 ik_max_word 做为分词,并且应用 synonym 同义词策略;slugtags 和 update_date 字段都没有指定 analyzer,说明他们使用默认分词;同时 tags 和 update_date

接着,写入测试数据并索引:



JSclient.index({
    index : 'test',
    type : 'article',
    id : '100',
    body : {
        title : '什么是 JS?',
        slug :'what-is-js',
        tags : ['JS', 'JavaScript', 'TEST'],
        content : 'JS 是 JavaScript 的缩写!',
        update_date : '2015-12-15T13:05:55Z',
    }
})


id 参数如果不指定,系统会自动生成一个并返回,后续在更新、删除时都要用到它。至于如何更新、删除,这里就不写了,请自行查看文档

搜一下试试:



JSclient.search({
    index : 'test',
    type : 'article',
    q : 'JS',
});


没有问题,可以搜出来!查询结果数量和具体内容都在 hits

result:
{"took":50,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.076713204,"hits":[{"_index":"test","_type":"article","_id":"100","_score":0.076713204,"_source":{"title":"什么是 JS?","slug":"what-is-js","tags":["JS","JavaScript","TEST"],"content":"JS 是 JavaScript 的缩写!","update_date":"2015-12-15T13:05:55Z"}}]}}

如果要实现更复杂的查询策略该怎么办?那就要请出前面表格中与 SQL 对应的 Query DSL 了。例如以下是本博客站内搜索所使用的 Query DSL:



JS{
    index : 'test',
    type : 'article',
    from : start,
    body : {
        query : { 
            dis_max : { 
                queries : [
                    {
                        match : {
                            title : { 
                                query : keyword, 
                                minimum_should_match : '50%',
                                boost : 4,
                            }
                        } 
                    }, {
                        match : {
                            content : { 
                                query : keyword, 
                                minimum_should_match : '75%',
                                boost : 4,
                            }
                        } 
                    }, {
                        match : {
                            tags : { 
                                query : keyword, 
                                minimum_should_match : '100%',
                                boost : 2,
                            }
                        } 
                    }, {
                        match : {
                            slug : { 
                                query : keyword, 
                                minimum_should_match : '100%',
                                boost : 1,
                            }
                        } 
                    }
                ],
                tie_breaker : 0.3
            }
        },
        highlight : {
            pre_tags : ['<b>'],
            post_tags : ['</b>'],
            fields : {
                title : {},
                content : {},
            }
        }
    }
}


from

result:
{"took":108,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.29921508,"hits":[{"_index":"test","_type":"article","_id":"100","_score":0.29921508,"_source":{"title":"什么是 JS?","slug":"what-is-js","tags":["JS","JavaScript","TEST"],"content":"JS 是 JavaScript 的缩写!","update_date":"2015-12-15T13:05:55Z"},"highlight":{"content":["<b>JS</b> 是 <b>JavaScript</b> 的缩写!"],"title":["什么是 <b>JS</b>?"]}}]}}

可以看到,同义词策略和关键词高亮功能都正常。跑通 Elasticsearch 基本流程,剩余工作就是导入更多数据、配置更多词表和尝试不同策略了,略过不写。

我接触 Elasticsearch 一共才几小时,我的出发点也很简单,只是为了给博客加上站内搜索,故本文既不全面也不深入,甚至还包含各种错误,仅供参考。Elasticsearch 功能十分强大和复杂,远远不是花几个小时就能玩明白的。最后推荐「Elasticsearch 权威指南(中文版)」这本书,非常细致和全面,我对 Elasticsearch 仅有的一点了解都来自于这本书和官方文档。

 


标签:syno,站内搜索,博客,JS,ik,elasticsearch,Elasticsearch2.1,Elasticsearch,type
From: https://blog.51cto.com/u_6186189/6951549

相关文章

  • Typecho建站:腾讯云轻量应用服务器搭建博客网站教程
    腾讯云轻量应用服务器自带Typecho应用模板镜像,腾讯云提供的Typecho模板镜像是基于CentOS7.664位操作系统,并已预置Nginx、PHP、MariaDB软件程序,使用Typecho应用模板可以快速搭建博客、企业官网、电商及论坛等各类网站。腾讯云服务器网分享使用腾讯云轻量应用服务器Typecho应用模板......
  • 基于JAVA的博客平台设计与实现
    随着互联网技术的快速发展,利用博客平台实现用户资讯的发布管理成为很多互联网公司研究的热门话题。通过对博客平台系统的网站功能需要进行讨论研究,这种资讯发布非常符合资讯发展的潮流,博客平台的实现将会对当前网站的信息推广产生不可估量的巨大浏览提升。正是鉴于此种对潜在用户主......
  • 最适合个人博客做的6种内容!你get了吗
    很多朋友私信looklook说自己很想试着开创一个属于自己的个人博客,但是又不知道从哪里下手比较好,不知道网友们喜欢看什么。今天looklook就从内容出发,把大家比较常在个人博客分享的内容列举出来,希望可以给到大家一个参考。推荐的个人博客内容1.记录生活:许多博主将博客视为日记本,用来记......
  • 博客园上传MD
     目录1、原因2、功能3、环境4、配置5、运行6、发布 1、原因最近在博客园重新开始发表博客。但是,自己不习惯博客园的默认编辑框,更喜欢Typora的markdown格式编写,因为它给我的第一印象就是简单方便,轻量极。所以现在我写文章都是在Typora中编写好之后,再复制到博......
  • halo博客从1.X迁移到2.X注意事项
    前言官方的迁移教程很详细:https://docs.halo.run/getting-started/migrate-from-1.x但是从我看官方文档到完成数据迁移,途中也吃了不少亏(昨天弄到半夜),所以写出这篇文档,避免更多的人踩坑本教程只针对于halo1.5/1.6Sakura主题迁移到halo2.8的踩坑记录,也许其他主题并不一样。且随......
  • 手把手教你从零开始搭建个人博客
    随着技术的进步和用户需求的变化,个人博客的形式和内容一直在不停地演变。为了给读者提供更丰富、有趣的阅读体验,搭建个人博客的网站一直在寻找更好的优化方法。所以现在出现了一批功能更完善的个人博客搭建软件,今天looklook就以HelpLook为例,一步步教大家怎么去搭建一个属于自己的个......
  • Hexo博客使用valine评论系统无效果及终极解决方案
    注意事项有一些博主valine评论系统无效果,有一些原因:1、很大程度是因为next的版本升级导致某些参数设置不同2、valine评论是基于LeanCloud,还有一个文章阅读次数功能也是用LeanCloud,两者会有一点冲突之后会给出一些解决方案评论系统选择Hexo可用的评论系统有很多,如下: 来必......
  • 【.NET6 + Vue3 + CentOS7.9 + Docker + Docker-Compose + SSL】个人博客前后端运维部
    个人博客前端:https://lujiesheng.cn个人博客后端:https://api.lujiesheng.cn个人博客运维:https://portainer.lujiesheng.cn1.服务器准备我采用的是腾讯云轻量应用服务器(2C4G8M80G),配置如下图:安装镜像选择CentOS7.664bit:2.服务器防火墙设置添加防火墙出入站规则......
  • Hexo主题Next配置algolia站内搜索
    Algolia是一家为网站与移动应用提供托管式搜索API的初创企业,成立于2012年,总部位于旧金山,曾参加过去年的YC训练营。网站或移动应用只需嵌入简单代码数分钟即可实现搜索功能。实现的效果如下search注册Algolia打开Algolia进行注册。可直接使用github账号进行注册。创建index点......
  • 每周博客2
    这周大概每天一小时到半小时Java。问题依旧和上周一样,对于代码不熟练,在后面的学习中,要更加勤快地练习,来熟悉这块缺口。通过这周课程学习以及课外的了解,我得到了一些心得体会。首先,兴趣是做任何一件事情的的前提,莎士比亚说过这么一句话,“学问必须合乎自己的兴趣,方才可以得益”。......