elasticsearch查询
ElasticSearch查询
- 基本查询
- 分词查询
- 词条查询
- 范围查询
- 布尔查询
- filter功能
- source筛选
- 排序
- 分页
- 高亮
- 聚合
查询所有-matchAll
GET /user/_search
{
"query": {
"match_all": {}
}
}
java Api
思路分析:
- 创建SearchSourceBuilder对象
- 添加查询条件QueryBuilders
- 如:添加排序、分页等其它条件
- 创建SearchRequest对象,并制定索引库名称
- 添加SearchSourceBuilder对象到SearchRequest对象source中
- 发起请求,得到结果
- 解析结果SearchResponse
- 获取总条数
- 获取SearchHits数组,并遍历
- 获取其中的
_source
,是JSON数据 - 把
_source
反序列化为User对象
- 获取其中的
public void basicMatch() throws IOException {
//1.创建SearchSourceBuilder对象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//添加查询条件QueryBuilders,这里选择match_all
sourceBuilder.query(QueryBuilders.matchAllQuery());
//创建SearchRequest对象,并指定索引库名称
SearchRequest request = new SearchRequest("user");
//添加SearchSourceBuilder对象到SearchRequest中
request.source(sourceBuilder);
//发送请求,得到结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析结果
SearchHits hits = response.getHits();
//获取总条数
long total = hits.getTotalHits().value;
System.out.println("总的数目:" +total);
//获取SearchHits数组,拿到查询结果
SearchHit[] hitsHits = hits.getHits();
for (SearchHit hit : hitsHits) {
//获取得分
float score = hit.getScore();
System.out.println("分数:"+score );
//获得其中的_source, 是json数据
String json = hit.getSourceAsString();
//把得到的json数据转化为User对象
User user = JSON.parseObject(json, User.class);
System.out.println(user);
}
}
词条查询-termQuery
将查询中的字符串当做一个词条。
term查询
和字段类型有关系
Elasticsearch中有两个数据类型
- text:会分词,不支持聚合
- keyword:不会分词,支持聚合
# keyword类型
GET /user/_search
{
"query": {
"term": {
"name": {
"value": "小红"
}
}
}
}
#text类型
GET /user/_search
{
"query": {
"term": {
"note": {
"value": "主角"
}
}
}
}
JavaApi
思路分析:
- 创建SearchSourceBuilder对象
- 添加查询条件QueryBuilders.termQuery()
- 创建SearchRequest对象,并制定索引库名称
- 添加SearchSourceBuilder对象到SearchRequest对象source中
- 发起请求,得到结果
- 解析结果SearchResponse
- 获取总条数
- 获取SearchHits数组,并遍历
- 获取其中的
_source
,是JSON数据 - 把
_source
反序列化为User对象
- 获取其中的
public void termMatch() throws IOException {
//1.创建SearchSourceBuilder对象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//添加查询条件QueryBuilders,这里选择term_query
sourceBuilder.query(QueryBuilders.termQuery("note","主角"));
//创建SearchRequest对象,并指定索引库名称
SearchRequest request = new SearchRequest("user");
//添加SearchSourceBuilder对象到SearchRequest中
request.source(sourceBuilder);
//发送请求,得到结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析结果
SearchHits hits = response.getHits();
//获取总条数
long total = hits.getTotalHits().value;
System.out.println("总的数目:" +total);
//获取SearchHits数组,拿到查询结果
SearchHit[] hitsHits = hits.getHits();
for (SearchHit hit : hitsHits) {
//获取得分
float score = hit.getScore();
System.out.println("分数:"+score );
//获得其中的_source, 是json数据
String json = hit.getSourceAsString();
//把得到的json数据转化为User对象
User user = JSON.parseObject(json, User.class);
System.out.println(user);
}
}
term查询特点对搜索的关键词不分词查询
分词匹配查询-matchQuery
match查询:
- 会对查询条件进行分词
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集
GET /user/_search
{
"query": {
"match": {
"note": "小红是主角"
}
}
}
JavaApi
public void matchQuery() throws IOException {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "小红是主角");
//指定分词器
// matchQueryBuilder.analyzer("ik_max_word");
//分词后各个词之间的关系默认是OR
matchQueryBuilder.operator(Operator.OR);
parseResponse(matchQueryBuilder);
}
private void parseResponse(QueryBuilder queryBuilder) throws IOException {
SearchSourceBuilder query = new SearchSourceBuilder();
query.query(queryBuilder);
//创建SearchRequest对象,并指定索引库名称
SearchRequest request = new SearchRequest("user");
//添加SearchSourceBuilder对象到SearchRequest中
request.source(query);
//发送请求,得到结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析结果
SearchHits hits = response.getHits();
//获取总条数
long total = hits.getTotalHits().value;
System.out.println("总的数目:" + total);
//获取SearchHits数组,拿到查询结果
SearchHit[] hitsHits = hits.getHits();
for (SearchHit hit : hitsHits) {
//获取得分
float score = hit.getScore();
System.out.println("分数:" + score);
//获得其中的_source, 是json数据
String json = hit.getSourceAsString();
//把得到的json数据转化为User对象
User user = JSON.parseObject(json, User.class);
System.out.println(user);
}
}
- term query 会将查询条件当做一个term去倒排索引中寻找,适合
keyword
、numeric
、date
查询
模糊查询-fuzzy
fuzzy查询默认会按照一定的规则来修正当前查询的条件。
GET /user/_search
{
"query": {
"fuzzy": {
"note": {
"value": "我是主角",
"fuzziness": 2
}
}
}
}
JavaApi
public void fuzzyQuery() throws IOException {
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("note", "我是主角");
fuzzyQueryBuilder.fuzziness(Fuzziness.TWO);
parseResponse(fuzzyQueryBuilder); //见上面解析应答函数
}
范围&排序查询-range&sort
//查询年龄在20到28之间的数据,(左闭右开)
//再先按年龄倒序,在按id倒序
GET /user/_search
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 28
}
}
},
"sort": [
{
"age": {
"order": "desc"
}
},
{
"id":{
"order": "desc"
}
}
]
}
JavaApi
- 构建rangeQuery对象
- 在sourceBuilder添加排序条件,(排序是对结果的重组,对条件不产生影响)
public void rangeQuery() throws IOException {
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age");
rangeQueryBuilder.gte(20);
rangeQueryBuilder.lte(28);
parseResponse(rangeQueryBuilder);
}
//在parseResponse中添加排序规则
//在构建query之后构建
query.sort("age", SortOrder.DESC);
query.sort("id",SortOrder.DESC);
多条件查询-queryString
- queryString会对查询条件进行分词。
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集(OR)default_operator
- 可以指定多个查询字段
query_string:识别query中的连接符(or 、and)
GET /user/_search
{
"query": {
"query_string": {
"fields":["name","note"],
"query": "我是主角" //能查到4条记录 "我是 AND 主角" //一条记录都查询不到
}
}
}
query_string:有default_operator连接符的脚本
GET /user/_search
{
"query": {
"query_string": {
"fields":["name","note"],
"query": "我是主角",
"default_operator": "AND" //一条记录都查不到
}
}
}
默认搜索域:如果我们查询时不指定搜索的域,那么就按照默认给定的搜索域查询
GET /user/_search
{
"query": {
"query_string": {
//"fields":["name","note"],
"query": "我是主角",
"default_operator": "AND",
"default_field": "note" //设置默认搜索域
}
}
}
simple_query_string
:不识别query中的连接符(or 、and),查询时会将条件分词后分别进行查询
GET /user/_search
{
"query": {
"simple_query_string": {
"query": "我是 AND 主角", //查到4条记录
"fields": ["name","note"]
}
}
}
JavaApi
public void queryStringQuery() throws IOException {
QueryStringQueryBuilder stringQueryBuilder = QueryBuilders
.queryStringQuery("我是主角").field("name").field("note");
parseResponse(stringQueryBuilder);
}
bool查询&结果过滤-boolQuery
boolQuery:对多个查询条件连接。
连接方式:
- must(and):条件必须成立
- must_not(not):条件必须不成立
- should(or):条件可以成立
- filter:条件必须成立,性能比must高。不会计算得分
得分:即条件匹配度,匹配度越高,得分越高
默认情况下,所有的查询条件、过滤条件都会影响打分和排名。而对搜索结果打分是比较影响性能的,因此我们一般只对用户输入的搜索条件对应的字段打分,其它过滤项不打分。此时就不能简单实用布尔查询的must来组合条件了,而是实用filter
方式。
GET /user/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"note": "主角"
}
}
],
"filter": [
{
"term":{
"name":"Rose"
}
},
{
"range":{
"age":{
"gte":10,
"lte":20
}
}
}
]
}
}
}
JavaApi
public void boolQuery() throws IOException {
//构建bool条件对象
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//构建matchQuery对象,查询note中包含主角的信息
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("note", "主角");
boolQueryBuilder.should(matchQuery);
//过滤姓名包含rose的
TermQueryBuilder termQuery = QueryBuilders.termQuery("name", "Rose");
boolQueryBuilder.filter(termQuery);
//年龄在10-20之间的
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age").gte(10).lte(20);
boolQueryBuilder.filter(rangeQuery);
parseResponse(boolQueryBuilder);
}
分页查询-from
默认情况下ES会设置size=10,查询10条记录。 通过from
和size
来指定分页的开始位置及每页大小。
GET /user/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 2
}
JavaApi
public void basicMatch() throws IOException {
//1.创建SearchSourceBuilder对象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//添加查询条件QueryBuilders,这里选择match_all
//创建SearchRequest对象,并指定索引库名称
parseResponse(QueryBuilders.matchAllQuery());
}
//parseRespone中添加
query.from(0);
query.size(2);
其本质是逻辑分页,因此为了避免深度分页的问题,ES限制最多查到第10000条。
如果需要查询到10000以后的数据,你可以采用两种方式:
- scroll滚动查询
- search after
可以使用from
和对结果进行分页,size
但是当达到深度分页时,成本变得过高。index.max_result_window
默认值是10,000 ,这是一种保护措施,搜索请求占用的堆内存和时间成正比from + size
。建议使用滚动api进行有效的深度滚动,但是滚动上下文的成本很高,因此不建议将其用于实时用户请求。该search_after
参数通过提供活动光标来解决此问题。想法是使用上一页的结果来帮助下一页的检索
高亮查询-highlight
高亮是在搜索结果中把搜索关键字标记出来,因此必须使用match这样的条件搜索。
高亮要素:
- pre_tags:前置标签,可以省略,默认是em
- post_tags:后置标签,可以省略,默认是em
- fields:需要高亮的字段
- title:这里声明title字段需要高亮,后面可以为这个字段设置特有配置,也可以空
GET /user/_search
{
"query": {
"match": {
"note": "主角"
}
},
"highlight": {
"fields": {
"note":{
"pre_tags": "<font color='red'>",
"post_tags": "</font>"
}
}
}
}
JavaApi
public void matchQuery() throws IOException {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "主角");
//分词后各个词之间的关系默认是OR
matchQueryBuilder.operator(Operator.OR);
parseResponse(matchQueryBuilder);
}
//parseResponsez中添加
//构建高亮元素
HighlightBuilder highlight = SearchSourceBuilder.highlight();
highlight.field("note");
highlight.preTags("<font color='red'>");
highlight.postTags("</font>");
query.highlighter(highlight);
//解析高亮标签
HighlightField highlightField = hit.getHighlightFields().get("note");
Text[] fragments = highlightField.getFragments();
String note = Arrays.toString(fragments);
if(StringUtils.isNotBlank(note)){
user.setNote(note);
}
聚合查询-aggregation
聚合(aggregations)可以让我们极其方便的实现对数据的统计、分析。例如:
- 什么品牌的手机最受欢迎?
- 这些手机的平均价格、最高价格、最低价格?
- 这些手机每月的销售情况如何?
参与聚合的字段,必须是keyword类型
Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫桶
,一个叫度量
:
-
桶
桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个
桶
,例如我们根据国籍对人划分,可以得到中国桶
、英国桶
,日本桶
……或者我们按照年龄段对人进行划分:010,1020,2030,3040等。Elasticsearch中提供的划分桶的方式有很多:
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似,需要知道分组的间隔(interval)
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组,类似数据库group by
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
- ……
我们发现bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量
-
度量
分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为
度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
GET /car/_search
{
"aggs": {
"popular_colors": {
"terms": {
"field": "color.keyword"
}
}
},
"size": 0
}
JavaApi
- 创建SearchRequest对象,并制定索引库名称
- 创建SearchSourceBuilder对象,设置分组相关参数
- 添加SearchSourceBuilder对象到SearchRequest对象source中
- 执行查询
- 得到查询结果
- 解析分组查询数据
public void aggTest() throws IOException {
SearchRequest request = new SearchRequest("car");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.aggregation(AggregationBuilders.terms("popular_color").field("color.keyword"));
sourceBuilder.size(0);
request.source(sourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
long total = hits.getTotalHits().value;
System.out.println("总数:"+total);
Terms terms = (Terms)response.getAggregations().asMap().get("popular_color");
List<? extends Terms.Bucket> buckets = terms.getBuckets();
for (Terms.Bucket bucket : buckets) {
System.out.println(bucket.getKey() + ": " + bucket.getDocCount());
}
}
//总数:8
//红: 4
//绿: 2
//蓝: 2
//求出每种颜色的平均价格
GET /car/_search
{
"aggs": {
"popular_colors": {
"terms": {
"field": "color.keyword"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
},
"size": 0
}
TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("popular_color").field("color.keyword");
AvgAggregationBuilder avgAggregationBuilder = AggregationBuilders.avg("avg_price").field("price");
aggregationBuilder.subAggregation(avgAggregationBuilder);
sourceBuilder.aggregation(aggregationBuilder);
ParsedAvg parsedAvg = (ParsedAvg)bucket.getAggregations().getAsMap().get("avg_price");
System.out.println(bucket.getKey()+ "平均价格"+ parsedAvg.getValue());
//总数:8
//红: 4
//红平均价格32500.0
//绿: 2
//绿平均价格21000.0
//蓝: 2
//蓝平均价格20000.0