查询类型

查询所有:查询出所有数据,类型是match_all

全文检索:利用分词器对用户输入的内容分词,然后去倒排索引中match_query和multi_match_query类型

精确查询:根据精确词条查找数据,一般是keyword、数值、日期、boolean类型,有ids、range、term类型

地理查询:根据经纬度查询,有geo_distance和geo_bounding_box类型

复合查询:复合查询是将以上所有条件组合起来,合并查询条件,比如function score查询:算分函数查询,可以控制相关性算分,控制文档排名 还有Boolean query查询,组合条件查询

下面通过DEV_TOOL工具的DSL语句演示几种常用查询

查询所有,match查询

#查询所有  match_all指的是查询类型,表示查询所有
GET /hotel/_search
{
  "query": {
    "match_all": {    
    }
  }
}

全文检索,match_query查询

#根据条件查询  all是一个字段,后面表示查询内容
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "7天"
    }
  }
}

全文检索,multi_match_query查询,根据多个条件来查询

#根据多个字段查询 第二个query表示查询条件,fields表示查询的字段,这里是多个字段
GET /hotel/_search
{
  "query": {
    "multi_match": {
     "query": "7天",
     "fields": ["brand","name"]
    }
  }
}

精确查询,term查询

#term查询(精确查询),字段一般是keyword类型,没有分词 city是字段,value表示查询字段的值
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "北京"
      }
    }
  }
}

range查询,根据范围查询,通常都是数字一类的

#range查询。根据范围查询,gte是大于等于。lte是小于等于,去掉e则没有等于
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte":300,
        "lte":700
      }
    }
  }
}

地理查询 geo_distance查询,指定一个坐标点,根据范围来查询,需在索引库设置一个经纬度字段,这里设置的是location

#地理坐标查询  "geo_distance"是根据指定位置查询,给一个坐标点,然后设置在查询多少km之内的条件
GET /hotel/_search
{
  "query": {
    "geo_distance":{
      "distance":"2km",
    "location":"31.21,121.5"
      
    }
  }
}

地理查询 geo_bounding_box查询,根据两个点组成一个矩形范围来查询,lat是纬度,lon是经度

#地理坐标查询  geo_bounding_box 是分别根据左上和右下两个点组成的矩形范围进行查找
GET /hotel/_search 
{
  "query":{
    "geo_bounding_box":{
      "location":{
     "top_left":{"lat":"31.21",
     "lon":"121.5"},
     "bottom_right":{"lat":"30.21",
     "lon":"122.5"
     }
      }
    }
  }
}

复合查询:function score类型,通过相关性算分,分数越高排名越靠前,在es5.0之前用的是TF-IDF算法,es5.0之后用的是BM25算法,这里引用黑马视频截图说明

可以对比一下两种算法的区别,BM25算法会随着词频增大而增大,但增长曲线会趋于水平 而function score就是在原始的算分方法之上,通过修改算分函数,来修改分数,分高者排名靠前

 具体语法如下

 根据上述模式写一个dsl语句

#function score查询  这里表示搜索外滩字段时可能会有多个酒店品牌,通过修改将字段brand为“如家”的酒店的权重增加10分,使其排名靠前
GET /hotel/_search
{
  "query":{
    "function_score":{
      "query":{
        "match":{
          "all":"外滩"
        }
      },
      "functions": [
        {"filter": {
          "term": {
            "brand": "如家"
          }
        },
      "weight": 10
        }
      ],
      "boost_mode": "sum"
    }
  }
}

复合查询:Boolean query查询。布尔查询是一个或多个查询子句的组合,组合方式有:

  • must:必须匹配每个子查询,类似与“与”
  • should:选择性匹配子查询,类似于“或”
  • must_not:必须不匹配,不参与算分,类似于“非”
  • filter:必须匹配,不参与算分,不参与算分可以提高性能

