一、DSL查询文档
1、DSL查询分类
ES提供了基于JSON的DSL语言定义的查询
DSL Query常见查询分类
- 查询所有
- 查询所有数据
- match_all
- 查询所有数据
- 全文检索查询full text
- 利用分词器对用户输入内容分词,然后去倒排索引库中匹配
- match
- multi_match
- 利用分词器对用户输入内容分词,然后去倒排索引库中匹配
- 精确查询
- 根据精确词条值查找数据(不分词),一般是查找keyword、数值、日期、boolean等类型字段
- term
- range
- 根据精确词条值查找数据(不分词),一般是查找keyword、数值、日期、boolean等类型字段
- 地理查询geo
- 根据经纬度查询
- geo_bounding_box
- geo_distance
- 根据经纬度查询
复合查询component
-
3、全文检索查询
全文检索查询
对用户输入的内容进行分词
- 常用于搜索框搜索
分类
match
查询- 对输入内容分词,再倒排索引检索
- 与查询条件(输入内容)越匹配,得分越高,显示越靠前
- 根据一个字段查询
GET /索引库名/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
- 大写表示字段/值是可变的,是自己索引库中的字段和值
- 小写表示固定格式,不可修改
- 对输入内容分词,再倒排索引检索
multi_match
查询(不建议使用)一般是查找keyword、数值、日期、boolean等类型字段
- 不会对搜索条件分词
分类
term
查询- 根据词条精确查询
- 一般查询keyword类型、数值类型、布尔类型、日期类型字段
GET /索引库名/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
- 根据词条精确查询
range
查询根据经纬度查询
分类
geo_bounding_box
查询(不常用)- 查询geo_point值落在某个矩形范围的所有文档(矩形范围)
GET /indexName/_search { "query": { "geo_bounding_box": { "FIELD": { "top_left": { "lat": 经度值, "lon": 纬度值 }, "bottom_right": { "lat": 经度值, "lon": 纬度值 } } } } }
top_left
左上lat
经度lon
纬度
bottom_right
右下
geo_distance
查询-
6.1 算分函数查询
fuction score
查询(算分函数查询) 可以控制文档相关性算分,控制文档排名
GET /索引库名称/_search { "query": { "function_score": { "query": { "match": {"all": "外滩"} }, "functions": [ { "filter": {"term": {"id": "1"}}, "weight": 10 } ], "boost_mode": "multiply" } } }
query
原始查询条件- 包含原始算分
filter
过滤条件- 符合条件的文档会被重新算分
weight
(算分函数的一种)- 算分函数
- 符合过滤条件的文档根据算分函数运算,得到函数算分
- 常见的算分函数
- weight:给一个常量值,作为函数结果(function score)
- field_value_factor:用文档中的某个字段值作为函数结果
- random_score:随机生成一个值,作为函数结果
- cript_score:自定义计算公式,公式结果作为函数结果
- 算分函数
boost_mode
加权模式- 默认相乘
multiply
(算分函数和相关性算法(原始查询得分)相乘) - 分类
multiply
:两者相乘replace
:用function score(算法函数)替换query score(相关性算法)- 其它:
sum
、avg
、max
、min
- 默认相乘
算分函数查询流程
- 根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
- 根据过滤条件,过滤文档
- 符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
- 将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果作为相关性算分
相关性算分算法
- 当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列
分类
一个或多个查询子句的组合。
子查询的组合方式
must
(and)- 必须匹配每个子查询
- 参与相关度算分
- must字段有且只能有一个,条件可有多个
should
(or)- 选择性匹配子查询
must_not
(!)- 必须不匹配,不参与算分
filter
(and)- 必须匹配(筛选条件)
- 不参与相关度算分
GET /索引库名称/_search { "query": { "bool": { "must": [ { "查询方式": { "字段名": "值" } } ], "should": [ { "查询方式": { "字段名": "值" } } ], "must_not": [ { "查询方式": { "字段名": "值" } } ], "filter": [ { "查询方式": { "字段名": "值" } } ] } } }
二、搜索结果处理
搜索结果处理的关键字(sort/(from, size)/highlight)与查询关键字(query)同级1、排序
ES支持对搜索结果排序
默认根据相关度算分(_score)来排序sort
自定义排序方式
默认情况下只返回top10的数据
- 如果要查询更多数据就需要修改分页参数
- 自定义分页
- ES中通过修改
from
、size
参数来控制要返回的分页结果from
- 分页开始位置
- 默认0
size
- 显示条数
- ES中通过修改
深度分页问题
- 默认上限10000(from + size)
- from : m, size : n
- 实际开发中,所有数据分散在集群中
- 从集群的每一个分片上都查出前m条数据,将查询到的数据聚合放到一个结果集中
- 再从该结果集中从第m条数据开始,获取n条数据
ES提供解决深度分页方案-无上限查询
- search after(官方推荐)
- 无上限查询
- 分页时需要排序
- 原理是从上一次的排序值开始,查询下一页数据
- 不支持翻页
- scroll(不推荐)
- 在搜索结果中把搜索关键字突出显示
- 原理
- 将搜索结果中的关键字用标签标记出来
- 在页面中给标签添加css样式
- 默认搜索哪个字段则哪个字段高亮
- 当搜索字段和高亮字段不匹配时
- 需要手动设置
highlight.require_field_match : “false”
- 需要手动设置
- 当搜索字段和高亮字段不匹配时
- 高亮结果放到查询结果的highlight字段中,表示哪个字段被高亮了
高亮三要素
- 指定要高亮的字段
- 标记高亮字段的前置标签
-
三、RestClient查询文档
0、初始化
查询的基本步骤
创建SearchRequest对象request
- request.source().Xxx()调用Xxx方法根据指定查询方式进行查询
- Xxx()方法即DSL查询方式
- 传入的参数是QueryBuilders.Yyy()
- 即查询方式Xxx具体的查询方式
- Xxx()方法即DSL查询方式
- 发送请求,得到结果
- 调用RestHighLevelClient对象的.search()方法
- 解析结果
- 参考JSON结果,从外到内,逐层解析
初始化
public class HotelSearchTest {
RestHighLevelClient rhlClient;
/**
* TODO:初始化ES客户端
*/
@BeforeEach
public void initClient(){
rhlClient = new RestHighLevelClient(
RestClient.builder(
new HttpHost("192.168.200.130", 9200, "http")
)
);
}
/**
* TODO:关闭ES客户端
*/
@AfterEach
public void closeClient(){
try {
rhlClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
抽取解析响应的方法
/**
* TODO:抽取出解析响应的方法
* @param response
*/
public void handleResponse(SearchResponse response){
//3、解析响应结果(需要先获取头hits,再继续往下获取想要的数据)
SearchHits hits = response.getHits();
//获取响应结果中的总条数
long totalHits = hits.getTotalHits().value;
System.out.println("满足查询条件的结果数量:" + totalHits);
//获取响应结果中的文档内容,即source部分(是一个数组)
SearchHit[] docArr = hits.getHits();
//遍历文档列表
for (SearchHit doc : docArr) {
//获取每个文档中的source字段内容(是json类型的字符串,需要反序列化为Java对象)
String jsonStr = doc.getSourceAsString();
//json类型数据反序列化为Java对象数据
HotelDoc hotelDoc = JSON.parseObject(jsonStr, HotelDoc.class);
System.out.println("文档内容:" + hotelDoc);
}
}
1、快速入门
/**
* TODO:快速入门
* @throws IOException
*/
@Test
public void testMatchAllQuery() throws IOException {
//1、创建搜索请求对象
SearchRequest request = new SearchRequest("hotel");
//设置query条件matchAll
request.source().query(QueryBuilders.matchAllQuery());
//2、执行client对应的搜索请求方法
SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT);
//3、解析响应结果(需要先获取头hits,再继续往下获取想要的数据)
SearchHits hits = response.getHits();
//获取响应结果中的总条数
long size = hits.getTotalHits().value;
System.out.println("满足查询条件的结果数量:" + size);
//获取响应结果中的文档内容,即source部分(是一个数组)
SearchHit[] docArr = hits.getHits();
//遍历文档列表
for (SearchHit doc : docArr) {
//获取每个文档中的source字段内容(是json类型的字符串,需要反序列化为Java对象)
String jsonStr = doc.getSourceAsString();
//json类型数据反序列化为Java对象数据
HotelDoc hotelDoc = JSON.parseObject(jsonStr, HotelDoc.class);
System.out.println("文档内容:" + hotelDoc);
}
}
整个响应结果就是response
- 可以获取总数量、文档列表等…
2、全文检索查询match
/** * TODO:全文搜索查询match * @throws IOException */ @Test public void testMatchQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //设置query条件match request.source().query(QueryBuilders.matchQuery("all", "如家")); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 handleResponse(response); }
3、精确查询
3.1 精确查询-term查询
/** * TODO:精确查询-词条查询term * @throws IOException */ @Test public void testTermQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //设置query条件term request.source().query(QueryBuilders.termQuery("city", "杭州")); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 handleResponse(response); }
3.2 精确查询-range查询
/** * TODO:精确查询-范围查询range * @throws IOException */ @Test public void testRangeQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //设置query条件range request.source().query(QueryBuilders.rangeQuery("price").gte(188).lte(588)); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 handleResponse(response); }
4、复合查询
/** * TODO:复合查询-布尔查询 * name 外滩如家 * price 188-588 * city 不可以是杭州 * @throws IOException */ @Test public void testBoolQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //创建bool条件,并设置条件 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(QueryBuilders.matchQuery("name", "外滩如家")); boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(188).lte(588)); boolQueryBuilder.mustNot(QueryBuilders.termQuery("city", "杭州")); //将条件设置的结果给请求对象 request.source().query(boolQueryBuilder); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 handleResponse(response); }
5、排序、分页、高亮
5.1 排序
/** * TODO:排序查询 * name 外滩如家 * price 188-588 * city 不可以是杭州 * @throws IOException */ @Test public void testSortQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //创建bool条件,并设置条件 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(QueryBuilders.matchQuery("name", "外滩如家")); boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(188).lte(588)); boolQueryBuilder.mustNot(QueryBuilders.termQuery("city", "杭州")); //将条件设置的结果给请求对象 request.source().query(boolQueryBuilder); //TODO:排序(设置:需要排序的字段, 升序/降序) request.source().sort("price", SortOrder.DESC); request.source().sort("score", SortOrder.DESC); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 handleResponse(response); }
5.2 分页
/** * TODO:分页查询 * name 外滩如家 * price 188-588 * city 不可以是杭州 * @throws IOException */ @Test public void testPageQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //创建bool条件,并设置条件 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(QueryBuilders.matchQuery("name", "外滩如家")); boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(188).lte(588)); boolQueryBuilder.mustNot(QueryBuilders.termQuery("city", "杭州")); //将条件设置的结果给请求对象 request.source().query(boolQueryBuilder); //排序(设置:需要排序的字段, 升序/降序) request.source().sort("price", SortOrder.DESC); request.source().sort("score", SortOrder.DESC); //TODO:分页 int page = 1; int pageSize = 5; request.source().from((page - 1) * pageSize).size(pageSize); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 handleResponse(response); }
5.3 高亮
/** * TODO:高亮查询 * @throws IOException */ @Test public void testHighlightQuery() throws IOException { //1、创建搜索请求对象 SearchRequest request = new SearchRequest("hotel"); //创建query条件,并设置条件 request.source().query(QueryBuilders.matchQuery("all", "如家")); HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.field("name"); //设置高亮字段 highlightBuilder.preTags("<span>"); //设置前置标签 highlightBuilder.postTags("</span>"); //设置后置标签 highlightBuilder.requireFieldMatch(false); //关闭字段检查 request.source().highlighter(highlightBuilder); //2、执行client对应的搜索请求方法 SearchResponse response = rhlClient.search(request, RequestOptions.DEFAULT); //3、处理响应结果 SearchHits hits = response.getHits(); long totalHits = hits.getTotalHits().value; System.out.println("查询结果条数:" + totalHits); SearchHit[] docArr = hits.getHits(); for (SearchHit doc : docArr) { String jsonStr = doc.getSourceAsString(); HotelDoc hotelDoc = JSON.parseObject(jsonStr, HotelDoc.class); //处理高亮结果,用高亮结果替换原来的name属性 Map<String, HighlightField> highlightFields = doc.getHighlightFields(); if (!CollectionUtils.isEmpty(highlightFields)){ //获取关于name字段的高亮结果 HighlightField highlightField = highlightFields.get("name"); if (highlightField != null){ String newName = highlightField.getFragments()[0].toString(); hotelDoc.setName(newName); } } System.out.println("高亮查询文档结果:" + hotelDoc); } }
四、黑马旅游案例
https://gitee.com/VanessaRain/hotel-demo.git(包含拓展任务)