一直以来,为了优化本博客站内搜索效果和速度,我使用 bing 的 site:
站内搜索做为数据源,在服务端获取、解析、处理并缓存搜索结果,直接输出 HTML。这个方案唯一的问题是时效性难以保证,尽管我可以在发布和修改文章时主动告诉 bing,但它什么时候更新索引则完全不受我控制。
本着不折腾就浑身不自在的原则,我最终还是使用 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 ~/es_root/bin/
chmod a+x elasticsearch
./elasticsearch
如果屏幕上没有打印错误信息,说明 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 elasticsearch-analysis-ik-master/
mvn package
如果一切顺利,在 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
文件,输入一些同义词并存为 utf-8 格式。例如:
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 的 Node.js 包。
无论进行什么操作,首先都需要实例化 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 的术语类比图,帮助理解:
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
的 Index。Type 不用单独创建,在创建 Mapping 时指定就可以。Mapping 用来定义 Document 中每个字段的类型、所使用的 analyzer、是否索引等属性,非常关键。创建 Mapping 的代码示例如下:
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 同义词策略;slug
、tags
和 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
参数指定从开始跳过多少条结果,用来实现分页。这份复杂的 Query DSL 搜出来的结果如下:
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 仅有的一点了解都来自于这本书和官方文档。
本文链接:https://imququ.com/post/elasticsearch.html
相关推荐
十分钟学会使用 Elasticsearch 优雅搭建自己的搜索系统。 什么是elasticsearch Elasticsearch 是一个开源的高度可扩展的全文搜索和分析引擎,拥有查询近实时的超强性能。 大名鼎鼎的Lucene 搜索引擎被广泛用于搜索...
提供ESAPI jar包下载 esapi-2.1.0.1.jar
今天小编就为大家分享一篇关于JAVA使用Elasticsearch查询in和not in的实现方式,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
Spring Boot结合Jest实现对ElasticSearch的全文检索,分词检索,分页,搜索结果高亮关键词,多字段检索 PageController中的搜索方法里面是全套的,分词,分页,高亮等都包含,数据格式个es-head中创建索引的索引在...
esapi-2.1.0.1.zip(esapi-2.1.0.1.jar)
searchEngine 是基于 ElasticSearch 和 Java 实现的搜索引擎系统,实现关键字高亮搜索、添加文本等功能。 该项目集成了 Spring Boot、ElasticSearch、RestHighLevelClient、Vue.js、Element-ui、Log4j 和 Fastjson ...
通过环境部署、索引配置、数据导入、索引错误处理等步骤,厦门大学图书馆成功实现了基于 Elasticsearch 的站内搜索,且在功能、界面等方面有所改进。 据此进一步提出,Elasticsearch 未来可应用于大数据环境,成为...
springboot整合elasticsearch7,进行数据同步。elasticsearch相关度查询、排序。高亮显示;自动补全等功能。代码仅供参考,代码中有具体的注释,可以根据代码及注释内容,对自己项目架构及业务进行修改、整合。
es也是用Java开发并使用Lucene作为其核心来实现所有索引和搜索功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。es基本是开箱即用(解压就可以用!),非常简单。Solr安装...
ElasticSearch 作为本站的站内搜索框架,支持分词检索数据和关键词高亮。 模板引擎采用 FreeMarker,为了考虑 SEO 没有使用 js 框架,后期会考虑将后台改为 Vue 实现,敬请期待。 前台页面还是一如既往的使用本站...
elasticsearch7.17.10-最新支持Java1.8版本
ElasticSearch是一个基于ApacheLucene构建的开源搜索服务器。它提供了一个可扩展的搜索解决方案,内置支持近实时搜索和多租户。 《ElasticSearch 可扩展的开源弹性搜索解决方案》将教你如何构建一个快速、灵活、可...
简易博客检索系统使用前后端分离,前端使用 Vue ,后端使用 SpringBoot 数据库 MySQL 检索使用 ElasticSearch 同步数据使用 logstash 基于Springboot + ElasticSearch +Vue+MySQL构建的博客检索系统 项目经过严格...
分享一套完整版视频课程——分布式搜索引擎Elasticsearch开发实战基础篇 (ElasticSearch、ELK、搜索引擎、Lucene),本教程旨在带领大家进入搜索引擎领域,从无到有,深入浅出的讲解了什么是搜索引擎,搜索引擎的...
全文搜索引擎ElasticSearch介绍与使用
一般来说我们开发Elasticsearch会选择使用集成springboot,在网上找的springboot集成elasticsearch文章几乎都是extends ElasticsearchRepository的方式来实现。但是新版本ElasticsearchRepository里的方法基本上已经...
使用ElasticSearch实现全文检索是一种常见且高效的方式。下面是详细的技术实践和说明: 安装和配置ElasticSearch: 首先,需要在服务器上安装ElasticSearch,并确保其正常运行。 然后,进行相关配置,如集群名称、...
elasticsearch elasticsearch-6.2.2 elasticsearch-6.2.2.zip 下载
ElasticSearch是一个开源的分布式搜索引擎,具有高可靠性,支持非常多的企业级搜索用例。Elasticsearch Hadoop作为一个完美的工具,用来连接 Elasticsearch 和 Hadoop 的生态系统。通过Kibana技术,Elasticsearch ...
Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elastic...