ElasticSearch查询
# 默认情况下,es一次展示10条数据,通过from和size来控制分页 # 查询结果详解 GET goods/_search { "query": { "match_all": {} }, "from": 0, "size": 100 } GET goods
/** * 查询所有 * 1. matchAll * 2. 将查询结果封装为Goods对象,装载到List中 * 3. 分页。默认显示10条 */ @Test public void matchAll() throws IOException { //2. 构建查询请求对象,指定查询的索引名称 SearchRequest searchRequest=new SearchRequest("goods"); //4. 创建查询条件构建器SearchSourceBuilder SearchSourceBuilder sourceBuilder=new SearchSourceBuilder(); //6. 查询条件 QueryBuilder queryBuilder= QueryBuilders.matchAllQuery(); //5. 指定查询条件 sourceBuilder.query(queryBuilder); //3. 添加查询条件构建器 SearchSourceBuilder searchRequest.source(sourceBuilder); // 8 . 添加分页信息 不设置 默认10条 // sourceBuilder.from(0); // sourceBuilder.size(100); //1. 查询,获取查询结果 SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); //7. 获取命中对象 SearchHits SearchHits hits = searchResponse.getHits(); //7.1 获取总记录数 Long total= hits.getTotalHits().value; System.out.println("总数:"+total); //7.2 获取Hits数据 数组 SearchHit[] hits1 = hits.getHits(); //获取json字符串格式的数据 List<Goods> goodsList = new ArrayList<>(); for (SearchHit searchHit : hits1) { String sourceAsString = searchHit.getSourceAsString(); //转为java对象 Goods goods = JSON.parseObject(sourceAsString, Goods.class); goodsList.add(goods); } for (Goods goods : goodsList) { System.out.println(goods); } }
设置条件的疑问点
term查询和字段类型有关系,首先回顾一下ElasticSearch两个数据类型
ElasticSearch两个数据类型
text:会分词,不支持聚合
keyword:不会分词,将全部内容作为一个词条,支持聚合
term查询:不会对查询条件进行分词。
GET goods/_search { "query": { "term": { "title": { "value": "华为" } } } }
term查询,查询text类型字段时,只有其中的单词相匹配都会查到,text字段会对数据进行分词
例如:查询title 为“华为”的,title type 为text
查询categoryName 字段时,categoryName字段为keyword ,keyword:不会分词,将全部内容作为一个词条,
GET goods/_search { "query": { "term": { "categoryName": { "value": "华为手机" } } } }
match查询:
会对查询条件进行分词。
然后将分词后的查询条件和词条进行等值匹配
默认取并集(OR)
# match查询 GET goods/_search { "query": { "match": { "title": "华为手机" } }, "size": 500 }
match 的默认搜索(or 并集)
例如:华为手机,会分词为 “华为”,“手机” 只要出现其中一个词条都会搜索到
match的 and(交集) 搜索
例如:例如:华为手机,会分词为 “华为”,“手机” 但要求“华为”,和“手机”同时出现在词条中
总结:
-
term query会去倒排索引中寻找确切的term,它并不知道分词器的存在。这种查询适合keyword 、numeric、date
-
wildcard查询
wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
"*华*" 包含华字的 "华*" 华字后边多个字符 "华?" 华字后边多个字符 "*华"或"?华" 会引发全表(全索引)扫描 注意效率问题
# wildcard 查询。查询条件分词,模糊查询 GET goods/_search { "query": { "wildcard": { "title": { "value": "华*" } } } }
\W:匹配包括下划线的任何单词字符,等价于 [A-Z a-z 0-9_] 开头的反斜杠是转义符 +号多次出现 (.)*为任意字符 正则查询取决于正则表达式的效率
GET goods/_search { "query": { "regexp": { "title": "\\w+(.)*" } } }
前缀查询
对keyword类型支持比较好
# 前缀查询 对keyword类型支持比较好 GET goods/_search { "query": { "prefix": { "brandName": { "value": "三" } } } }
//模糊查询 WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");//华后多个字符 //正则查询 RegexpQueryBuilder query = QueryBuilders.regexpQuery("title", "\\w+(.)*"); //前缀查询 PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "三");
# 范围查询 GET goods/_search { "query": { "range": { "price": { "gte": 2000, "lte": 3000 } } }, "sort": [ { "price": { "order": "desc" } } ] }
//范围查询 以price 价格为条件 RangeQueryBuilder query = QueryBuilders.rangeQuery("price"); //指定下限 query.gte(2000); //指定上限 query.lte(3000); sourceBuilder.query(query); //排序 价格 降序排列 sourceBuilder.sort("price",SortOrder.DESC);
queryString查询
queryString 多条件查询
•会对查询条件进行分词。
•然后将分词后的查询条件和词条进行等值匹配
•默认取并集(OR)
•可以指定多个查询字段
# queryString GET goods/_search { "query": { "query_string": { "fields": ["title","categoryName","brandName"], "query": "华为 AND 手机" } } }
simple_query_string:不识别query中的连接符(or 、and),查询时会将 “华为”、"and"、“手机”分别进行查询
GET goods/_search { "query": { "simple_query_string": { "fields": ["title","categoryName","brandName"], "query": "华为 AND 手机" } } }
query_string:有default_operator连接符的脚本
GET goods/_search { "query": { "query_string": { "fields": ["title","brandName","categoryName"], "query": "华为手机 " , "default_operator": "AND" } } }
java代码
QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机").field("title").field("categoryName")
.field("brandName").defaultOperator(Operator.AND);
simple_query_string:有default_operator连接符的脚本
GET goods/_search { "query": { "simple_query_string": { "fields": ["title","brandName","categoryName"], "query": "华为手机 " , "default_operator": "OR" } } }
注意:query中的or and 是查询时 匹配条件是否同时出现----or 出现一个即可,and 两个条件同时出现
default_operator的or and 是对结果进行 并集(or)、交集(and)
布尔查询-脚本
boolQuery:对多个查询条件连接。连接方式:
•must(and):条件必须成立
•must_not(not):条件必须不成立
•filter:条件必须成立,性能比must高。不会计算得分
得分:即条件匹配度,匹配度越高,得分越高
# boolquery #must和filter配合使用时,max_score(得分)是显示的 #must 默认数组形式 GET goods/_search { "query": { "bool": { "must": [ { "term": { "brandName": { "value": "华为" } } } ], "filter":[ { "term": { "title": "手机" } }, { "range":{ "price": { "gte": 2000, "lte": 3000 } } } ] } } } #filter 单独使用 filter可以是单个条件,也可多个条件(数组形式) GET goods/_search { "query": { "bool": { "filter": [ { "term": { "brandName": { "value": "华为" } } } ] } } }
布尔查询 -- JavaAPI
布尔查询:boolQuery
-
查询品牌名称为:华为
-
查询标题包含:手机
-
查询价格在:2000-3000
must 、filter为连接方式
term、match为不同的查询方式
//1.构建boolQuery BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); //2.构建各个查询条件 //2.1 查询品牌名称为:华为 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brandName", "华为"); boolQuery.must(termQueryBuilder); //2.2. 查询标题包含:手机 MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", "手机"); boolQuery.filter(matchQuery); //2.3 查询价格在:2000-3000 RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price"); rangeQuery.gte(2000); rangeQuery.lte(3000); boolQuery.filter(rangeQuery); sourceBuilder.query(boolQuery);
聚合查询-脚本
•指标聚合:相当于MySQL的聚合函数。max、min、avg、sum等
# 聚合查询 # 指标聚合 聚合函数 GET goods/_search { "query": { "match": { "title": "手机" } }, "aggs": { "max_price": { "max": { "field": "price" } } } } # 桶聚合 分组 GET goods/_search { "query": { "match": { "title": "手机" } }, "aggs": { "goods_brands": { "terms": { "field": "brandName", "size": 100 } } } }
聚合查询-JavaAPI
聚合查询:桶聚合,分组查询
-
查询title包含手机的数据
-
/** * 聚合查询:桶聚合,分组查询 * 1. 查询title包含手机的数据 * 2. 查询品牌列表 */ @Test public void testAggQuery() throws IOException { SearchRequest searchRequest=new SearchRequest("goods"); SearchSourceBuilder sourceBuilder=new SearchSourceBuilder(); //1. 查询title包含手机的数据 MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "手机"); sourceBuilder.query(queryBuilder); //2. 查询品牌列表 只展示前100条 AggregationBuilder aggregation=AggregationBuilders.terms("goods_brands").field("brandName").size(100); sourceBuilder.aggregation(aggregation); searchRequest.source(sourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); //7. 获取命中对象 SearchHits SearchHits hits = searchResponse.getHits(); //7.1 获取总记录数 Long total= hits.getTotalHits().value; System.out.println("总数:"+total); // aggregations 对象 Aggregations aggregations = searchResponse.getAggregations(); //将aggregations 转化为map Map<String, Aggregation> aggregationMap = aggregations.asMap(); //通过key获取goods_brands 对象 使用Aggregation的子类接收 buckets属性在Terms接口中体现 // Aggregation goods_brands1 = aggregationMap.get("goods_brands"); Terms goods_brands =(Terms) aggregationMap.get("goods_brands"); //获取buckets 数组集合 List<? extends Terms.Bucket> buckets = goods_brands.getBuckets(); Map<String,Object>map=new HashMap<>(); //遍历buckets key 属性名,doc_count 统计聚合数 for (Terms.Bucket bucket : buckets) { System.out.println(bucket.getKey()); map.put(bucket.getKeyAsString(),bucket.getDocCount()); } System.out.println(map); }
高亮查询-脚本
高亮三要素:
•高亮字段
•前缀
•后缀
<em>手机</em>
GET goods/_search { "query": { "match": { "title": "电视" } }, "highlight": { "fields": { "title": { "pre_tags": "<font color='red'>", "post_tags": "</font>" } } } }
实施步骤: 高亮查询:
-
-
将高亮了的字段数据,替换原有数据
/** * * 高亮查询: * 1. 设置高亮 * * 高亮字段 * * 前缀 * * 后缀 * 2. 将高亮了的字段数据,替换原有数据 */ @Test public void testHighLightQuery() throws IOException { SearchRequest searchRequest = new SearchRequest("goods"); SearchSourceBuilder sourceBulider = new SearchSourceBuilder(); // 1. 查询title包含手机的数据 MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机"); sourceBulider.query(query); //设置高亮 HighlightBuilder highlighter = new HighlightBuilder(); //设置三要素 highlighter.field("title"); //设置前后缀标签 highlighter.preTags("<font color='red'>"); highlighter.postTags("</font>"); //加载已经设置好的高亮配置 sourceBulider.highlighter(highlighter); searchRequest.source(sourceBulider); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); SearchHits searchHits = searchResponse.getHits(); //获取记录数 long value = searchHits.getTotalHits().value; System.out.println("总记录数:"+value); List<Goods> goodsList = new ArrayList<>(); SearchHit[] hits = searchHits.getHits(); for (SearchHit hit : hits) { String sourceAsString = hit.getSourceAsString(); //转为java Goods goods = JSON.parseObject(sourceAsString, Goods.class); // 获取高亮结果,替换goods中的title Map<String, HighlightField> highlightFields = hit.getHighlightFields(); HighlightField HighlightField = highlightFields.get("title"); Text[] fragments = HighlightField.fragments(); //highlight title替换 替换goods中的title goods.setTitle(fragments[0].toString()); goodsList.add(goods); } for (Goods goods : goodsList) { System.out.println(goods); } }
#查询别名 默认别名无法查看,默认别名同索引名 GET goods/_alias/ #结果 { "goods" : { "aliases" : { } } }
1.新建student_index_v1索引
# -------重建索引----------- # 新建student_index_v1。索引名称必须全部小写 PUT student_index_v1 { "mappings": { "properties": { "birthday":{ "type": "date" } } } } #查看 student_index_v1 结构 GET student_index_v1 #添加数据 PUT student_index_v1/_doc/1 { "birthday":"1999-11-11" } #查看数据 GET student_index_v1/_search #添加数据 PUT student_index_v1/_doc/1 { "birthday":"1999年11月11日" }
2.重建索引:将student_index_v1 数据拷贝到 student_index_v2
# 业务变更了,需要改变birthday字段的类型为text # 1. 创建新的索引 student_index_v2 # 2. 将student_index_v1 数据拷贝到 student_index_v2 # 创建新的索引 student_index_v2 PUT student_index_v2 { "mappings": { "properties": { "birthday":{ "type": "text" } } } } # 将student_index_v1 数据拷贝到 student_index_v2 # _reindex 拷贝数据 POST _reindex { "source": { "index": "student_index_v1" }, "dest": { "index": "student_index_v2" } } GET student_index_v2/_search PUT student_index_v2/_doc/2 { "birthday":"1999年11月11日" }
3.创建索引库别名:
# 思考: 现在java代码中操作es,还是使用的实student_index_v1老的索引名称。 # 1. 改代码(不推荐) # 2. 索引别名(推荐) # 步骤: # 0. 先删除student_index_v1 # 1. 给student_index_v2起个别名 student_index_v1 # 先删除student_index_v1 #DELETE student_index_v1 这一操作将删除student_index_v1索引库 #索引库默认的别名与索引库同名,无法删除 # 给student_index_v1起个别名 student_index_v11 POST student_index_v2/_alias/student_index_v11 #测试删除命令 POST /_aliases { "actions": [ {"remove": {"index": "student_index_v1", "alias": "student_index_v11"}} ] } # 给student_index_v2起个别名 student_index_v1 POST student_index_v2/_alias/student_index_v1 #查询别名 GET goods/_alias/ GET student_index_v1/_search GET student_index_v2/_search