下面写一个dsl语句,要求查询"如家"或者是"君悦"品牌,价格不高于1000,距离自定义坐标点不超过10km。  根据分析可知,搜索的是两个品牌中的一个都可以,所以是"或"关系,并且品牌参与算分 ,所以用should,价格不高于1000,而且价格不参与算分,可以用must_not,距离也不参与算分,用filter,如果用must_not则是10km之外的酒店,所以不可取

GET hotel/_search
{
  "query":{
    "bool": {
      "should": [
        {"match": {
          "brand": "如家"
        }},
          {"match": {
            "brand": "君悦"
          }}
        
      ],
      "must_not": [
        {"range": {
          "price": {
            "gt": 1000
          }
        }}
      ],
      "filter": [
       {"geo_distance": {
          "distance": "10km",
          "location": "31.220706, 121.498769"
        } }
      ]
    }
  }
}

搜索结果排序处理——将查询出的结果排序

排序结果默认是相关性算分排序,排序的字段类型有keyword类型、数值类型、地理坐标类型、日期类型,除了地理坐标外,其他语法基本相同,语法如下

#搜索结果排序处理  sort和query同级,里面添加可排序的字段,选择升序或降序
GET /hotel/_search
{
  "query":{
    "function_score":{
      "query":{
        "match":{
          "all":"外滩"
        }
      }
      
    }
  },
  "sort":[
       { "price":"desc"},
       {"socre":"desc"}
        ]
}

这里表示先按照price字段排序,如果值相等,再按照score字段排序

还有一个是地理坐标排序,语法如下

#搜索结果排序处理  地理坐标排序 _geo_distance表示地理坐标排序  
#location是一个经纬度字段 然后设置经纬度 通过order来排序
GET /hotel/_search
{
  "query":{
    "function_score":{
      "query":{
        "match":{
          "all":"外滩"
        }
      }
      
    }
  },
  "sort":[
       {"_geo_distance": {
         "location": {
           "lat": 32,
           "lon": 122
         },
         "order": "asc"
       }}
        ]
}

处理结果分页

正常情况下默认分页10条,也可以手动指定,语法如下

#搜索结果分页处理  from表示从第几条数据开始显示  size表示要分页多少页
#这里表示从第10条数据开始显示 总共只显示5条
GET /hotel/_search
{
  "query":{
   "match": {
     "all": "酒店"
   }
  },
  "from": 10,
  "size": 5
}

es分页原理是先把所有数据查出来,再根据我们设置的分页去截这一段数据,考虑到es存在多个集群,如果集群过多且分页数过大,会造成极大的内存消耗,所以es官方只运行最大分页到10000

比如:

GET /hotel/_search
{
  "query":{
   "match": {
     "all": "酒店"
   }
  },
  "from": 9995,
  "size": 6
}

#当出现从第9995条数据开始,往后查6条,实际上就是第10001条数据,这时候就会报错,官方最多允许查10000条数据

如果要查10000之后的数据,可以用search after,分页时需要先排序,原理是从上一次的排序值开始,查询下一页数据,该方法只能向后分页,不能向前分页

scroll:原理是将排序数据形成快照,保存在内存,不推荐使用 。 内存消耗大,而且因为是快照,所以如果有数据更新了也无法及时获取新数据

高亮处理

将查询到的关键字进行高亮处理,方便显示,比如当搜索百度时,这个关键字就会变红,这就是做了高亮处理

 语法如下,高亮添加只能用match,不要用match_all,全部搜索就没有高亮的意义了,这里表示对酒店进行高亮

#高亮处理  highlight表示高亮 fields表示需要高亮的字段,这里是name
#require_field_match表示是否需要和搜索的字段匹配,默认是true,因为我的搜索字段all,如果默认匹配的话和我要高亮的name字段就不匹配了,所以我选择false,正常情况如果我的搜索字段和高亮字段一致可以不写
# pre_tags表示要在搜索字段前面添加的标签,默认就是<em>,也可以不写出来  "post_tags"则是在后面添加的标签  添加标签后前端才好对其设置样式显示高亮
 
