elasticsearch搜索功能

  1. 直接使用DSL语句查询文档

0.DSL查询分类

Elasticsearch提供了基于JSON的DSL来定义查询。常见的查询类型包括:
      查询所有:查询到所有的数据,一般测试用:
                       match_all
      全文检索:(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。
                      match_query
                      multi_match_query
      精确查询:根据精确词条值查找数据,一般是查找keyword,数值,日期,boolean等类型字段。
                     ids
                     range
                     term
      地理查询:根据经纬度查询
                    geo_distance
                    geo_bounding_box    
      复合查询:复合查询可以将上述各种查询条件组合起来,合并查询条件
                    bool
                    function_score

1.查询所有

一般测试用
#查询所有
GET /hotel/_search
{
  "query": {
    "match_all": {}
  }
}

2.全文检索查询

会对用户输入内容分词,常用于搜索框搜索
#全文检索查询,会对用户输入的内容分词,常用于搜索框搜索(依据相关度排序)
#match:根据一个字段查询
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "外滩如家"
    }
  }
}
#multi_match:与match查询类似,只不过允许同时查询多个字段。根据多个字段查询,
参与查询字段越多,性能越差(建议cope_to)
GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "外滩如家",
      "fields": ["brand","name","business"]
    }
  }
}

3.精准查询

一般是查找keyword,数值,日期,boolean等类型字段。所以不会对搜索条件分词
#精确查询一般是查找keyword,数值,日期,boolean等类型字段。所以不会对搜索条件分词,常见有:
#term:根据词条精确值查询
GET /hotel/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "如家"
      }
    }
  }
}
#range:根据值的范围查询
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 500,
        "lte": 1000
      }
    }
  }
}

4.地理坐标查询

常用于搜索自己附近的地点或车酒店等
#根据经纬度查询
#geo_bounding_box查询geo_point值落在某个矩形范围的所有文档
GET /hotel/_search
{
  "query": {
    "geo_bounding_box":{
      "location":{
        "top_left":{
          "lat":31,
          "lon":121.5
        },
        "bottom_right":{
          "lat":30.9,
          "lon":121.7
        }
      }
    }
  }
}

#geo_distance:查询到指定中心点小于某个距离值的所有文档
GET /hotel/_search
{
  "query": {
    "geo_distance":{
      "distance":"2km",
      "location":"31.21,121.5"
    }
  }
}

5.组合查询

将其他简单的查询组合起来,实现更加复杂的搜索逻辑。

1.算分排序(Function Score Query)

注意:利用match查询时,文档会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。

#复合查询:复合查询可以将其他简单查询组合起来,实现更复杂的搜索逻辑。
#function score:算分函数排序,可以控制文档相关性算分,控制文档排名
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {"match": {
        "all": "外滩"
      }},
      "functions": [
        {
          "filter": {"term": {
            "id": "1"
          }},
          "weight": 10
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

#例子:给如家这个品牌的酒店排名靠前一些
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {"match": {
        "all": "外滩"
      }},
      "functions": [
        {
          "filter": {"term": {
            "brand": "如家"
          }},
          "weight": 10
        }
      ],
      "boost_mode": "sum"
    }
  }
}
elasticsearch搜索功能_第1张图片

2.布尔查询(Boolean Query)

布尔查询是一个或多个查询子句的组合

must:必须匹配每个子查询(与)

should:选择性匹配子查询(或)

must_not:必须不匹配,不参与算分(非)

filter:必须匹配,不参与算分

#Boolean Query
#布尔查询是一个或多个查询子句的组合,子查询的组合方式有:
#must:必须匹配每个子查询,“与”
#should:选择性匹配子查询,“或”
#must_not:必须不匹配,不参与算分,“非”
#filter:必须匹配,不参与算分
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {
          "city": {
            "value": "上海"
          }
        }}
      ],
      "should": [
        {"term": {
          "brand": {
            "value": "皇冠假日"
          }
        }},
        {"term": {
          "brand": {
            "value": "华美达"
          }
        }}
      ],
      "must_not": [
        {"range": {
          "price": {
            "lte": 500
          }
        }}
      ],
      "filter": [
        {"range": {
          "score": {
            "gte": 45
          }
        }}
      ]
    }
  }
}
#利用bool查询实现功能
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "name": "如家"
        }}
      ],
      "must_not": [
        {"range": {
          "price": {
            "gte": 400
          }
+          "lat": 31.21,
            "lon": 121.5
          }
        }}
      ]
    }
  }
}

