ElastaticSearch-应用详解
声明: 本博客已标明出处,如有侵权请告知,马上删除。
ES基本操作
ES概述
概述
Elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容
Elasticsearch是专门用于文档搜索的一块搜索引擎.
结构
ES操作-索引
脚本操作
创建索引库
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名": {
"type": "text",
"analyzer": "ik_smart"
},
"字段名2": {
"type": "keyword",
"index": "false"
}
#...略
}
}
}
查询索引库
GET /索引库名
添加新字段
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
删除索引库
DELETE /索引库名
Java操作
导入坐标
<!--定义版本-->
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<!--elasticsearch-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
相关代码
@SpringBootTest
public class HotelIndexTest {
private RestHighLevelClient client;
@BeforeEach
void setUp() {
//建立联接
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
//释放联接
client.close();
}
@Test
void testCreateIndex() throws IOException {
// 1.准备Request PUT /hotel
CreateIndexRequest request = new CreateIndexRequest("hotel");
// 2.准备请求参数
request.source("mappings映射的json字符串", XContentType.JSON);
// 3.发送请求
client.indices().create(request, RequestOptions.DEFAULT);
}
@Test
void testExistsIndex() throws IOException {
// 1.准备Request
GetIndexRequest request = new GetIndexRequest("hotel");
// 3.发送请求
boolean isExists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(isExists ? "存在" : "不存在");
}
@Test
void testDeleteIndex() throws IOException {
// 1.准备Request
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
// 3.发送请求
client.indices().delete(request, RequestOptions.DEFAULT);
}
}
ES操作-文档
脚本操作
添加文档
POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
查询文档
GET /索引库名称/_doc/文档id
修改文档-替换
PUT /索引库名称/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
// ... 略
}
修改文档-修改
POST /索引库名称/_update/文档id
{
"doc": {
"字段名": "新的值",
}
}
删除文档
DELETE /索引库名称/_doc/文档id
Java操作
导入坐标
<!--定义版本-->
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<!--elasticsearch-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
相关代码
@SpringBootTest
public class HotelDocumentTest {
private RestHighLevelClient client;
@Autowired
private IHotelService hotelService;
@BeforeEach
void setUp() {
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
@Test
void testAddDocument() throws IOException {
// 1.查询数据库hotel数据
Hotel hotel = hotelService.getById(61083L);
// 2.转换为HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 3.转JSON
String json = JSON.toJSONString(hotelDoc);
// 1.准备Request
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
// 2.准备请求参数DSL,其实就是文档的JSON字符串
request.source(json, XContentType.JSON);
// 3.发送请求
client.index(request, RequestOptions.DEFAULT);
}
@Test
void testGetDocumentById() throws IOException {
// 1.准备Request // GET /hotel/_doc/{id}
GetRequest request = new GetRequest("hotel", "61083");
// 2.发送请求
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析响应结果
String json = response.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println("hotelDoc = " + hotelDoc);
}
@Test
void testDeleteDocumentById() throws IOException {
// 1.准备Request // DELETE /hotel/_doc/{id}
DeleteRequest request = new DeleteRequest("hotel", "61083");
// 2.发送请求
client.delete(request, RequestOptions.DEFAULT);
}
@Test
void testUpdateById() throws IOException {
// 1.准备Request
UpdateRequest request = new UpdateRequest("hotel", "61083");
// 2.准备参数
request.doc(
"price", "870"
);
// 3.发送请求
client.update(request, RequestOptions.DEFAULT);
}
@Test
void testBulkRequest() throws IOException {
// 查询所有的酒店数据
List<Hotel> list = hotelService.list();
// 1.准备Request
BulkRequest request = new BulkRequest();
// 2.准备参数
for (Hotel hotel : list) {
// 2.1.转为HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 2.2.转json
String json = JSON.toJSONString(hotelDoc);
// 2.3.添加请求
request.add(new IndexRequest("hotel").id(hotel.getId().toString()).source(json, XContentType.JSON));
}
// 3.发送请求
client.bulk(request, RequestOptions.DEFAULT);
}
}
ES复杂搜索
ES搜索-脚本操作
基本语法
GET /索引库名称/_search
{
"query": {
"查询类型": {
"查询条件": "条件值"
}
}
}
查询所有
GET /索引库名称/_search
{
"query": {
"match_all": {
}
}
}
全文检索
match
GET /索引库名称/_search
{
"query": {
"match": {
"列名": "搜索文本"
}
}
}
multi_match
GET /索引库名称/_search
{
"query": {
"multi_match": {
"query": "搜索文本",
"fields": ["列名1", " 列名2"]
}
}
}
精确查询
** term查询**
GET /索引库名称/_search
{
"query": {
"term": {
"列名": {
"value": "值"
}
}
}
}
range查询
GET /索引库名称/_search
{
"query": {
"range": {
"列名": {
"gte": 小值, # 这里的gte代表大于等于,gt则代表大于
"lte": 大值 # lte代表小于等于,lt则代表小于
}
}
}
}
地理查询
区域查询
GET /索引库名称/_search
{
"query": {
"geo_bounding_box": {
"列名": {
"top_left": { # 左上点
"lat": 31.1,
"lon": 121.5
},
"bottom_right": { # 右下点
"lat": 30.9,
"lon": 121.7
}
}
}
}
}
附近查询
GET /索引库名称/_search
{
"query": {
"geo_distance": {
"distance": "15km", // 半径
"列名": "31.21,121.5" // 圆心
}
}
}
相关性算分
- 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)
- 过滤条件:filter部分,符合该条件的文档才会重新算分
- 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数
- weight:函数结果是常量
- field_value_factor:以文档中的某个字段值作为函数结果
- random_score:以随机数作为函数结果
- script_score:自定义算分函数算法
- 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
- multiply:相乘
- replace:用function score替换query score
- 其它,例如:sum、avg、max、min
GET /索引库名称/_search
{
"query": {
"function_score": {
"query": { .... }, # 原始查询,可以是任意条件
"functions": [ # 算分函数
{
"filter": { # 满足的条件,品牌必须是如家
"term": {
"brand": "如家"
}
},
"weight": 2 # 算分函数,算分权重为2
}
],
"boost_mode": "sum" # 运算模式,求和
}
}
}
复合查询
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:
- must:必须匹配每个子查询,类似“与”
- should:选择性匹配子查询,类似“或”
- must_not:必须不匹配,不参与算分,类似“非”
- filter:必须匹配,不参与算分
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{"term": {"city": "上海" }}
],
"should": [
{"term": {"brand": "皇冠假日" }},
{"term": {"brand": "华美达" }}
],
"must_not": [
{ "range": { "price": { "lte": 500 } }}
],
"filter": [
{ "range": {"score": { "gte": 45 } }}
]
}
}
}
ES搜索-Java操作
查询模版
package cn.jd.hotel;
import cn.jd.hotel.pojo.HotelDoc;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.Map;
@SpringBootTest
public class HotelSearchTest {
private RestHighLevelClient client;
@BeforeEach
void setUp() {
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
/**
* 查询
* @throws IOException
*/
public void testESQuery(QueryBuilder query) throws IOException {
// 1.准备request
SearchRequest request = new SearchRequest("hotel");
// 2.准备请求参数
request.source().query(query);
// 3.发送请求,得到响应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.结果解析
handleResponse(response);
}
/**
* 处理结果
* @param response 查询到的结果
*/
private void handleResponse(SearchResponse response) {
SearchHits searchHits = response.getHits();
// 4.1.总条数
long total = searchHits.getTotalHits().value;
System.out.println("总条数:" + total);
// 4.2.获取文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
for (SearchHit hit : hits) {
// 4.4.获取source
String json = hit.getSourceAsString();
// 4.5.反序列化,非高亮的
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 4.6.打印
System.out.println(hotelDoc);
}
}
}
查询所有
@Test
public void testMatchAll() throws IOException {
testESQuery(QueryBuilders.matchAllQuery());
}
全文检索
match
@Test
public void testMatch() throws IOException {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("列名", "搜索文本");
testESQuery(matchQueryBuilder);
}
multi_match
@Test
public void testMultiMatch() throws IOException {
MultiMatchQueryBuilder mq = QueryBuilders.multiMatchQuery("搜索文本", "列名1", "列名2");
testESQuery(mq);
}
精确查询
term查询
@Test
public void testTerm() throws IOException {
TermQueryBuilder tq = QueryBuilders.termQuery("列名", "值");
testESQuery(tq);
}
range查询
@Test
public void testTerm() throws IOException {
RangeQueryBuilder rq = QueryBuilders.rangeQuery("列名").gte("小值").lte("大值");
testESQuery(rq);
}
地理查询
区域查询
@Test
public void testGeoBoundingBox() throws IOException {
GeoBoundingBoxQueryBuilder gbq = QueryBuilders.geoBoundingBoxQuery("列名")
.setCorners("左上角维度,左上角经度", "右下角维度,右下角经度");
testESQuery(gbq);
}
附近查询
@Test
public void testGeoDistance() throws IOException {
GeoDistanceQueryBuilder dq = QueryBuilders.geoDistanceQuery("列名")
.distance("15", DistanceUnit.KILOMETERS)
.point(new GeoPoint("维度,经度"));
testESQuery(dq);
}
相关性算分
@Test
public void testScoreQuery() throws IOException {
//1.准备基础查询
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("列名", "搜索文本");
// 2.算分函数查询
FunctionScoreQueryBuilder fq = QueryBuilders.functionScoreQuery(
matchQueryBuilder, // 原始查询,matchQueryBuilder
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAD", true), // 过滤条件
ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
)
}
);
testESQuery(fq);
}
复合查询
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:
- must:必须匹配每个子查询,类似“与”
- should:选择性匹配子查询,类似“或”
- must_not:必须不匹配,不参与算分,类似“非”
- filter:必须匹配,不参与算分
@Test
public void testTerm() throws IOException {
BoolQueryBuilder bq = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("city", "上海"))
.should(QueryBuilders.termQuery("brand","皇冠假日"))
.should(QueryBuilders.termQuery("brand","华美达"))
.mustNot(QueryBuilders.rangeQuery("price").lte(500))
.filter(QueryBuilders.rangeQuery("score").gte(45));
testESQuery(bq);
}
ES结果处理
ES结果处理-脚本操作:
多个结果处理可以并用.
结果排序
普通字段排序
GET /indexName/_search
{
"query": {
"match_all": {} #查询条件,可以是任意条件
},
"sort": [
{
"列名": "desc" #排序字段、排序方式ASC、DESC
}
]
}
地理位置排序
GET /indexName/_search
{
"query": {
"match_all": {} #查询条件,可以是任意条件
},
"sort": [
{
"_geo_distance" : {
"列名" : "纬度,经度", #文档中geo_point类型的字段名、目标坐标点
"order" : "asc", #排序方式
"unit" : "km" #排序的距离单位
}
}
]
}
结果分页
GET /hotel/_search
{
"query": {
"match_all": {} #查询条件,可以是任意条件
},
"from": 0, # 分页开始的位置,默认为0
"size": 10, # 期望获取的文档总数
"sort": [
{"列名": "asc"} #排序
]
}
结果高亮
所谓高亮查询,其实就是在查询的关键字前边添加前缀,在关键字的后边添加后缀
注意:
- 高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。
- 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
- 如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false
GET /hotel/_search
{
"query": {
"match": {
"列名": "值" #查询条件,高亮一定要使用全文检索查询
}
},
"highlight": {
"fields": { # 指定要高亮的字段
"列名": {
"pre_tags": "<em>", # 用来标记高亮字段的前置标签
"post_tags": "</em>", # 用来标记高亮字段的后置标签
"required_field_match": "false" #对非搜索字段高亮
}
}
}
}
ES结果处理-Java操作
结果排序和分页:
对结果进行排序和分页, 都是在查询Request的对象处修改
/**
* 查询-分页和排序
* @throws IOException
*/
public void testESQuerySortPage(QueryBuilder query) throws IOException {
// 1.准备request
SearchRequest request = new SearchRequest("hotel");
// 2.准备请求参数
// 2.1.query
request.source().query(query);
// 2.2.排序 sort
request.source().sort("列名", SortOrder.ASC);
// 2.3.分页 from、size
request.source().from((page - 1) * size).size(5);
// // 2.3.距离排序
// String location = "维度,经度";
// if (StringUtils.isNotBlank(location)) {
// //按照地理位置排序
// request.source().sort(SortBuilders
// .geoDistanceSort("location", new GeoPoint(location))
// .order(SortOrder.ASC)
// .unit(DistanceUnit.KILOMETERS)
// );
// }
// 3.发送请求,得到响应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.结果解析
handleResponse(response);
}
高亮搜索
所谓高亮查询,其实就是在查询的关键字前边添加前缀,在关键字的后边添加后缀
高亮操作分为两部分: 1.设置高亮 2.获取高亮信息
注意: 高亮操作的列, 必须是"全文检索"的列, 也就是列的类型必须是text
设置高亮
**
* 查询-高亮
* @throws IOException
*/
public void testESQueryHighlight(QueryBuilder query) throws IOException {
// 1.准备request
SearchRequest request = new SearchRequest("hotel");
// 2.准备请求参数
// 2.1.query
request.source().query(query);
// 2.2.设置高亮
request.source().highlighter(new HighlightBuilder().field("列名").requireFieldMatch(false));
// 3.发送请求,得到响应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.结果解析
handleResponseHighlight(response);
}
** 获取高亮信息**
/**
* 处理结果
* @param response 查询到的结果
*/
private void handleResponseHighlight(SearchResponse response) {
SearchHits searchHits = response.getHits();
// 4.1.总条数
long total = searchHits.getTotalHits().value;
System.out.println("总条数:" + total);
// 4.2.获取文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
for (SearchHit hit : hits) {
// 4.4.获取source
String json = hit.getSourceAsString();
// 4.5.反序列化,非高亮的
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 4.6.处理高亮结果
// 1)获取高亮map
Map<String, HighlightField> map = hit.getHighlightFields();
// 2)根据字段名,获取高亮结果
HighlightField highlightField = map.get("name");
// 3)获取高亮结果字符串数组中的第1个元素
String hName = highlightField.getFragments()[0].toString();
// 4)把高亮结果放到HotelDoc中
hotelDoc.setName(hName);
// 4.7.打印
System.out.println(hotelDoc);
}
}
ES聚合查询
ES聚合查询-脚本操作
桶聚合-分组:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 #原始查询,可以修改(只对200元以下的文档聚合),如果不写,表示查询所有
}
}
},
"size": 0, #表示不显示文档,只显示聚合
"aggs": {
"brandAgg": { #brandAgg是聚合名称,该名称自由定义,聚合可以书写多个
"terms": {
"field": "brand", #根据brand进行分组
"size": 20, #最多显示20个结果
"order": {
"_count": "asc" #按照_count升序排列,如果不写,默认根据count降序
}
}
}
}
}
度量聚合-聚合函数
直接聚合:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 #原始查询,可以修改(只对200元以下的文档聚合),表示查询所有
}
}
},
"size": 0, #表示不显示文档,只显示聚合
"aggs": { #如果直接写在外边,就是对所有聚合,如果写在桶聚合的里边,就是对分组之后进行聚合
"score_stats": { #score_stats是聚合名称,该名称自由定义
"stats": { #聚合类型,这里stats计算count,min,max,avg,sum等,也可以单独书写为min,max,avg,count,sum等
"field": "score" #聚合字段,这里是score
}
}
}
}
分组后统计:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 #原始查询,可以修改(只对200元以下的文档聚合),表示查询所有
}
}
},
"size": 0,
"aggs": {
"brandAgg": { #聚合可以书写多个
"terms": {
"field": "brand",
"size": 20
},
"aggs": { #书写在brandAgg里边,所以是brandAgg聚合的子聚合,也就是分组后对每组分别计算
"score_stats": { #聚合名称,可以随便写
"stats": { #聚合类型,这里stats可以计算min、max、avg等,也可以书写为min,max,avg,count
"field": "score" #聚合字段,这里是score
}
}
}
}
}
}
ES聚合查询-Java操作
桶聚合-分组:
@Test
public void testAgg() throws IOException {
// 1.准备请求
SearchRequest request = new SearchRequest("hotel");
// 1.1 原始查询,如果不写,默认查询所有
request.source().query(QueryBuilders.rangeQuery("price").lte(200));
// 2.请求参数
// 2.1.size
request.source().size(0);
// 2.2.聚合(聚合可以书写多个)
request.source().aggregation(
AggregationBuilders.terms("brandAgg")
.field("brand")
.size(20)
.order(BucketOrder.count(true))
);
// 3.发出请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析结果
Aggregations aggregations = response.getAggregations();
// 4.1.根据聚合名称,获取聚合结果
Terms brandAgg = aggregations.get("brandAgg");
// 4.2.获取buckets
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
// 4.3.遍历
for (Terms.Bucket bucket : buckets) {
String brandName = bucket.getKeyAsString();
System.out.println("brandName = " + brandName);
long docCount = bucket.getDocCount();
System.out.println("docCount = " + docCount);
}
}
度量聚合-聚合函数
直接聚合
/**
* 度量聚合-聚合函数
*/
@Test
void testStatsAggs() throws IOException {
// 1.准备请求
SearchRequest request = new SearchRequest("hotel");
// 1.1 原始查询,如果不写,默认查询所有
request.source().query(QueryBuilders.rangeQuery("price").lte(200));
// 2.请求参数
// 2.1.size
request.source().size(0);
// 2.2.聚合
request.source().aggregation(
AggregationBuilders.stats("scoreStats").field("score")
);
// 3.发出请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析结果
Aggregations aggregations = response.getAggregations();
// 4.1.根据聚合名称,获取聚合结果
Stats scoreStats = aggregations.get("scoreStats");
// 4.2.获取数据
double count = scoreStats.getCount();
double max = scoreStats.getMax();
double min = scoreStats.getMin();
double avg = scoreStats.getAvg();
double sum = scoreStats.getSum();
System.out.println(count);
System.out.println(max);
System.out.println(min);
System.out.println(avg);
System.out.println(sum);
}
分组后统计
/**
* 分组后统计
*/
@Test
void testTermsStatsAggs() throws IOException {
// 1.准备请求
SearchRequest request = new SearchRequest("hotel");
// 1.1 原始查询,如果不写,默认查询所有
request.source().query(QueryBuilders.rangeQuery("price").lte(200));
// 2.请求参数
// 2.1.size
request.source().size(0);
// 2.2.聚合
request.source().aggregation(
AggregationBuilders.terms("brandAgg")
.field("brand")
.size(20)
.subAggregation(AggregationBuilders
.stats("scoreStats")
.field("score"))
);
// 3.发出请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析结果
Aggregations aggregations = response.getAggregations();
// 4.1.根据聚合名称,获取聚合结果
Terms brandAgg = aggregations.get("brandAgg");
// 4.2.获取buckets
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
// 4.3.遍历
for (Terms.Bucket bucket : buckets) {
//获取key
String brandName = bucket.getKeyAsString();
//获取Count数量
long docCount = bucket.getDocCount();
//获取统计信息
Aggregations scoreAgg = bucket.getAggregations();
Stats scoreStats = scoreAgg.get("scoreStats");
// 4.2.获取数据
double count = scoreStats.getCount();
double max = scoreStats.getMax();
double min = scoreStats.getMin();
double avg = scoreStats.getAvg();
double sum = scoreStats.getSum();
System.out.println(brandName + ":" + docCount);
System.out.println(brandName + "统计信息:" + count + "-" + max + "-" + min + "-" + avg + "-" + sum);
}
}
ES自定义分词器规则
PUT /hotel #索引库名称
{
"settings": { #索引库设置
"analysis": {
"analyzer": { #自定义分词器
"text_anlyzer": { #分词器名称
"tokenizer": "ik_max_word", #先用ik_max_word分词器,分词
"filter": "py" #然后用py过滤器进行进一步处理
},
"completion_analyzer": { #分词器名称
"tokenizer": "keyword", #先用确定keyword规则
"filter": "py" #然后用py过滤器进行进一步处理
}
},
"filter": { #定义过滤器
"py": { #过滤器名字
"type": "pinyin", #指定过滤器使用的分词器类型
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"id":{
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "text_anlyzer", #生成倒排索引时,使用text_anlyzer分词器
"search_analyzer": "ik_smart", #查询该列的时候,使用ik_smart分词器
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword",
"copy_to": "all"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "text_anlyzer", #生成倒排索引时,使用text_anlyzer分词器
"search_analyzer": "ik_smart" #查询该列的时候,使用ik_smart分词器
},
"suggestion":{
"type": "completion",
"analyzer": "completion_analyzer" #生成和查询时,都使用completion_analyzer分词器
#因为已经指定了分词器,所以,该列中可以直接存储中文,查询时会自动根据拼音补全
}
}
}
}
ES自动补全查询
ES自动补全查询-脚本操作
自动补全字段的类型必须是completion类型, 且字段值必须是多词条数组
GET /hotel/_search
{
"suggest": {
"hotel_suggest": {
"text": "sh", #查询关键字
"completion": {
"field": "suggestion", #补全查询的字段(根据哪个字段自动补全)
"skip_duplicates": true, #跳过重复的
"size": 10 #获取前10条结果
}
}
}
}
ES自动补全查询-Java操作
@Test
public void testSuggest() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
request.source().suggest(new SuggestBuilder().addSuggestion(
"hotelSuggest",
SuggestBuilders.completionSuggestion("suggestion") //补全的字段
.prefix("sh") //查询关键字
.skipDuplicates(true) //跳过重复
.size(10) //获取前10条结果
));
// 3.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析结果
Suggest suggest = response.getSuggest();
// 4.1.根据补全查询名称,获取补全结果
CompletionSuggestion suggestions = suggest.getSuggestion("hotelSuggest");
// 4.2.获取options
List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
// 4.3.遍历
List<String> list = new ArrayList<>(options.size());
for (CompletionSuggestion.Entry.Option option : options) {
String text = option.getText().toString();
list.add(text);
System.out.println(text);
}
}
ES集群-设置分片
注意,分片和副本设置只能在集群环境下设置
PUT /索引库名称 #索引库名称
{
"settings": {
"number_of_shards": 3, #分片数量,默认1
"number_of_replicas": 1 #副本数量
},
"mappings": {
"properties": {
//.....
}
}
}