GET /hotel/_search
{
  "query":{
   "match": {
     "all": "酒店"
   }
  },
  "highlight": {
    "fields": {"name":{ "require_field_match": "false"}}, 
     "pre_tags": "<em>",
    "post_tags": "</em>"
   
  }
  
}

ResrClient操作es

Match_all查询

//首先初始化client

    @Autowired
    private IHotelService hotelService;
    //声明对象,并做对象初始化连接到es,提高代码复用性
    private RestHighLevelClient client;

    @BeforeEach
    void set() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.65.129:9200")//es地址
        ));
    }

    @AfterEach
    void terDown() throws IOException {
        this.client.close();
    }


 @Test  //常用的查询方式
    void testMatchall() throws IOException {
        //1、准备请求
        SearchRequest request = new SearchRequest("hotel");
        //2、准备dsl,这里官方已经封装好了,直接调方法来用就行了 matchAll类型
        request.source().query(QueryBuilders.matchAllQuery());
  //3、发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //代码抽取后引用
        handleReponse(response);
    }


 //抽取重复代码
    private void handleReponse(SearchResponse response) {
        //4、解析响应
        SearchHits searchHits = response.getHits();
        //4.1获取总条数
        long total = searchHits.getTotalHits().value;
        System.out.println("总共搜索到" + total + "条数据");
        //4.2文档数据
        SearchHit[] hits = searchHits.getHits();
        //遍历
        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);
        }

    }
}

Match查询

 @Test  //常用的查询方式
    void testMatch() throws IOException {
        //1、准备请求
        SearchRequest request = new SearchRequest("hotel");
        //2、准备dsl match类型
        //request.source().query(QueryBuilders.matchQuery("all", "如家")); //里面的参数分别为搜索的字段名字和搜索的值
 //3、发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //代码抽取后引用
        handleReponse(response);

multi_match查询   term查询 range查询

 @Test  //常用的查询方式
    void testMatch2() throws IOException {
        //1、准备请求
        SearchRequest request = new SearchRequest("hotel");
        //2、准备dsl match类型
        //multi_match类型
        request.source().query(QueryBuilders.multiMatchQuery("如家","all","name"));//里面参数分别是要搜索的字段值,然后是要搜索的字段名字
        //term查询
        request.source().query(QueryBuilders.termQuery("city","北京"));//对应的参数分别是字段名称,要查询的字段的值
        //range查询
        request.source().query(QueryBuilders.rangeQuery("price").gte(200).lte(500));//对应的参数是字段名字,后面接一个取值范围,大于多少小于多少

 //3、发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //代码抽取后引用
        handleReponse(response);

复合查询及其分页操作

@Test  //复合查询
    void testComplexMatch() throws IOException {
        //1、准备请求
        SearchRequest request = new SearchRequest("hotel");
        //创建BoolQueryBuilder对象,通过对象指定"与","或"等等,然后再指定查询的方法
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("杭州","北京"));
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(200).lte(500));
        //准备dsl
        request.source().query(boolQueryBuilder);
        //分页查询
        request.source().from(5).size(10); //表示查询第五页的10条数据
        //搜索结果排序
        request.source().sort("price", SortOrder.ASC);
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //代码抽取后引用
        handleReponse(response);

    }

搜索结果高亮处理

 //搜索结果的高亮处理
    @Test

    void testgaoliang() throws IOException {
        //1、准备请求
        SearchRequest request = new SearchRequest("hotel");
        //2、准备dsl
        request.source().query(QueryBuilders.matchQuery("all","如家"));
        //高亮  设置高亮字段和设置是否让高亮字段和搜索字段匹配,默认是true,这里选false
        request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //代码抽取后引用
        handleReponse(response);


    }

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