springboot es 全文检索,spring data es 复杂查询

  springboot es 全文检索,spring data es 复杂查询

  

目录

1、配置2、API操作ES2.1查询索引列表2.2术语查询2.3通配符查询2.4范围查询2.5匹配查询2.6多匹配查询2.7现有查询2.8布尔值查询2.9排序2.10 结果字段过滤2.11 分页2.22 聚合跳羚版本:2.0.5 .释放弹性搜索版本:7.9.1

 

  

1、配置

引入依赖:

 

  依赖关系groupIdorg.elasticsearch.client/groupId艺术弹性搜索-休息-高层-客户端/artifactId版本7 .9 .1/版本/依赖关系依赖关系groupIdorg.elasticsearch/groupId artifactIdelasticsearch/artifactId版本7 .9 .1/版本/依赖关系应用程序.属性配置文件:

  弹性搜索。schema=httpelasticsearch。地址=192。168 .80 .130:9200,192.168.80.131:9200,192 .168 .80 .132:9200弹性搜索。连接超时=10000 elasticsearch。插座超时=6000弹性搜索。connectionrequesttime out=1000 elastic search无密码可忽略弹性搜索。用户名=elastic lasticsearch。密码=123456连接配置:

  导入org。阿帕奇。http。httphost导入org。阿帕奇。http。auth。授权范围;导入组织。阿帕奇。http。auth。用户名密码凭据;导入组织。阿帕奇。http。客户。凭据提供程序;导入组织。阿帕奇。http。impl。客户。basiccredentialsprovider导入org。弹性搜索。客户。rest客户端;导入org。弹性搜索。客户。rest客户端生成器;导入org。弹性搜索。客户。resthighlevelclient导入org。spring框架。豆子。工厂。注释。价值;导入org。spring框架。语境。注释。豆;导入org。spring框架。语境。注释。配置;导入javax。注释。预判;导入Java。io。io异常;导入Java。util。ArrayList导入Java。util。列表;@ configuration公共类ElasticSearchConfig {/* * *协议*/@ Value( $ {弹性搜索。架构: http } )私有字符串架构;/** * 集群地址,如果有多个用","隔开*/@ Value( $ {弹性搜索。地址} )私有字符串地址;/** * 集群地址,如果有多个用","隔开*/@ Value( $ {弹性搜索。用户名} )私有字符串用户名;/** * 集群地址,如果有多个用","隔开*/@ Value( $ {弹性搜索。密码} )

   private String password; /** * 连接超时时间 */ @Value("${elasticsearch.connectTimeout:5000}") private int connectTimeout; /** * Socket 连接超时时间 */ @Value("${elasticsearch.socketTimeout:10000}") private int socketTimeout; /** * 获取连接的超时时间 */ @Value("${elasticsearch.connectionRequestTimeout:5000}") private int connectionRequestTimeout; /** * 最大连接数 */ @Value("${elasticsearch.maxConnectNum:100}") private int maxConnectNum; /** * 最大路由连接数 */ @Value("${elasticsearch.maxConnectPerRoute:100}") private int maxConnectPerRoute; private RestHighLevelClient restHighLevelClient; @Bean public RestHighLevelClient restHighLevelClient() { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); UsernamePasswordCredentials elastic = new UsernamePasswordCredentials(userName, password); credentialsProvider.setCredentials(AuthScope.ANY,elastic); // 拆分地址 List<HttpHost> hostLists = new ArrayList<>(); String[] hostList = address.split(","); for (String addr : hostList) { String host = addr.split(":")[0]; String port = addr.split(":")[1]; hostLists.add(new HttpHost(host, Integer.parseInt(port), schema)); } // 转换成 HttpHost 数组 HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{}); // 构建连接对象 RestClientBuilder builder = RestClient.builder(httpHost); // 异步连接延时配置 builder.setRequestConfigCallback(requestConfigBuilder -> { requestConfigBuilder.setConnectTimeout(connectTimeout); requestConfigBuilder.setSocketTimeout(socketTimeout); requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout); return requestConfigBuilder; }); // 异步连接数配置 builder.setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setMaxConnTotal(maxConnectNum); httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return httpClientBuilder; }); restHighLevelClient = new RestHighLevelClient(builder); return restHighLevelClient; } @PreDestroy public void clientClose() { try { this.restHighLevelClient.close(); } catch (IOException e) { e.printStackTrace(); } }}

 

  

2、API操作ES

 

  

2.1 查询索引列表

可以模糊匹配索引名称

 

  

@Testpublic void tset() throws IOException { GetIndexRequest getIndexRequest = new GetIndexRequest("log*"); // 获取es前缀过滤下所有索引 GetIndexResponse getIndexResponse = restHighLevelClient.indices().get(getIndexRequest, RequestOptions.DEFAULT); // 将es查出的索引转换为list List<String> elasticsearchList = new ArrayList<>(getIndexResponse.getMappings().keySet()); elasticsearchList.forEach(System.out::println);}

 

  

2.2 TermsQuery

es 的 trem query 做的是精确匹配查询,关于这里早 serviceName 字段后面加的 .keyword 说明如下:

 

  1.es5.0 及以后的版本取消了 String 类型,将原先的 String 类型拆分为 text 和 keyword 两种类型。它们的区别在于 text 会对字段进行分词处理而 keyword 则不会。

  2.当没有为索引字段预先指定 mapping 的话,es 就会使用 Dynamic Mapping ,通过推断你传入的文档中字段的值对字段进行动态映射。例如传入的文档中字段 total 的值为12,那么 total 将被映射为 long 类型;字段 addr 的值为"192.168.0.1",那么 addr 将被映射为 ip 类型。然而对于不满足 ip 和 long 格式的普通字符串来说,情况有些不同:ES 会将它们映射为 text 类型,但为了保留对这些字段做精确查询以及聚合的能力,又同时对它们做了 keyword 类型的映射,作为该字段的 fields 属性写到 _mapping 中。例如,我这里使用的字段 serviceName,用来存储服务名称字符串类型,会对它做如下的 Dynamic Mapping:

  

"serviceName" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } }}

