Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页

这篇具有很好参考价值的文章主要介绍了Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

前面我们已经将ES的基础操作(索引,映射,文档)学习过了,从这一章开始,我们便开始学习ES的最大的功能—搜索
ES为用户提供了丰富的搜索功能:既有基本的搜索功能,又有搜索建议功能;既有常用的普通类型的匹配功能,又有基于地理位置的搜索功能;既提供了分页搜索功能,又提供了搜索的调试分析功能等等。这些都会在这一大章中学习到。但是考虑到搜索涉及到的章节确实非常多,于是我仍然像之前基础操作一样,拆解成一些章节供大家更容易吸收学习
那么这一节我们主要学习ES的搜索辅助功能。例如,为优化搜索功能,需要指定搜索的一部分字段内容。为了更好地呈现结果,需要用到结果计数和分页功能;当遇到性能瓶颈时,需要剖析搜索各个环节的耗时;面对不符合预期的搜索结果时,需要分析各个文档的评分细节。

二、指定搜索返回字段

考虑到性能问题,需要对搜索结果进行“瘦身”----指定返回搜索字段。在ES中,通过_source子句可以设定返回结果的字段。_source指向一个JSON数组,数组中的元素是希望返回的字段名称
在此之前,为了后面的学习,我们需要将hotel的索引彻底换一下,这里推荐大家先删除hotel索引,然后重新建立Hotel索引及映射关系,然后通过bulk批量插入值:
删除hotel索引后定义hotel索引的结构DSL如下:

DELETE /hotel

PUT /hotel
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "city": {
        "type": "keyword"
      },
      "price": {
        "type": "double"
      },
      "create_time": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "amenities": {
        "type": "text"
      },
      "full_room": {
        "type": "boolean"
      },
      "location": {
        "type": "geo_point"
      },
      "praise": {
        "type": "integer"
      }
    }
  }
}

然后在索引中批量新增如下数据:

POST /_bulk
{"index":{"_index":"hotel","_id":"001"}}
{"title":"文雅酒店","city":"北京","price":"558.00","create_time":"2020-03-29 21:00:00","amenities":"浴池,普通停车场/充电停车场","full_room":true,"location":{"lat":36.940243,"lon":120.39400},"praise":10}
{"index":{"_index":"hotel","_id":"002"}}
{"title":"京盛酒店","city":"北京","price":"337.00","create_time":"2020-07-29 13:00:00","amenities":"充电停车场/可升降停车场","full_room":false,"location":{"lat":39.911543,"lon":116.4030},"praise":60}
{"index":{"_index":"hotel","_id":"003"}}
{"title":"文雅文化酒店","city":"天津","price":"260.00","create_time":"2021-02-27 22:00:00","amenities":"提供假日party,免费早餐,浴池,充电停车场","full_room":true,"location":{"lat":39.186555,"lon":117.162767},"praise":30}
{"index":{"_index":"hotel","_id":"004"}}
{"title":"京盛集团酒店","city":"上海","price":"800.00","create_time":"2021-05-29 21:35:00","amenities":"浴池(假日需预订),室内游泳池,普通停车场/充电停车场","full_room":true,"location":{"lat":36.940243,"lon":120.39400},"praise":100}
{"index":{"_index":"hotel","_id":"005"}}
{"title":"京盛精选酒店","city":"南昌","price":"300.00","create_time":"2021-07-29 22:50:00","amenities":"室内游泳池,普通停车场","full_room":false,"location":{"lat":39.918229,"lon":116.422011},"praise":20}

下面的DSL指定搜索结果只返回title和city字段:

GET /hotel/_search
{
  "_source": ["title","city"],
  "query": {
    "term": {
      "city": {
        "value": "北京"
      }
    }
  }
}

执行上述DSL后,搜索结果如下:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页
在上述搜索结果中,每个命中文档的_source结构体中只包含指定的city和title两个字段的数据。
在Java客户端中,通过调用searchSourceBuilder.fetchSource()方法可以设定搜索返回的字段,该方法接收两个参数,即需要的字段数组和不需要的字段数组
我们先在service创建一个搜索接口,并且设定只返回title,city两个字段:

public List<Hotel> queryBySource(HotelDocRequest hotelDocRequest) throws IOException {
		String indexName = hotelDocRequest.getIndexName();
		if (CharSequenceUtil.isBlank(indexName)) {
			throw new SearchException("索引名不能为空");
		}
		Hotel hotel = hotelDocRequest.getHotel();
		if (ObjectUtil.isEmpty(hotel)) {
			throw new SearchException("搜索条件不能为空");
		}
		SearchRequest searchRequest = new SearchRequest(indexName);
		String city = hotel.getCity();
		//创建搜索builder
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		//构建query
		searchSourceBuilder.query(new TermQueryBuilder("city",city));
		//设定希望返回的字段数组
		searchSourceBuilder.fetchSource(new String[]{"title","city"},null);
		searchRequest.source(searchSourceBuilder);
		ArrayList<Hotel> resultList = new ArrayList<>();
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		RestStatus status = searchResponse.status();
		if (status != RestStatus.OK) {
			return Collections.emptyList();
		}
		SearchHits searchHits = searchResponse.getHits();
		for (SearchHit searchHit : searchHits) {
			Hotel hotelResult = new Hotel();
			hotelResult.setId(searchHit.getId());   //文档_id
			hotelResult.setIndex(searchHit.getIndex());   //索引名称
			hotelResult.setScore(searchHit.getScore());   //文档得分
			//转换为Map
			Map<String, Object> dataMap = searchHit.getSourceAsMap();
			hotelResult.setTitle((String) dataMap.get("title"));
			hotelResult.setCity((String) dataMap.get("city"));
			resultList.add(hotelResult);
		}
		return resultList;
	}

然后在controller中调用service接口:

@PostMapping("/query/source")
	public FoundationResponse<String> queryHotelsBySource(@RequestBody HotelDocRequest hotelDocRequest) {
		try {
			List<Hotel> hotelList = esQueryService.queryBySource(hotelDocRequest);
			if (CollUtil.isNotEmpty(hotelList)) {
				return FoundationResponse.success(hotelList.toString());
			} else {
				return FoundationResponse.success("no data");
			}
		} catch (IOException e) {
			log.warn("搜索发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		} catch (Exception e) {
			log.error("服务发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		}
	}

postman调用该接口:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页

三、结果计数

为提升搜索体验,需要给前段传递搜索匹配结果的文档条数,即需要对搜索结果进行计数。针对这个要求,ES提供了_count功能,在该API中,用户提供query子句用于结果匹配,而ES会返回匹配的文档条数。类似于RDBMS中的SELECT COUNT(*) FROM XXX WHERE XXX…
下面的DSL将返回城市为"北京"的酒店条数:

GET /hotel/_count
{
  "query": {   //计数的查询条件
    "match": {
        "city": "北京"
    }
  }
}

执行上述DSL后,返回的信息如下:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页
由结果可知,ES不仅返回了匹配的文档数量(值为2),并且还返回了和分片相关的元数据,如总共扫描的分片个数,以及成功、失败、跳过的分片个数等
在Java客户端中,通过CountRequest执行_count API,然后调用CountRequest对象的source()方法设置查询逻辑。countRequest.source()方法返回CountResponse对象,通过countResponse.getCount()方法可以得到匹配的文档条数
我们首先在service层创建根据城市获取搜索条数的API:

	public long getCityCount(HotelDocRequest hotelDocRequest) throws IOException {
		String indexName = hotelDocRequest.getIndexName();
		if (CharSequenceUtil.isBlank(indexName)) {
			throw new SearchException("索引名不能为空");
		}
		Hotel hotel = hotelDocRequest.getHotel();
		if (ObjectUtil.isEmpty(hotel)) {
			throw new SearchException("搜索条件不能为空");
		}
		//客户端的count请求
		CountRequest countRequest = new CountRequest(indexName);
		String city = hotel.getCity();
		//创建搜索builder
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		//构建query
		searchSourceBuilder.query(new TermQueryBuilder("city",city));
		countRequest.source(searchSourceBuilder); //设置查询
		CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
		return countResponse.getCount();
	}

然后controller调用service:

@PostMapping("/query/count")
	public FoundationResponse<Long> queryCount(@RequestBody HotelDocRequest hotelDocRequest) {
		try {
			Long count = esQueryService.getCityCount(hotelDocRequest);
			return FoundationResponse.success(count);
		} catch (IOException e) {
			log.warn("搜索发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		} catch (Exception e) {
			log.error("服务发生异常,原因为:{}", e.getMessage());
			return FoundationResponse.error(100, e.getMessage());
		}
	}

postman调用该接口:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页

四、结果分页

在实际的搜索应用中,分页是必不可少的功能。在默认情况下,ES返回前10个搜索匹配的文档。用户可以通过设置from和size来定义搜索位置和每页显示的文档数量,from表示查询结果的起始下标,默认值为0,size表示从起始下标开始返回的文档个数,默认值为10.下面的DSL将返回下标从0开始的20个结果:

GET /hotel/_search
{
  "_source": ["title","city"],
  "from": 0,   //设置搜索的起始位置
  "size": 20,  //设置搜索返回的文档个数
  "query": {   //搜索条件
    "term": {
      "city": {
        "value": "北京"
      }
    }
  }
}

在默认情况下,用户最多可以取得10000个文档,即from为0时,size参数最大为10000,如果该请求超过该值,ES返回如下报错信息:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页
对于普通的搜索应用来说,size设为10000已经足够用了。如果确实需要返回多于10000条数据,可以适当修改max_result_window的值。以下示例将hotel索引的最大窗口值修改为20000:

PUT /hotel/_settings
{
  "index":{
    "max_result_window":20000   //设定搜索返回的文档个数
  }
}

注意,如果将配置修改得很大,一定要有足够强大的硬件作为支撑
作为一个分布式搜索引擎,一个ES索引的数据分布在多个分片中,而这些分片又分配在不同的节点上。一个带有分页的搜索请求往往会跨越多个分片,每个分片必须在内存中构建一个长度为from+size的、按照得分排序的有序队列,用以存储命中的文档。然后这些分片对应的队列数据都会传递给协调节点,协调节点将各个队列的数据进行汇总,需要提供一个长度为(分片总数)*(from+size)的队列用以进行全局排序,然后再按照用户的请求从from位置开始查找,找到size个文档后进行返回
基于上述原理,ES不适合深翻页。什么是深翻页呢?简而言之就是请求的from值很大。假设在一个3个分片的索引中进行搜索请求,参数from和size的值分别为1000和10,其响应过程如下图:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页
当深翻页的请求过多时会增加各个分片所在节点的内存和CPU消耗。尤其是协调节点,随着页码的增加和并发请求的增多,该节点需要对这些请求涉及的分片数据进行汇总和排序,过多的数据会导致协调节点资源耗尽而停止服务。
作为搜索引擎,ES更适合的场景是对数据进行搜索,而不是进行大规模的数据遍历
。一般情况下,只需要返回前1000条数据即可,没有必要取到10000条数据。如果确实有大规模数据遍历的需求,可以参考使用scroll模式或者考虑使用其他存储引擎。
在Java客户端中,可以调用SearchSourceBuilder的from和size()方法来设定from和size参数。这里,我是用一种平常开发设置分页参数的一种方法,我们知道,类似mysql,我们都是通过offset,limit参数去控制从哪开始,查多少这样一个场景,其实ES和这个是一样的。我们可以建立一个共同的分页接口Pageable并写入获取Offset和Limit这两个参数的方法:

package com.mbw.request;

public interface Pagable {

	int getOffset();

	int getLimit();

	boolean isAutoCount();
}

然后就是写一个分页条件类,因为前端一般分页参数输入的是pageNo和pageSize来控制分页,熟悉分页的应该都了解,offset和limit可以通过这两个参数计算获取,下面是该条件类的主要代码:

package com.mbw.request;

import java.io.Serializable;


/**
 * 查询条件对象基类
 */
public class PageCondition implements Serializable, Pagable {

	private static final long serialVersionUID = 1L;

	public static final int DEFAULT_PAGE_NO = 1;
	public static final int DEFAULT_PAGE_SIZE = 10;

	protected int pageNo = DEFAULT_PAGE_NO;
	protected int pageSize = DEFAULT_PAGE_SIZE;
	protected boolean autoCount = true;

	public PageCondition() {
	}

	public PageCondition(int pageNo, int pageSize) {
		this.pageNo = pageNo < 1 ? DEFAULT_PAGE_NO : pageNo;
		this.pageSize = pageSize < 2 ? DEFAULT_PAGE_SIZE : pageSize;
	}

	public int getEnd(){
		return getLimit()+getOffset();
	}

	@Override
	public int getOffset() {
		return (pageNo - 1) * pageSize;
	}

	@Override
	public int getLimit() {
		return pageSize;
	}

	public void setPageNo(int pageNo) {
		this.pageNo = pageNo;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	/**
	 * 查询对象时是否自动另外执行count查询获取总记录数, 默认为false.
	 */
	@Override
	public boolean isAutoCount() {
		return autoCount;
	}

	/**
	 * 查询对象时是否自动另外执行count查询获取总记录数.
	 */
	public void setAutoCount(final boolean autoCount) {
		this.autoCount = autoCount;
	}

	public int getPageNo() {
		return pageNo;
	}

	public int getPageSize() {
		return pageSize;
	}

}

这样我们就可以通过pageNo和pageSize去控制offset和limit了,然后我们只需要调用SearchSourceBuilder的from和size方法即可,我们这边沿用之前指定搜索返回字段的service接口:

	public List<Hotel> queryBySource(HotelDocRequest hotelDocRequest) throws IOException {
		String indexName = hotelDocRequest.getIndexName();
		if (CharSequenceUtil.isBlank(indexName)) {
			throw new SearchException("索引名不能为空");
		}
		Hotel hotel = hotelDocRequest.getHotel();
		if (ObjectUtil.isEmpty(hotel)) {
			throw new SearchException("搜索条件不能为空");
		}
		SearchRequest searchRequest = new SearchRequest(indexName);
		String city = hotel.getCity();
		//创建搜索builder
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		//构建query
		searchSourceBuilder.query(new TermQueryBuilder("city",city));
		//设置分页参数
		searchSourceBuilder.from(hotelDocRequest.getOffset());
		searchSourceBuilder.size(hotelDocRequest.getLimit());
		//设定希望返回的字段数组
		searchSourceBuilder.fetchSource(new String[]{"title","city"},null);
		searchRequest.source(searchSourceBuilder);
		ArrayList<Hotel> resultList = new ArrayList<>();
		SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		RestStatus status = searchResponse.status();
		if (status != RestStatus.OK) {
			return Collections.emptyList();
		}
		SearchHits searchHits = searchResponse.getHits();
		for (SearchHit searchHit : searchHits) {
			Hotel hotelResult = new Hotel();
			hotelResult.setId(searchHit.getId());   //文档_id
			hotelResult.setIndex(searchHit.getIndex());   //索引名称
			hotelResult.setScore(searchHit.getScore());   //文档得分
			//转换为Map
			Map<String, Object> dataMap = searchHit.getSourceAsMap();
			hotelResult.setTitle((String) dataMap.get("title"));
			hotelResult.setCity((String) dataMap.get("city"));
			resultList.add(hotelResult);
		}
		return resultList;
	}

那么如果我现在什么都不输入,那么肯定会是用默认值pageNo=1,pageSize=10,意味着Offset=0,limit=10.那这样查出来肯定还是之前的2条,假设前端把pageSize改成1,那么postman调用应该就只有第一条了:
Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页文章来源地址https://www.toymoban.com/news/detail-445901.html

到了这里,关于Elasticsearch(八)搜索---搜索辅助功能(上)--指定搜索返回字段,结果计数和分页的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Elasticsearch搜索辅助功能解析(十)

            ES提供的各种搜索辅助功能。例如,为优化搜索性能,需要指定搜索结果返回一部分字段内容。为了更好地呈现结果,需要用到结果计数和分页功能;当遇到性能瓶颈时,需要剖析搜索各个环节的耗时;面对不符合预期的搜索结果时,需要分析各个文档的评分细节

    2024年02月13日
    浏览(37)
  • Elasticsearch (ES) 搜索引擎: 搜索功能:搜索分页、搜索匹配、全文搜索、搜索建议、字段排序

    原文链接:https://xiets.blog.csdn.net/article/details/132348920 版权声明:原创文章禁止转载 专栏目录:Elasticsearch 专栏(总目录) ES 搜索 API 官网文档:Search APIs 先创建一个索引,并写入一些文档用于搜索示例: 写入一些文档示例: 官网API:The _source option 搜索结果中的文档数据封装

    2024年02月08日
    浏览(45)
  • thinkPhp5返回某些指定字段

    或者指定要的字段的数组  查询符合条件的多条数据  

    2024年02月09日
    浏览(33)
  • 详解织梦模板DEDECMS搜索结果调用自定义字段的方法

    在搜索页调用自定义字段,使其可有让用户搜索这个自定义字段,找了好多文章和教程,却发现dedecms的标签底层模板字段不包括这个字段呢?这就大大限制了灵活性,但也不可能让所有字段都允许调用的,那样就会大大降低系统效率,所以今天分享的是一个比较完美解决这个

    2024年02月02日
    浏览(46)
  • elasticsearch相同查询条件每次返回结果不一样

    现象: 今天web开发人员反馈,在腾讯云对某一个客户的某表数据执行相同的查询结果,有时候不返回数据; 我们是按周区分索引的,数据量不大,去掉时间条件执行查询发现会交替出现  命中21980和命中的8999结果; 解决: 1.首先简单查询其他的表或者其他的集群有没有类似

    2024年01月23日
    浏览(45)
  • 用ES搜索关键字并且返回模糊字段高亮

       一般来说,各个网站,首页的搜索,都会有进行全文搜索的示例,并且把模糊匹配的多个数据进行标记(高亮),这样便于全局检索关键的数据,便于客户进行浏览。基于此,本文简单介绍这种功能基本java 的 实现    由于公司页面此功能隐藏了,本文就以接口调用返回看具

    2024年02月14日
    浏览(40)
  • 【经验分享】在百度搜索引擎中的结果中,过滤指定的网站

    系统版本:Windows 10 描述:在搜索引擎中想在结果中过滤一些指定网站,快速找到有价值的内容。 视频教程: 文字教程: 1.进入百度搜索引擎,bing搜索引擎貌似不可以。 www.baidu.com 2.搜索内容如:锐捷无线AC基础命令 -csdn,“-”这个符号是减号,0旁边那个,或者小键盘的减

    2024年02月03日
    浏览(45)
  • 关于Elasticsearch(es)中使用sql返回结果只有1000条

    es中返回的结果只有1000条,这是默认的,想要返回更多的结果,需要做两步: 1、设置max_result_window的大小 PUT indexName/_settings { “max_result_window”:20000 } max_result_window表示结果的最大数量 但是你这样设置完了之后,查询依旧还是1000条 2、查询的时候加上fetch_size 这表示页面上显

    2024年02月14日
    浏览(29)
  • elasticsearch删除脏数据(根据指定字段删除数据)

    es中出现几条脏数据,现在要把这几条数据直接删掉 找到要删除的脏数据,一般是根据id之类的字段来删除,因为id具有唯一性,其实和mysql差不多 1、先查到该条记录(注意我们这边使用的是 ticketId字段,因为他具有唯一性,方便查询) 返回结果 2、接下来就把 ticketId 为 fd

    2024年02月16日
    浏览(28)
  • Elasticsearch 搜索数组字段

    参考文章:Elasticsearch 搜索数组字段_hhhzua的专栏-CSDN博客_elasticsearch 查询数组 1、搜索 数组字段 tags 中同时存在元素 str_a 、 str_b 2、搜索 数组字段 tags 中存在元素 str_a 或 str_b

    2024年02月12日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包