Elasticsearch使用
# Elasticsearch使用
# 1.倒排索引
倒排索引:每条数据每条记录就是一个文档,json格式存储,文档按照语义划分为词语。在倒排索引中,每行记录格式为词条:文档id,每个词条都是唯一的。

因此查找“华为手机”这条记录时,首先先进行词条划分,然后在倒排索引中查找“华为”和“手机”两个词条所对应的所有文档id作为搜索的结果集。
MySql表中,行<—>Es索引,文档
# 2.安装
Kibana是用于可视化以及管理Es的一个界面应用程序。
ik分词器用于扩展中文分词
分词器扩展(文件/var/lib/docker/volumes/es-plugins/_data/ik/config/IKAnalyzer.cfg.xml中的ext_dict下编辑)和停用词(ext_stopwords)
# 3.DSL索引库操作
# mappings
- tyoe数据类型
- 字符串:text(可分词文本),keyword(精确值,品牌,国家)
- 数值类型:long,double...注意es中没有数组这一概念,但是允许一个字段有多个值
- 布尔类型
- 对象类型object
- index:是否创建索引,默认所有的字段都为true,如果是无意义字段不参与搜索则需要设置为false
- analyzer:使用分词器(和text类型使用)
- properties:该字段的子字段
# 创建,查看,删除索引库
建表语句:
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
查看索引库
GET /索引库名
删除索引库
DELETE /索引库名
修改索引库,只能添加新字段,原有的字段已经建立了倒排索引不允许修改
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
2
3
4
5
6
7
8
# 文档插入,删除,修改
文档插入,相当于向表中插入一条数据
POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
2
3
4
5
6
7
8
9
10
查询文档
GET /索引库名称/_doc/id
修改文档(①全量修改:相当于覆盖原来文档。②增量修改:修改文档的部分字段)
全量修改
PUT /{索引库名}/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
// ... 略
}
2
3
4
5
6
增量修改
POST /{索引库名}/_update/文档id
{
"doc": {
"字段名": "新的值",
}
}
2
3
4
5
6
经纬度地理坐标类型type使用geo_point
copy_to:将当前字段值拷贝到指定字段,比如查询一个酒店的address字段,就可以从all指定字段同时获取city字段,而不需要再去搜索依次city字段。
# 4.RestClient操作索引库
导入依赖,并保证es版本要和docker的es服务版本一致。
<properties>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
2
3
4
5
6
7
8
所有操作流程:初始化RestHighLevelClient->创建xxxRequest->准备参数source->调用xxx方法
创建索引库,指定索引库名称、索引库语句的资源文件。client.indices()包含所有索引库操作。
private RestHighLevelClient client=new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.200.200:9200")));
void testCteateHotelIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest("hotel");
request.source(MAPPING_TEMPLATE, XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);
}
2
3
4
5
6
7
插入文档数据,根据MP的HotelService(@Autowired)来获取行数据,然后封装成索引库文档的格式类型,source导入资源前还需要使用fastjson把对象转换成json对象。
查询文档数据,创建response对象并通过getSourceAsString()获取到json字符串,然后使用JSON.parseObject解析成java对象。
更新文档数据,更新的数据以key-value格式逗号隔开。
@Test
void testCteateHotelIndex1() throws IOException {
Hotel hotel = hotelService.getById(61083L);
HotelDoc hotelDoc = new HotelDoc(hotel);
IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
//查询
GetRequest request = new GetRequest("hotel", "61083");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
String json = response.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println(hotelDoc.toString());
//更新
UpdateRequest updateRequest = new UpdateRequest("hotel", "61083");
updateRequest.doc(
"price","952",
"starName","四钻"
);
client.update(updateRequest, RequestOptions.DEFAULT);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
es批量导入一个表,使用BulkRequest类,发送一个请求实现批处理。
# 5.DSL查询
查询类型:
- match:全文检索,针对text类型
- multi_match:允许根据多字段查询
- term:根据词条keyword,id进行精确查询(搜索内容必须和词条完全一致)
- range:根据值范围查询
- geo_distance:根据经纬度进行地理查询,"distance指示圆形范围的半径距离。
- funtion_score:复合查询,每条文档会根据结果和搜索词条的关联度打分,返回结果按照分值降序排序。可以自定义函数来对专门文档加分过滤,指定酸粉函数和权重。
- bool:must表示必须匹配每个子查询,should表示选择性匹配,must_not必须不匹配(不算分),filter表示必须满足的过滤条件(不算分)
GET /索引库名/_search
{
"query":{
"查询类型":{
"查询字段":"查询值"
}
}
}
2
3
4
5
6
7
8
# 6.ES搜索结果处理
- 排序
搜索得到的结果默认按照打分进行排序,可以使用sort字段指定按照哪个字段(Asc还是desc)重新进行排序。
如果sort指定geo_distance,则表示按照到指定位置的距离大小进行升序降序
- 分页
通过from指定分页开始的位置,size指定期望获取的文档总数。
倒排索引不适合分页,底层是查找出所有数据然后截取所要分页的部分。(如果是分布式存取,则需要对所有服务器都取出前N条,再重新筛选),因此ES底层from+size限制不能超过一万。
解决:after search搜索时必须排序,记住上一次查询的最后一个值,只能向后翻页。scroll向内存复制一个快照。
- 高亮
服务端提前给搜索结果关键字用标签标记出来,高亮显示
GET /索引库名/_search
{
"query":{
"match":{
"查询字段":"查询值"
}
},
"from":990,/分页开始的位置,默认为0
"size":10,//期望获取的文档总数
"sort": [
{ "price": "asc"}
],
"highlisht":{
"高亮字段(与上面搜索字段一致)":{
"require_field_match":"false"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 7.RestClient操作文档查询
具体流程:准备Request->准备DSL查询语句->发送请求->解析响应回来的json数据。
除了DSL语句其他部分都可以进行封装,核心是修改searchRequest.source().query()的query方法内部参数。所有查询条件类对象都是用QueryBuilders工厂来获得。当出现多个查询条件(全文查询,精确查询,范围查询...)需要从QueryBuilders中获取BoolQueryBuilder拼起来所有的条件,最后通过request.source().query(boolQuery)整合搜索条件。如果需要编写函数查询,还需要把boolQuery作为它的原始查询。
request下还有如下四个操作:
- ①查询
单字段查询
QueryBuilders.matchQuery("all","如家")
词条查询
QueryBuilders.termQuery("city","杭州")
组合查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.termQuery("city", "杭州"));
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(250));
2
3
4
5
6
7
8
- ②分页
- ③排序
- ④高亮
分页,高亮(注意高亮结果不在"source"字段,在"highlight"字段获取,在原字段值的基础上加了标签,因此转化为java对象属性保存时也会带有标签,输入到前端值就会渲染)
//分页
searchRequest.source().from(0).size(5);
//排序
searchRequest.source().sort("price", SortOrder.ASC)
//高亮
String s = hit.getSourceAsString();
Map<String, HighlightField> fields = hit.getHighlightFields();
HighlightField highlightField = fields.get("name");
String string = highlightField.getFragments()[0].string();
HotelDoc hotelDoc = JSON.parseObject(s, HotelDoc.class);
hotelDoc.setName(string);
2
3
4
5
6
7
8
9
10
11