2.DSL搜索结果的处理

1.排序

elasticseach支持对搜索结果排序,默认是根据相度算分来排序。可以排序字段类型有keyword类型,数值类型,地理坐标类型,日期类型等。
GET /hotel/_search
{
  "query": {"match_all": {}},
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}

GET /hotel/_search
{
  "query": {"match_all": {}},
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 40,
          "lon": -70
        },
         "order": "desc",
          "unit": "km"
      }
    }
  ]
}

GET /hotel/_search
{
  "query": {"match_all": {}},
  "sort": [
    {
      "score":"desc"
    },
    {
      "price":"asc"
    }
  ]
}

#找到121.612282,31.034661周围的酒店,距离升序排序
GET /hotel/_search
{
  "query": {"match_all": {}},
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.034661,
          "lon": 121.612282
        },
        "order": "asc",
        "unit": "km"
      }
    }
  ]
}

2.分页

elasticsearch默认情况下只返回top10的数据,而如果要查询更多数据就需要修改分页参数了。
elasticsearch中通过修改from,size参数来控制要返回的分页结果
#elasticsearch默认情况下只返回top 10的数据,而如果要查询更多的数据就需要修改页面参数了。
#from:默认从0开始(分页开始的位置)
#size:几条数据(期望获取的文档总数)
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 991,  
  "size": 11,
  "sort": [
    {
      "price": {
        "order": "asc"
      }
    }
  ]
}

深度分页问题:ES是分布式的,所以会面临深度分页问题,ES设定结果集查询的上限是10000。

解决方案

1.search after:分页时需要排序,原理上是从上一次的排序值开始,查询下一页数据

2.score:原理将排序数据形成快照,保存在内存中,官方已经不推荐使用

elasticsearch搜索功能_第2张图片

3.高亮

在搜索结果中把搜索关键字突出显示。
原理:
     将搜索结果中的关键字用标签标记出来
    在页面中给标签加css样式
#高亮:就是在搜索结果中把搜索关键字突出显示
#默认情况下,ES搜索字段必须与高亮字段一致,否则无法高亮
#require_field_match:需不需要字段匹配
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "如家"
    }
  },
  "highlight": {
    "fields": {
      "name":{
        "pre_tags":"",
        "post_tags":"",
        "require_field_match": "false"
    }
    } 
  }
}
GET /hotel/_search
{
  "query": {
    "match": {
      "name": "如家"
    }
  },
  "from": 0, // 分页开始的位置
  "size": 20, // 期望获取的文档总数
  "sort": [ 
    {  "price": "asc" }, // 普通排序
    {
      "_geo_distance" : { // 距离排序
          "location" : "31.040699,121.618075", 
          "order" : "asc",
          "unit" : "km"
      }
    }
  ],
  "highlight": {
    "fields": { // 高亮字段
      "name": {
        "pre_tags": "",  // 用来标记高亮字段的前置标签
        "post_tags": "" // 用来标记高亮字段的后置标签
      }
    }
  }
}

3.RestClient查询文档

解析结果有些重复,所以提前抽取成方法

elasticsearch搜索功能_第3张图片
public class QueryTest {
    private RestHighLevelClient client;
    @BeforeEach
    void setUp() {
        this.client=new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.122.128:9200")
        ));
    }
    @AfterEach
    void tearDown() throws IOException {
        client.close();
    }

    /*
    * 结果集处理方法
    * */
    private void ResponseHandle(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) {
            //4.3.得到source
            String json = hit.getSourceAsString();
            //4.4.反序列化
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);

            /*
            * 高亮解析
            * */
            Map highlightFields = hit.getHighlightFields();
            if (highlightFields!=null || highlightFields.size()!=0){
                //2.获取高亮字段结果
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField!=null){
                    //取出高亮结果数组中的第一个,就是酒店名称
                    String name=highlightField.getFragments()[0].string();
                    hotelDoc.setName(name);
                }
            }

            //4.5.打印
            System.out.println(hotelDoc);
        }
    }
}

