Elasticserach
WHAT
Elasticsearch
,分布式全文搜索引擎。
它可以高效的 存储 和 检索 数据。
es 是使用 Java
语言开发的,并且基于 Lucene
,es 通过简单的RESTful API
来隐藏 Lucene的复杂性,从而使得全文搜索变得简单。
es 是 lucene 的封装,提供了RESTful API 的操作接口,开箱即用。
同类“竞品”
Solr
:是一个高性能,采用 Java 开发,基于Lucene的全文搜索服务器。它对外提供类似于web-service的API接口。
es 与 solr
- es基本是开箱即用,非常简单。而solr会有点复杂
- solr利用Zookeeper进行分布式管理,而elasticsearch自身带有分布式协调管理功能
- solr支持更多格式的数据,比如json xml csv。而es只支持json文件格式(够用)
- solr官方提供的功能更多,而elasticsearch更注重核心功能,高级功能由第三方插件提供
- solr查询快,但更新索引时慢,用于电商等查询多的应用
- es建立索引宽,即实时性查询快,用于facebook新浪等搜索
- solr较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而elasticsearch相对开发维护者较少,更新太快,学习使用成本较高
WHY
使用数据库 来做搜索业务,不能很好的满足需求。
虽然某一程度上也可以视为数据库,但是它更主要的身份还是一个优秀的全文搜索引擎。它的出现解决了一部分传统关系型数据库和NoSQL非关系型数据库所没有办法高效完成的一些工作,比如高效的全文检索,结构化检索,甚至是数据分析。
响应时间
eg
:数据库在做模糊查询时,如LIKE
语句,它会遍历整张表,同时进行字符串匹配。
而 es
是基于 倒排索引 的 ,检索速度非常快。
分词
Elasticsearch
支持中文分词插件 IK
相关性
Elasticsearch
支持全文搜索和相关度评分。这样在返回结果就会根据分数由高到低排列。分数越高,意味着和查询语句越相关。
可视化界面
MySQL 的 Navicat
Elasticsearch 的 Kibana
HOW
Elasticsearch 中的概念:
Index
:索引(相当于数据库)
Document
:文档(一条条记录)
注意:高版本可以可能会舍弃 type
,创建的时候可以设置为默认(_doc)
IK分词器
默认的中文分词,是将每个字看成一个词,显然不符合需求,所以,需要安装中文分词器
ik
来解决问题。
IK 提供了两个分词算法:ik_smart
和 ik_max_word
- ik_smart:最少切分
- ik_max_word:最细粒度切分
eg:
GET _analyze
{
"analyzer": "ik_smart",
"text": "我是社会主义接班人"
}
//输出
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "社会主义",
"start_offset" : 2,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "接班人",
"start_offset" : 6,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 3
}
]
}
GET _analyze
{
"analyzer": "ik_max_word",
"text": "我是社会主义接班人"
}
//输出
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "社会主义",
"start_offset" : 2,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "社会",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "主义",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "接班人",
"start_offset" : 6,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 5
},
{
"token" : "接班",
"start_offset" : 6,
"end_offset" : 8,
"type" : "CN_WORD",
"position" : 6
},
{
"token" : "人",
"start_offset" : 8,
"end_offset" : 9,
"type" : "CN_CHAR",
"position" : 7
}
]
}
命令模式使用
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 通过文档id查询文档 |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有的数据 |
SpringBoot 集成Elcticsearch
配置类:
/**
* SpringBoot与Elasticsearch集成
* 之后,用这个client对象就可以了
*/
@Configuration
public class ElasticsearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
return client;
}
}
搜索test
@Autowired
public RestHighLevelClient restHighLevelClient;
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest();
//构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//构建高亮
// searchSourceBuilder.highlighter();
//查询条件,可以使用 QueryBuilders 工具来实现 精确查询 QueryBuilders.termQuery()
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "damin");
//匹配全部
//MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
searchSourceBuilder.query(termQueryBuilder);
//设置分页
//不设置 SearchSourceBuilder 也有默认值
//searchSourceBuilder.from();
//searchSourceBuilder.size();
//设置搜索时间,(时间不要超过多少)
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//放到请求中
searchRequest.source(searchSourceBuilder);
//执行请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// searchResponse.getHits() //所有的结果都封装在 SearchHits -> getHits
//遍历
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}
爬取京东商品页面实例部分代码
视频:遇见狂神说
//普通搜索Service
@Autowired
public RestHighLevelClient restHighLevelClient;
//获取数据实现搜索功能
public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize) throws IOException {
if (pageNo <= 1) {
pageNo = 1;
}
//搜索条件 eg: goods
SearchRequest searchRequest = new SearchRequest("索引");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from(pageNo);
sourceBuilder.size(pageSize);
//精准匹配
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//执行搜索
searchRequest.source(sourceBuilder);
//通过客户端进行查询
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
//把 documentFields结果 变成map 返回
list.add(documentFields.getSourceAsMap());
}
return list;
}
//高亮显示
public List<Map<String,Object>> searchPageHighlightBuilder(String keyword,int pageNo,int pageSize) throws IOException {
if (pageNo <= 1) {
pageNo = 1;
}
//搜索条件 eg: goods
SearchRequest searchRequest = new SearchRequest("索引");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from(pageNo);
sourceBuilder.size(pageSize);
//精准匹配
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.requireFieldMatch(false); //多个高亮显示
highlightBuilder.preTags("<span style='color:red'>");//前缀标签
highlightBuilder.postTags("</span>"); //后缀标签
//标签,vue解析 :v-html=""
sourceBuilder.highlighter(highlightBuilder);
//执行搜索
searchRequest.source(sourceBuilder);
//通过客户端进行查询
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
//获取高亮字段
Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
HighlightField title = highlightFields.get("title");
//原来的结果 把原来没有高亮的字段 => 替换为有高亮的字段
Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
//解析高亮字段
if (title != null) {
Text[] fragments = title.fragments();
String n_title = "";
for (Text text : fragments) {
n_title += text;
}
sourceAsMap.put("title",n_title);
}
list.add(sourceAsMap);
}
return list;
}
Controller
@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
public List<Map<String,Object>> search(@PathVariable("keyword") String keyword,
@PathVariable("pageNo") int pageNo,
@PathVariable("pageSize") int pageSize) throws IOException {
//一些基本业务,比如没有传到值
if (pageNo == 0){
}
return contentService.searchPage(keyword,pageNo,pageSize);
}