赞
踩
// 查询所有
GET /indexName/_search
{
"query": {
"match_all": {
}
}
}
常见的全文检索查询包括:
match查询语法如下:
GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
mulit_match语法如下:
GET /indexName/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"]
}
}
}
match查询示例:
multi_match查询示例:
精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:
因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是不分词的词条。查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。
语法说明:
// term查询
GET /indexName/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
示例:
当我搜索的是精确词条时,能正确查询出结果:
但是,当我搜索的内容不是词条,而是多个词语形成的短语时,反而搜索不到:
范围查询,一般应用在对数值类型做范围过滤的时候。比如做价格范围过滤。
基本语法:
// range查询
GET /indexName/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10, // 这里的gte代表大于等于,gt则代表大于
"lte": 20 // lte代表小于等于,lt则代表小于
}
}
}
}
示例:
矩形范围查询,也就是geo_bounding_box查询,查询坐标落在某个矩形范围的所有文档:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-musSFUBK-1644302076175)(assets/DKV9HZbVS6.gif)]
查询时,需要指定矩形的左上、右下两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。
语法如下:
// geo_bounding_box查询 GET /indexName/_search { "query": { "geo_bounding_box": { "FIELD": { "top_left": { // 左上点 "lat": 31.1, "lon": 121.5 }, "bottom_right": { // 右下点 "lat": 30.9, "lon": 121.7 } } } } }
这种并不符合“附近的人”这样的需求,所以我们就不做了。
附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。
换句话来说,在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件:
语法说明:
// geo_distance 查询
GET /indexName/_search
{
"query": {
"geo_distance": {
"distance": "15km", // 半径
"FIELD": "31.21,121.5" // 圆心
}
}
}
示例:
我们先搜索陆家嘴附近15km的酒店:
发现共有47家酒店。
function score 查询中包含四部分内容:
function score的运行流程如下:
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:
比如在搜索酒店时,除了关键字搜索外,我们还可能根据品牌、价格、城市等字段做过滤:
每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询了。
需要注意的是,搜索时,参与打分的字段越多,查询的性能也越差。因此这种多条件查询时,建议这样做:
1)语法示例:
GET /hotel/_search { "query": { "bool": { "must": [ {"term": {"city": "上海" }} ], "should": [ {"term": {"brand": "皇冠假日" }}, {"term": {"brand": "华美达" }} ], "must_not": [ { "range": { "price": { "lte": 500 } }} ], "filter": [ { "range": {"score": { "gte": 45 } }} ] } } }
2)示例
需求:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。
分析:
elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
keyword、数值、日期类型排序的语法基本一致。
语法:
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": "desc" // 排序字段、排序方式ASC、DESC
}
]
}
排序条件是一个数组,也就是可以写多个排序条件。按照声明的顺序,当第一个条件相等时,再按照第二个条件排序,以此类推
示例:
需求描述:酒店数据按照用户评价(score)降序排序,评价相同的按照价格(price)升序排序
地理坐标排序略有不同。
语法说明:
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance" : {
"FIELD" : "纬度,经度", // 文档中geo_point类型的字段名、目标坐标点
"order" : "asc", // 排序方式
"unit" : "km" // 排序的距离单位
}
}
]
}
这个查询的含义是:
示例:
需求描述:实现对酒店数据按照到你的位置坐标的距离升序排序
提示:获取你的位置的经纬度的方式:https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/
假设我的位置是:31.034661,121.612282,寻找我周围距离最近的酒店。
分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:
类似于mysql中的limit ?, ?
分页的基本语法如下:
GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}
什么是高亮显示呢?
我们在百度,京东搜索时,关键字会变成红色,比较醒目,这叫高亮显示:
高亮显示的实现分为两步:
<em>
标签<em>
标签编写CSS样式高亮的语法:
GET /hotel/_search { "query": { "match": { "FIELD": "TEXT" // 查询条件,高亮一定要使用全文检索查询 } }, "highlight": { "fields": { // 指定要高亮的字段 "FIELD": { "pre_tags": "<em>", // 用来标记高亮字段的前置标签 "post_tags": "</em>" // 用来标记高亮字段的后置标签 } } } }
注意:
示例:
代码解读:
第一步,创建SearchRequest
对象,指定索引库名
第二步,利用request.source()
构建DSL,DSL中可以包含查询、分页、排序、高亮等
query()
:代表查询条件,利用QueryBuilders.matchAllQuery()
构建一个match_all查询的DSL第三步,利用client.search()发送请求,得到响应
这里关键的API有两个,一个是request.source()
,其中包含了查询、排序、分页、高亮等所有功能:
另一个是QueryBuilders
,其中包含match、term、function_score、bool等各种查询:
响应结果的解析:
elasticsearch返回的结果是一个JSON字符串,结构包含:
hits
:命中的结果
total
:总条数,其中的value是具体的总条数值max_score
:所有结果中得分最高的文档的相关性算分hits
:搜索结果的文档数组,其中的每个文档都是一个json对象
_source
:文档中的原始数据,也是json对象因此,我们解析响应结果,就是逐层解析JSON字符串,流程如下:
SearchHits
:通过response.getHits()获取,就是JSON中的最外层的hits,代表命中的结果
SearchHits#getTotalHits().value
:获取总条数信息SearchHits#getHits()
:获取SearchHit数组,也就是文档数组
SearchHit#getSourceAsString()
:获取文档结果中的_source,也就是原始的json文档数据完整代码如下:
@Test void testMatchAll() throws IOException { // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.准备DSL request.source() .query(QueryBuilders.matchAllQuery()); // 3.发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.解析响应 handleResponse(response); } private void handleResponse(SearchResponse response) { // 4.解析响应 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) { // 获取文档source String json = hit.getSourceAsString(); // 反序列化 HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); System.out.println("hotelDoc = " + hotelDoc); } }
全文检索的match和multi_match查询与match_all的API基本一致。差别是查询条件,也就是query的部分。
因此,Java代码上的差异主要是request.source().query()中的参数了。同样是利用QueryBuilders提供的方法:
而结果解析代码则完全一致,可以抽取并共享。
完整代码如下:
@Test
void testMatch() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
request.source()
.query(QueryBuilders.matchQuery("all", "如家"));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
精确查询主要是两者:
与之前的查询相比,差异同样在查询条件,其它都一样。
查询条件构造的API如下:
布尔查询是用must、must_not、filter等方式组合其它查询,代码示例如下:
可以看到,API与其它查询的差别同样是在查询条件的构建,QueryBuilders,结果解析等其他代码完全不变。
完整代码如下:
@Test void testBool() throws IOException { // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.准备DSL // 2.1.准备BooleanQuery BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 2.2.添加term boolQuery.must(QueryBuilders.termQuery("city", "杭州")); // 2.3.添加range boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250)); request.source().query(boolQuery); // 3.发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.解析响应 handleResponse(response); }
搜索结果的排序和分页是与query同级的参数,因此同样是使用request.source()来设置。
对应的API如下:
完整代码示例:
@Test void testPageAndSort() throws IOException { // 页码,每页大小 int page = 1, size = 5; // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.准备DSL // 2.1.query request.source().query(QueryBuilders.matchAllQuery()); // 2.2.排序 sort request.source().sort("price", SortOrder.ASC); // 2.3.分页 from、size request.source().from((page - 1) * size).size(5); // 3.发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.解析响应 handleResponse(response); }
高亮的代码与之前代码差异较大,有两点:
高亮请求的构建API如下:
上述代码省略了查询条件部分,但是大家不要忘了:高亮查询必须使用全文检索查询,并且要有搜索关键字,将来才可以对关键字高亮。
完整代码如下:
@Test
void testHighlight() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchQuery("all", "如家"));
// 2.2.高亮
request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
高亮的结果与查询的文档结果默认是分离的,并不在一起。
因此解析高亮的代码需要额外处理:
代码解读:
完整代码如下:
private void handleResponse(SearchResponse response) { // 4.解析响应 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) { // 获取文档source String json = hit.getSourceAsString(); // 反序列化 HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); // 获取高亮结果 Map<String, HighlightField> highlightFields = hit.getHighlightFields(); if (!CollectionUtils.isEmpty(highlightFields)) { // 根据字段名获取高亮结果 HighlightField highlightField = highlightFields.get("name"); if (highlightField != null) { // 获取高亮值 String name = highlightField.getFragments()[0].string(); // 覆盖非高亮结果 hotelDoc.setName(name); } } System.out.println("hotelDoc = " + hotelDoc); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。