1.查询的分类

    • 查询所有:查询出所有数据,一般测试用。例如:match_all
    • 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
      • match_query
      • multi_match_query
    • 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
      • ids
      • range
      • term
    • 地理(geo)查询:根据经纬度查询。例如:
      • geo_distance
      • geo_bounding_box
    • 复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
      • bool
      • function_score

    查询的语法基本一致:

    1. GET /indexName/_search
    2. {
    3. "query": {
    4. "查询类型": {
    5. "查询条件": "条件值"
    6. }
    7. }
    8. }

    2.全文检索查询

    • match查询:单字段查询
    • multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件

    3.精准查询
    精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。
    3.1term查询

    1. // term查询
    2. GET /indexName/_search
    3. {
    4. "query": {
    5. "term": {
    6. "FIELD": {
    7. "value": "VALUE"
    8. }
    9. }
    10. }
    11. }

    3.2range查询 范围查询

    1. // range查询
    2. GET /indexName/_search
    3. {
    4. "query": {
    5. "range": {
    6. "FIELD": {
    7. "gte": 10, // 这里的gte代表大于等于,gt则代表大于
    8. "lte": 20 // lte代表小于等于,lt则代表小于
    9. }
    10. }
    11. }
    12. }

    4.地理坐标查询
    地理坐标查询,其实就是根据经纬度查询
    4.1矩形范围查询

    1. GET hotel/_search
    2. {
    3. "query":{
    4. "geo_bounding_box":{
    5. "location":{
    6. "top_left": {
    7. "lat": 31.1,
    8. "lon": 121.5
    9. },
    10. "bottom_right":{
    11. "lat": 30.9,
    12. "lon": 121.7
    13. }
    14. }
    15. }
    16. }
    17. }

    4.2附近查询

    1. // geo_distance 查询
    2. GET /indexName/_search
    3. {
    4. "query": {
    5. "geo_distance": {
    6. "distance": "15km", // 半径
    7. "FIELD": "31.21,121.5" // 圆心
    8. }
    9. }
    10. }

    5.复合查询
    5.1算分函数查询
    function score 查询中包含四部分内容:

    • 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)
    • 过滤条件:filter部分,符合该条件的文档才会重新算分
    • 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数
      • weight:函数结果是常量
      • field_value_factor:以文档中的某个字段值作为函数结果
      • random_score:以随机数作为函数结果
      • script_score:自定义算分函数算法
    • 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
      • multiply:相乘
      • replace:用function score替换query score
      • 其它,例如:sum、avg、max、min

    function score的运行流程如下:

    • 1)根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
    • 2)根据过滤条件,过滤文档
    • 3)符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
    • 4)将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。

    function score query定义的三要素是什么?

    • 过滤条件:哪些文档要加分
    • 算分函数:如何计算function score
    • 加权方式:function score 与 query score如何运算

    5.2布尔查询
    布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:

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

    需要注意的是,搜索时,参与打分的字段越多,查询的性能也越差。因此这种多条件查询时,建议这样做:

    • 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分
    • 其它过滤条件,采用filter查询。不参与算分

    6.搜索结果处理
    6.1排序
    普通字段排序

    1. GET /indexName/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "sort": [
    7. {
    8. "FIELD": "desc" // 排序字段、排序方式ASC、DESC
    9. }
    10. ]
    11. }

    地理坐标排序

    1. GET /indexName/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "sort": [
    7. {
    8. "_geo_distance" : {
    9. "FIELD" : "纬度,经度", // 文档中geo_point类型的字段名、目标坐标点
    10. "order" : "asc", // 排序方式
    11. "unit" : "km" // 排序的距离单位
    12. }
    13. }
    14. ]
    15. }

    6.2分页

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "from": 0, // 分页开始的位置,默认为0
    7. "size": 10, // 期望获取的文档总数
    8. "sort": [
    9. {"price": "asc"}
    10. ]
    11. }

    6.3高亮

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "match": {
    5. "FIELD": "TEXT" // 查询条件,高亮一定要使用全文检索查询
    6. }
    7. },
    8. "highlight": {
    9. "fields": { // 指定要高亮的字段
    10. "FIELD": {
    11. "pre_tags": "<em>", // 用来标记高亮字段的前置标签
    12. "post_tags": "</em>" // 用来标记高亮字段的后置标签
    13. }
    14. }
    15. }
    16. }

    注意:

    • 高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。
    • 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
    • 如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false

    7.RestClient查询文档

    文档的查询同样适用昨天学习的 RestHighLevelClient对象,基本步骤包括:

    • 准备Request对象
    • 准备请求参数
    • 发起请求
    • 解析响应
    public class HotelSearchTest {
        private RestHighLevelClient client;
    
        //查询全部
        @Test
        void testMatchAll() throws IOException {
            //1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            //2.准备DSL
            request.source().query(QueryBuilders.matchAllQuery());
            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //System.out.println(response);
            //4.解析响应
            handleResponse(response);
    
        }
        //查询指定部分
        @Test
        void testMatch() throws IOException {
            //1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            //2.准备DSL
            request.source().query(QueryBuilders.matchQuery("all","如家"));
            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //System.out.println(response);
            handleResponse(response);
    
        }
        //布尔条件查询
        @Test
        void testBool() throws IOException {
            //1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            //2.准备DSL
            //2.1准备BooleanQuery
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            //2.2添加term
            boolQuery.must(QueryBuilders.termQuery("city","杭州"));
            //2.3添加range
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
            request.source().query(boolQuery);
            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //System.out.println(response);
            handleResponse(response);
    
        }
        //分页
        @Test
        void testPageAndSort() throws IOException {
            //页码 每页大小
            int page = 2, size = 5;
            //1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            //2.准备DSL
            //2.1 query
            request.source().query(QueryBuilders.matchAllQuery());
            //2.2 排序sort
            request.source().sort("price", SortOrder.ASC);
            //2.3 分页from size
            request.source().from((page - 1) * size).size(5);
            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //System.out.println(response);
            handleResponse(response);
        }
        //高亮
        @Test
        void testHighlight() throws IOException {
            //1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            //2.准备DSL
            //2.1 query
            request.source().query(QueryBuilders.matchQuery("all","如家"));
            //2.2 高亮
            request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
    
            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //System.out.println(response);
            handleResponse(response);
    
        }
    
        private void handleResponse(SearchResponse response) {
            //4.解析响应
            SearchHits searchHits = response.getHits();
            //4.1获取总条数
            long total = searchHits.getTotalHits().value;
            System.out.println("共搜索到" + total + "条数据");
            //4.2文档数组
            SearchHit[] hits = searchHits.getHits();
    
            //4.3遍历
            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);
            }
        }
    
    
        @BeforeEach
        void setUp(){
            this.client = new RestHighLevelClient(RestClient.builder(
                    HttpHost.create("http://192.168.18.99:9200")
            ));
        }
    
        @AfterEach
        void tearDowm() throws IOException {
            this.client.close();
        }
    }