在之后的查询中使用 serviceName 是将 serviceName 作为 text 类型查询,而使用 serviceName.keyword 则是将 serviceName 作为 keyword 类型查询。前者会对查询内容做分词处理之后再匹配,而后者则是直接对查询结果做精确匹配。

 

  3.es 的 trem query 做的是精确匹配而不是分词查询,因此对 text 类型的字段做 term 查询将是查不到结果的(除非字段本身经过分词器处理后不变,未被转换或分词)。此时,必须使用 serviceName.keyword 来对 serviceName 字段以 keyword 类型进行精确匹配。

  

GET logdata-log-center-2021.05.06/_search{ "query": { "terms": { "serviceName.keyword": [ "log-center-user-portal", "log-center-collect-manage" ] } }}

Java API

 

  

@Testpublic void test() throws IOException { //构建查询源构建器 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// termQuery只能匹配一个值,第一个入参为字段名称,第二个参数为传入的值,相当于sql中的=// searchSourceBuilder.query(QueryBuilders.termQuery("serviceName.keyword", "log-center-user-portal-web")); //termsQuery可以一次性匹配多个值,相当于sql中的in searchSourceBuilder.query(QueryBuilders.termsQuery("serviceName.keyword", "log-center-user-portal-web", "log-center-collect-manage")); //构建查询请求对象,入参为索引 SearchRequest searchRequest = new SearchRequest("log-web-up-log-center-2021.10.30"); //向搜索请求对象中配置搜索源 searchRequest.source(searchSourceBuilder); // 执行搜索,向ES发起http请求 SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); if (RestStatus.OK.equals(response.status())) { long total = response.getHits().getTotalHits().value; //检索到符合条件的总数 SearchHit[] hits = response.getHits().getHits(); //未指定size,默认查询的是10条 for (SearchHit hit : hits) { String index = hit.getIndex();//索引名称 String id = hit.getId(); //文档id JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); //文档内容 System.out.println(jsonObject); } }}

 

  

2.3 WildcardQuery

es的 wildcard query 做的是模糊匹配查询,类似 sql 中的 like,而 value 值前后的 * 号类似与 sql 中的 % 。

 

  

GET logdata-log-center-2021.05.06/_search{ "query": { "wildcard": { "serviceName.keyword": { "value": "*user-portal*" } } }}

Java API

 

  

searchSourceBuilder.query(QueryBuilders.wildcardQuery("serviceName.keyword", "*" + "user-portal" + "*"));

 

  

2.4 RangeQuery

es 的 range query 做的是范围查询,相当于 sql 中的 between … and …

 

  

GET log-web-up-log-center-2021.10.30/_search{ "query": { "range": { "timestamp": { "gte": "2021-10-30 15:00:00", "lte": "2021-10-30 16:00:00", "format": "yyyy-MM-dd HH:mm:ssyyyy-MM-dd HH:mm:ss.SSS" } } }}

Java API

 

  

searchSourceBuilder.query(QueryBuilders.rangeQuery("timestamp") .gte("2021-10-30 15:00:00") //起始值 .lte("2021-10-30 16:00:00") //结束值 .format("yyyy-MM-dd HH:mm:ssyyyy-MM-dd HH:mm:ss.SSS"));//可以指定多个格式化标准,使用隔开

 

  

2.5 MatchQuery

es的 match query 做的是全文检索,会对关键字进行分词后匹配词条。

 

  

GET log-web-up-log-center-2021.10.30/_search{ "query": { "match": { "orgName": { "query": "有限公司" } } }}

query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用逗号分隔也可以不用。

 

  Java API

  

//全文检索,支持分词匹配searchSourceBuilder.query(QueryBuilders.matchQuery("orgName", "有限公司");

 

  

2.6 MultiMatchQuery

上面的 MatchQuery 有一个短板,假如用户输入了某关键字,我们在检索的时候不知道具体是哪一个字段,这时我们用什么都不合适,而 MultiMatchQuery 的出现解决了这个问题,他可以通过 fields 属性来设置多个域联合查找,具体用法如下

 

  

GET log-web-up-log-center-2021.10.30/_search{ "query": { "multi_match": { "query": "user-portal", "fields": ["serviceName", "systemName"] } }}

Java API

 

  

//全文检索,支持分词匹配,支持多字段检索searchSourceBuilder.query(QueryBuilders.multiMatchQuery("user-portal", "serviceName", "systemName", "description"));

 

  

2.7 ExistsQuery

es的 exists query 做的是检索某个字段存在的数据,即不为 null 的数据。其中指定的 field 可以是一个具体的字段,也可以是一个 json 结构。

 

  

GET logdata-log-center-2021.05.06/_search{ "query": { "exists": { "field": "networkLogDetailInfo" } }}

Java API

 

  

//查询networkLogDetailInfo不为null的数据searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo"));

 

  

2.8 BoolQuery

es的 bool query 做的是将多个查询组合起来去检索数据,主要的组合参数有 must、should、mustNot 等。

 

  must:数据必须匹配 must 所包含的查询条件,相当于 ANDshould:数据匹配 should 包含的一个或多个查询条件,相当于 ORmustNot:数据必须不匹配 mustNot 所包含的查询条件,相当于 NOT

GET logdata-log-center-2021.05.06/_search{ "query": { "bool": { "must": [ { "exists": { "field": "networkLogDetailInfo" } }, { "range": { "timestamp": { "gte": "2021-05-05 00:00:00", "lte": "2021-05-07 00:00:00", "format": "yyyy-MM-dd HH:mm:ssyyyy-MM-dd HH:mm:ss.SSS" } } } ], "must_not": [ { "exists": { "field": "serviceLogDetailInfo" } } ] } }}

Java API

 

  

@Testpublic void test() throws IOException { //构建查询源构建器 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //构建bool类型查询器 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); //使用must连接,相当于and,构建第一个查询条件existsQuery必须包含此字段 boolQueryBuilder.must(QueryBuilders.existsQuery("networkLogDetailInfo")); //使用must连接第二个条件,rangeQuery范围查找,相当于between...and... boolQueryBuilder.must(QueryBuilders.rangeQuery("timestamp") .from("2021-05-05 00:00:00") //起始值 .to("2021-05-07 00:00:00") //结束值 .includeLower(true) //是否等于起始值 .includeUpper(false) //是否等于结束值 .format("yyyy-MM-dd HH:mm:ssyyyy-MM-dd HH:mm:ss.SSS")); //格式化时间 //使用mustNot连接第三个条件 boolQueryBuilder.mustNot(QueryBuilders.existsQuery("serviceLogDetailInfo")); searchSourceBuilder.query(boolQueryBuilder); //构建查询请求对象,入参为索引 SearchRequest searchRequest = new SearchRequest("logdata-log-center-2021.05.06"); //向搜索请求对象中配置搜索源 searchRequest.source(searchSourceBuilder); // 执行搜索,向ES发起http请求 SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); if (RestStatus.OK.equals(response.status())) { long total = response.getHits().getTotalHits().value; //检索到符合条件的总数 SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { String index = hit.getIndex();//索引名称 String id = hit.getId(); //文档id JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); //文档内容 System.out.println(jsonObject); } }}

 

  

2.9 排序

es 使用 sort 进行排序,可以多个字段联合排序。

 

  

GET logdata-log-center-2021.05.06/_search{ "query": { "bool": { "must_not": [ { "exists": { "field": "serviceLogDetailInfo" } } ] } }, "sort": [ { "serviceName.keyword": { "order": "asc" }, "timestamp": { "order": "desc" } } ]}

先按照第一个字段排序,第一个字段相同时按照第二个字段排序。

 

  Java API

  

//升序searchSourceBuilder.sort("serviceName.keyword", SortOrder.ASC);//降序searchSourceBuilder.sort("timestamp", SortOrder.DESC);

 

  

2.10 结果字段过滤

检索数据,有时只需要其中的几个字段,es 也支持对结果集进行字段筛选过滤。字段可以使用 * 进行模糊匹配。

 

  

GET logdata-log-center-2021.05.06/_search{ "_source": { "includes": ["messageId", "system*", "service*", "timestamp"], "excludes": [] }}

Java API

 

  

//筛选字段,第一个参数为需要的字段,第二个参数为不需要的字段searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {});

 

  

2.11 分页

es 的分页方式有三种:from+ size、scroll、search_after, 默认采用的分页方式是 from+ size 的形式。

 

  2.11.1 from+ size

  

GET logdata-log-center-2021.05.06/_search{ "from": 0, "size": 2, "query": { "exists": { "field": "networkLogDetailInfo" } }, "_source": { "includes": ["messageId", "system*", "service*", "timestamp"], "excludes": [] }}

 

  通过查询结果可以发现,我们设置了分页参数之后, hits.total 返回的是数据总数7149,而按照分页规则,我们设置的size=2,因此 hits.hits 里面只有两条数据。

  Java API

  

@Testpublic void test() throws IOException { //构建查询源构建器 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //查询条件 searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo")); int page = 1; // 页码 int size = 2; // 每页显示的条数 int index = (page - 1) * size; searchSourceBuilder.from(index); //设置查询起始位置 searchSourceBuilder.size(size); //结果集返回的数据条数 //筛选字段,第一个参数为需要的字段,第二个参数为不需要的字段 searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {}); //构建查询请求对象,入参为索引 SearchRequest searchRequest = new SearchRequest("logdata-log-center-2021.05.06"); //向搜索请求对象中配置搜索源 searchRequest.source(searchSourceBuilder); // 执行搜索,向ES发起http请求 SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); if (RestStatus.OK.equals(response.status())) { long total = response.getHits().getTotalHits().value; //检索到符合条件的总数 SearchHit[] hits = response.getHits().getHits(); //未指定size,默认查询的是10条 for (SearchHit hit : hits) { String index = hit.getIndex();//索引名称 String id = hit.getId(); //文档id JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); //文档内容 System.out.println(jsonObject); } }}

2.11.2 scroll

 

  一种可满足深度分页的方式,es 提供了 scroll 的方式进行分页读取。原理上是对某次查询生成一个游标 scroll_id , 后续的查询只需要根据这个游标去取数据,每次只能拿到下一页的数据,直到结果集中返回的 hits 字段为空,就表示遍历结束。这里scroll=1m是scroll_id的有效期,表示1分钟,过期后会被es自动清理,每次查询会更新此值。

  

GET logdata-log-center-2021.05.06/_search?scroll=1m{ "size": 2, "query": { "exists": { "field": "networkLogDetailInfo" } }, "_source": { "includes": ["messageId", "system*", "service*", "timestamp"], "excludes": [] }}

 

  后续的查询中查询条件不需要指定,只需要携带 scroll_id 即可它会按照首次查询条件进行分页展示,下一次查询(两种方式):

  

POST /_search/scroll{ "scroll": "1m", "scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFFp0bGhXbjBCQU55Q3EtSDcxaWF4AAAAAACF-OYWV0liWUNLUHVTN09DS1ZtUl9SSHhVdw=="}
GET /_search/scroll?scroll=1m&scroll_id=FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFFp0bGhXbjBCQU55Q3EtSDcxaWF4AAAAAACF-OYWV0liWUNLUHVTN09DS1ZtUl9SSHhVdw==

Java API

 

  

public void testScroll(String scrollId) throws IOException { //查询源构建器 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //每页显示2条 searchSourceBuilder.size(2); //查询条件 searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo")); //筛选字段,第一个参数为需要的字段,第二个参数为不需要的字段 searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {}); SearchRequest request = new SearchRequest("logdata-log-center-2021.05.06"); request.source(searchSourceBuilder); Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L)); request.scroll(scroll);//滚动翻页 SearchResponse response; if (!StringUtils.isBlank(scrollId)) { //Scroll查询 SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); scrollRequest.scroll(scroll); response = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT); } else { //首次查询使用普通查询 response = restHighLevelClient.search(request, RequestOptions.DEFAULT); } //更新scrollId scrollId = response.getScrollId(); System.out.println(scrollId); if (RestStatus.OK.equals(response.status())) { //设置查询总量 SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { String index = hit.getIndex(); String id = hit.getId(); JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); System.out.println(jsonObject); } }}

2.11.3 search_after

 

  search_after 是 ES5.0 及之后版本提供的新特性,search_after查询时需要指定sort排序字段,可以指定多个排序字段,后续查询有点类似 scroll ,但是和 scroll 又不一样,它提供一个活动的游标,通过上一次查询的最后一条数据的来进行下一次查询。 这里需要说明一下,使用search_after查询需要将from设置为0或-1,当然你也可以不写

  第一次查询:

  

POST logdata-log-center-2021.05.06/_search{ "size": 2, "query": { "exists": { "field": "networkLogDetailInfo" } }, "_source": { "includes": ["messageId", "system*", "service*", "timestamp"], "excludes": [] }, "sort": [ { "timestamp": { "order": "desc" } } ]}

查询结果:可以看到每一条数据都有一个sort部分,而下一页的查询需要本次查询结果最后一条的sort值作为游标,实现分页查询

 

  

 

  第二次查询:

  

POST logdata-log-center-2021.05.06/_search{ "search_after": [ 1620374316433 ], "size": 2, "query": { "exists": { "field": "networkLogDetailInfo" } }, "_source": { "includes": ["messageId", "system*", "service*", "timestamp"], "excludes": [] }, "sort": [ { "timestamp": { "order": "desc" } } ]}

Java API

 

  

public void testSearchAfter(Object[] values) throws IOException { //查询源构建器 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.size(2); searchSourceBuilder.from(0); //searchAfter需要将from设置为0或-1,当然也可以不写 //查询条件 searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo")); //筛选字段,第一个参数为需要的字段,第二个参数为不需要的字段 searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {}); //以时间戳排序 searchSourceBuilder.sort("timestamp", SortOrder.DESC); if (values != null) searchSourceBuilder.searchAfter(values); SearchRequest request = new SearchRequest("logdata-log-center-2021.05.06"); request.source(searchSourceBuilder); SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);&am      

	  
	  
	  
	  
	  
	  
        

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: