solr和ElasticSearch都是很优秀的搜索引擎。
solr对外提供类似WebService的API接口,用post方法向solr服务器发送描述field及内容的xml文档,solr根据xml文档添加、删除、更新索引。
当建索引时solr会产生IO阻塞,查询性能较差。
Lucene是一套信息检索工具包,jar包,不包含搜索引擎系统。
包含:索引结构、读写索引的工具、排序、搜索规则等等的工具类
ElasticSearch基于Lucene做了一些封装和增强,是基于倒排索引(基于value找key)的分布式全文搜索引擎,具有高可扩展性,可近乎实时搜索和存储数据。目的是通过简单的RestFul API来隐藏Lucene的复杂性。提供全文搜索、结构化搜索、分析等功能。
官网下载ElasticSearch:https://www.elastic.co/cn/downloads/elasticsearch
一、环境安装:
解压即可使用,安装后的文件目录内容如下:
bin 启动文件,运行bin下elasticsearch.bat启动elasticsearch
config 配置文件
elasticsearch.yml elasticsearch的配置文件,默认9200端口
jvm.options jvm相关的配置文件
log4j2.properties 日志配置文件
roles.yml 角色配置文件
lib 相关jar包 包含lucence
logs 日志
modules 功能模块
plugins 插件,比如ik分词器
jdk 要求jdk1.8以上
安装完访问测试:http://127.0.0.1:9200/
另外需安装nodeJs、Python、Kibana(查询操作数据)、ik分词器、head处理器(可视化界面,用于展示数据)
先安装nodeJs,利用cnpm安装可视化界面 head插件,head插件GitHub下载地址:https://github.com/mobz/elasticsearch-head
cnpm install(根据package.json去淘宝镜像下载安装依赖包)
npm start (启动head)
访问测试:http://127.0.0.1:9100/
要让elasticSearch被远程启动和访问,需要修改配置config/elasticsearch.yml
跨域(跨端口、跨网站、跨ip)请求配置:
http.cors.enabled:true
http.cors.allow-origin:"*"
安装Kibana:
ELK的流程:收集清洗数据(Nginx log file、Tomcat log file)-->搜索,存储-->Kibana
Kibana可以将elasticsearch的数据通过友好的界面展示出来,提供实时分析的功能。是一个标准的工程,拆箱即用。可以通过在Kibana的可视化界面上输入命令来操作elasticsearch。
1、启动bin/kibana.bat
2、访问测试 http://127.0.0.1:5601/
3、汉化,修改kibana.yml的i18n.locale
二、基本概念 索引、分片、文档、字段类型
elasticSearch是面向文档的,一切都是JSON数据。
关系型数据库 ElasticSearch
数据库(database) 索引
表(tables) types
行(rows) documents
字段(columns) fields
字段类型(mapping):fields的类型,如整形
索引是映射类型的容器。elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上。
倒排索引:将value拆分,存储拆分后的value-key之间的关系,根据value找key
一般索引:根据key找对应的value
在elasticsearch中,索引被分为多个分片,每份分片是一个Lucene的索引,一个elasticsearch索引由多个Lucene索引组成。
三、ik分词器(中文分词器)
软件包下载:https://github.com/medcl/elasticsearch-analysis-ik/
IK提供了两个分词算法:ik_smart和ik_max_word,ik_smart为最少切分,ik_max_word为最细粒度区分。
示例:
ik分词器会将存入elasticsearch的数据根据内置字典拆分出多个关键字,并存储拆分出的关键字与原始数据的关联关系,当用户输入关键字搜索时即可通过倒排索引找到对应的原始数据。
若需扩展配置自己的字典,需在ik-config中配置自己的字典,将其配置到IKAnalyzer.cfg.xml中
四、Kibanan操作命令
restFul操作ES,对ES进行CRUD
创建索引:
如果自己的文档字段没有指定,es就会给我们默认配置字段类型。
在kibanna的控制台上输入命令往ES中插入文档 eg:
put /meng/type1/1
{
"name": "张三",
"age": 3,
"description": "高冷学霸,肤白貌美大长腿,Java工程师"
}
默认类型:
创建索引规则(字段类型)及字段:
put /test2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"birthday": {
"type": "date"
}
}
}
}
get _cat/indices?v 获得es当前的很多信息
覆盖更新文档: 修改文档后version会+1
put /meng/type1/1
{
"name": "张三",
"age": 25
}
修改文档中指定fields:
post /meng/type1/1/_update
{
"doc":{
# 要修改的具体内容
"age": 25
}
}
删除文档:
delete /meng/type1/1
五、elasticsearch查询
模糊搜索索引meng中name字段中含有Java的文档:
get /meng/type1/_search?q-name:Java
条件查询示例:
get test2/_doc/_search
{
# 模糊搜索
"query":{
"match":{
"name":"张三"
}
},
# 查询的字段
"_source": ["name","description","age"],
# 排序
"sort": [
{
"age": {
"order": "desc"
}
}
],
# 分页
"from":0,
"size":2
}
多条件匹配,多个条件都要满足(must -> and, should -> or, must_not -> not非):
get test2/_doc/_search
{
"query":{
"bool":{
"must":[
{
"match":{
"name":"张三"
}
},
{
"match":{
"age":25
}
}
],
# 区间过滤 gt 大于, gte 大于等于, lt 小于, lte 小于等于
"filter": {
"range": {
"age": {
"gt": 21,
"lte": 25
}
}
}
}
}
}
多个条件使用空格隔开,只要满足其中一个结果就会被匹配出来,匹配出来的数据按权重排序:
get test2/_doc/_search
{
"query":{
"match":{
"tags":"男 技术"
}
}
}
精确查询和分词:
term查询直接通过倒排索引指定的词条进行精确查找。
Match 会使用分词器解析(先分析文档,然后再进行查询)
Text和keyword类型:
keyword类型不会被分词器解析。
高亮查询:
get test2/_doc/_search
{
"query":{
"match": {
"name": "java"
}
},
"highlight": {
# 自定义搜索高亮条件,前置后置标签
"pre_tags": "<p style='color:red'>",
"post_tags": "</p>",
"fields": {
"name": {}
}
}
}
六、springBoot集成ES
1、pom中引入依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.5.1</version>
</dependency>
2、代码编写:
@Autowired
RestHighLevelClient client;
// 创建索引请求
CreateIndexRequest request = new CreateIndexRequest(“kuang_index”);
// 客户端执行请求IndicesClient,请求后获得响应
CreateIndexResponse response = Client.indices().create(request, RequestOptions.DEFAULT);
索引相关操作:
// 获取索引
GetIndexRequest request = new GetIndexRequest (“kuang_index”);
Boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); //判断是否存在
// 删除索引
DeleteIndexRequest request = new DeleteIndexRequest(“kuang_index”);
AcknowledgeResponse delete= client.indices().delete(request, RequestOptions.DEFAULT);
delete.isAcknowledged();
文档CRUD:
// 创建文档
User user = new User(“法外狂徒张三”, 3);
IndexRequest request = new IndexRequest(“kuang_index”);
// 规则:put /kuang_index/_doc/1
request.id(“1”);
// 设置请求超时时间
request.timout(“1ms”);
//将数据放入请求
request.source(JSON.toJsonString(user), XContentType.JSON);
//客户端发送请求,获取响应的结果
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
indexResponse.status(); // 对应命令返回的状态,created、updated
// 获取文档
GetRequest request = new GetRequest(“kuang_index”, “1”);
request.fetchSourceContext(new FetchSourceContext(false));
request.storedFields(“_none_”);
// 判断文档是否存在
boolean exists = client.exists(request, RequestOptions.DEFAULT);
GetResponse response = client.get(request, RequestOptions.DEFAULT);
response.getSourceAsString(); //文档内容
// 更新文档
UpdateRequest updRequest = new UpdateRequest(“kuang_index”, “1”);
updRequest.timout(“1ms”);
User user = new User(“法外狂徒张三”, 18);
updRequest.doc(JSON.toJsonString(user), XContentType.JSON);
UpdateResponse updResponse = client.update(updRequest, RequestOptions.DEFAULT);
updResponse.status(); // 更新状态
// 删除文档
DeleteRequest delRequest = new DeleteRequest(“kuang_index”, “1”);
delRequest.timout(“1ms”);
DeleteResponse delResponse = client.delete(delRequest, RequestOptions.DEFAULT);
delResponse.status();
// 批量插入,批处理请求
BulkRequset bulkRequest = new BulkRequest();
bulkRequest.timout(“10s”);
ArrayList<User> userList = new ArrayList<>();
userList.add(new User(“法外狂徒张三java”, 3));
userList.add(new User(“法外狂徒张三python”, 4));
userList.add(new User(“法外狂徒张三前端”, 5));
for(int i = 0;i< userList.size(); i++){
bulkRequest.add(new IndexRequest("kuang_index").id(""+(i+1)).source(JSON.toJsonString(userList.get(i)), XContentType.JSON));
}
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
bulkResponse.hasFailures(); // 执行是否失败
// 查询请求
SearchRequest searchRequest = new SearchRequest(“kuang_index”);
// 构造搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 模糊匹配,精确匹配用TermQueryBuilder
// 查询条件,使用QueryBuilders工具来实现
MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery(“name”, “狂神”);
sourceBuilder.query(queryBuilder);
queryBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 分页条件
sourceBuilder.from(1);
sourceBuilder.size(20);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 查询的返回值
searchResponse.getHits();
for(SearchHit documents: searchResponse.getHits().getHits()){
System.out.println(documents.getSourceAsMap());
}
// 高亮搜索 HighLightBuilder