/*
     * 结果集处理方法
     * */
    private PageResult ResponseHandle(SearchResponse response) {
        //4.解析响应结果
        SearchHits searchHits = response.getHits();
        //4.1.查询的总条数
        long total = searchHits.getTotalHits().value;
//        System.out.println("共搜索到" + total + "条数据");
        //4.2.查询的结果数组
        SearchHit[] hits = searchHits.getHits();

        //遍历获取结果
        ArrayList hotels = new ArrayList<>();
        for (SearchHit hit : hits) {
            //4.3.得到source
            String json = hit.getSourceAsString();

            //4.4.反序列化
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);

            //获取排序值
            Object[] sortValues = hit.getSortValues();
            if (sortValues.length>0){
                Object sortValue = sortValues[0];
                hotelDoc.setDistance(sortValue);
            }

            hotels.add(hotelDoc);

//            /*
//             * 高亮解析
//             * */
//            Map highlightFields = hit.getHighlightFields();
//            if (highlightFields!=null || highlightFields.size()!=0){
//                //2.获取高亮字段结果
//                HighlightField highlightField = highlightFields.get("name");
//                if (highlightField!=null){
//                    //取出高亮结果数组中的第一个,就是酒店名称
//                    String name=highlightField.getFragments()[0].string();
//                    hotelDoc.setName(name);
//                }
//            }

//            //4.5.打印
//            System.out.println(hotelDoc);
        }
        //封装返回
      return new PageResult(total,hotels);
    }

1.查询所有:

elasticsearch搜索功能_第4张图片
 /*
    * 查询所有
    * */
    @Test
    void matchAllTest() throws IOException {
        //1.准备request
        SearchRequest request = new SearchRequest("hotel");
        //2.组织DSL参数
        request.source().query(QueryBuilders.matchAllQuery());
        //3.发送请求,得到响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        ResponseHandle(response);
    }

2.match查询:

/*
    * 全文检索
    * */
    @Test
    void matchTest() throws IOException {
        SearchRequest request = new SearchRequest("hotel");
//        request.source().query(QueryBuilders.matchQuery("all","外滩如家"));
        request.source().query(QueryBuilders.multiMatchQuery("如家","name","business"));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        ResponseHandle(response);
    }

3.精确查询:

 /*
    * 精确查询
    * */
    @Test
    void TrueTest() throws IOException {
        //1.准备request
        SearchRequest request = new SearchRequest("hotel");
        //2.组织DSL参数
//        request.source().query(QueryBuilders.termQuery("brand","如家"));
        request.source().query(QueryBuilders.rangeQuery("price").gte(100).lte(200));
        //3.发送请求,得到响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        ResponseHandle(response);
    }

4.复合查询:

 /*
    * 组合查询
    * */
    @Test
    void boolTest() throws IOException {
        //1.准备request
        SearchRequest request = new SearchRequest("hotel");

//        //创建布尔查询
//        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//        //添加must条件
//        boolQuery.must(QueryBuilders.termQuery("city","杭州"));
//        //添加filter条件
//       boolQuery.filter(QueryBuilders.rangeQuery("price").lte(500));
//        request.source().query(boolQuery);
                request.source().query(
                        QueryBuilders.boolQuery()
                                .must(QueryBuilders.termQuery("city", "深圳"))
                                .filter(QueryBuilders.rangeQuery("price").gte(250))
                );
        //3.发送请求,得到响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        ResponseHandle(response);
    }

5.排序,分页,高亮:

 /*
     * 查询所有的分页查询和排序以及高亮
     * */
    @Test
    void pageTest() throws IOException {
        int page=1;
        int pageSize=5;
        //1.准备request
        SearchRequest request = new SearchRequest("hotel");
        //2.组织DSL参数
        /*
        * 高亮需对关键字高亮,必须有条件,不能用matchAll了
        * */
        request.source().query(QueryBuilders.matchQuery("all","如家"));

        //分页查询
        request.source().from((page-1)*pageSize).size(3);

        //排序
        request.source().sort("price", SortOrder.ASC);

        //高亮
        request.source().highlighter(
                new HighlightBuilder()
                .field("name")
                 //是否需要和查询字段匹配
                .requireFieldMatch(false)
        );
        /*
        * 高亮结果解析比较麻烦
        * */

        //3.发送请求,得到响应结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        ResponseHandle(response);
    }
elasticsearch搜索功能_第5张图片

总结:查询的基本步骤是:

  1. 创建SearchRequest对象

  1. 准备Request.source(),也就是DSL

  1. QueryBuilders来构建查询条件

  1. 出入Request.source()的query方法

  1. 发送请求,得到结果

  1. 解析结果()

你可能感兴趣的:(#,微服务,elasticsearch,大数据,搜索引擎)