能够描述es基础搜索语法?常见query类型?

  • 查询所有
    • 查询出所有数据,一般测试用。例如:match_all
  • 全文检索查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如
    • match_query 根据一个字段查询
      1. GET /indexName/_search
      2. {
      3. "query": {
      4. "match": {
      5. "FIELD": "TEXT"
      6. }
      7. }
      8. }
      ```json @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); // 4.解析响应 handleResponse(response);

}

  1. - multi_match_query 根据多个字段查询,参与查询字段越多,查询性能越差
  2. ```json
  3. GET /indexName/_search
  4. {
  5. "query": {
  6. "multi_match": {
  7. "query": "TEXT",
  8. "fields": ["FIELD1", " FIELD12"]
  9. }
  10. }
  11. }
  • 精确查询

    • 根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:

        • ids
      • range

        // range查询
        GET /indexName/_search
        {
        "query": {
        "range": {
        "FIELD": {
        "gte": 10,
        "lte": 20
        }
        }
        }
        }
        
        Integer minPrice = params.getMinPrice();
        Integer maxPrice = params.getMaxPrice();
        if (maxPrice != null && minPrice != null) {
           boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lt(maxPrice));
        }
        
      • term

        // term查询
        GET /indexName/_search
        {
        "query": {
        "term": {
        "FIELD": {
        "value": "VALUE"
        }
        }
        }
        }
        
        //城市不为空使用term查询字段
        String starName = params.getCity();
        if (StringUtils.isBlank(starName)) {
           boolQuery.filter(QueryBuilders.termQuery("starName", starName));
        }
        
  • 地理(geo)查询

    • 根据经纬度查询。例如

      • geo_distance 查询到指定中心点小于某个距离值的所有文档

        // geo_distance 查询
        GET /indexName/_search
        {
        "query": {
        "geo_distance": {
        "distance": "15km",
        "FIELD": "31.21,121.5"
        }
        }
        }
        
      • geo_bounding_box 查询geo_point值落在某个矩形范围的所有文档

        // geo_bounding_box查询
        GET /indexName/_search
        {
        "query": {
        "geo_bounding_box": {
        "FIELD": {
        "top_left": {
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": {
          "lat": 30.9,
          "lon": 121.7
        }
        }
        }
        }
        }
        
  • 复合(compound)查询

    • 复合查询可以将上述各种查询条件组合起来,合并查询条件。例如

      • bool
        • must:必须匹配每个子查询,类似“与”
        • should:选择性匹配子查询,类似“或”
        • must_not:必须不匹配,不参与算分,类似“非”
        • filter:必须匹配,不参与算分
          GET /hotel/_search
          {
          "query": {
          "bool": {
          "must": [
          {
          "match": {"name": "如家"}
          }
          ],
          "must_not": [
          {
          "range": { "price": {"gt": 400}}
          }
          ],
          "filter": [
          {
          "geo_distance": {
           "distance": "10km", "location": {"lat": 31.21, "lon": 121.5}
          }
          }
          ]
          }
          }
          }
          
          ```json @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); // 4.解析响应 handleResponse(response); } ```

      • function_score
    • image.png
  • 查询的语法基本一致 ```json GET /indexName/_search { “query”: { “查询类型”: {
    "查询条件": "条件值"
    
    } } }

查询类型为match_all // 查询所有 GET /indexName/_search { “query”: { “match_all”: { } } }

<a name="pgMDS"></a>
### es实现地理坐标查询思路?

- 修改RequestParams参数,接收location字段
- 修改search方法业务逻辑,如果location有值,添加根据geo_distance排序的功能
```java
@Override
public PageResult search(RequestParams params) {
    try {
        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2.准备DSL
        // 2.1.query
        buildBasicQuery(params, request);

        // 2.2.分页
        int page = params.getPage();
        int size = params.getSize();
        request.source().from((page - 1) * size).size(size);

        // 2.3.排序
        String location = params.getLocation();
        if (location != null && !location.equals("")) {
            request.source().sort(SortBuilders
                                  .geoDistanceSort("location", new GeoPoint(location))
                                  .order(SortOrder.ASC)
                                  .unit(DistanceUnit.KILOMETERS)
                                 );
        }

        // 3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析响应
        return handleResponse(response);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
  • 排序距离显示

    • 修改HotelDoc,添加排序距离字段,用于页面显示
    • 修改HotelService类中的handleResponse方法,添加对sort值的获取
      //解析响应结果
             SearchHits hits = response.getHits();
             PageResult pageResult = new PageResult();
             //设置查询数量
             pageResult.setTotal(hits.getTotalHits().value);
             List<HotelDoc> list = new ArrayList<>();
             //遍历文档列表
             for (SearchHit hit : hits) {
                 HotelDoc hotelDoc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);
                 if (StringUtils.isBlank(location)) {
                     Object[] sortValues = hit.getSortValues();
                     if (sortValues != null && sortValues.length > 0) {
                         hotelDoc.setDistance(sortValues[0]);
                     }
                 }
                 list.add(hotelDoc);
             }
             pageResult.setHotels(list);
             return pageResult;
         } catch (IOException e) {
             e.printStackTrace();
             throw new RuntimeException("es搜索出错了!");
         }
      

      es实现高亮思路?

  • 第一步:从结果中获取source。hit.getSourceAsString(),这部分是非高亮结果,json字符串。还需要反序列为HotelDoc对象

  • 第二步:获取高亮结果。hit.getHighlightFields(),返回值是一个Map,key是高亮字段名称,值是HighlightField对象,代表高亮值
  • 第三步:从map中根据高亮字段名称,获取高亮字段值对象HighlightField
  • 第四步:从HighlightField中获取Fragments,并且转为字符串。这部分就是真正的高亮字符串了
  • 第五步:用高亮的结果替换HotelDoc中的非高亮结果
    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);
      }
    }
    

    es实现竞价排名思路?

  1. 给HotelDoc类添加isAD字段,Boolean类型
  2. 挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true
  3. 修改search方法,添加function score功能,给isAD值为true的酒店增加权重

    private void buildBasicQuery(RequestParams params, SearchRequest request) {
    // 1.构建BooleanQuery
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    // 关键字搜索
    String key = params.getKey();
    if (key == null || "".equals(key)) {
        boolQuery.must(QueryBuilders.matchAllQuery());
    } else {
        boolQuery.must(QueryBuilders.matchQuery("all", key));
    }
    // 城市条件
    if (params.getCity() != null && !params.getCity().equals("")) {
        boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
    }
    // 品牌条件
    if (params.getBrand() != null && !params.getBrand().equals("")) {
        boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
    }
    // 星级条件
    if (params.getStarName() != null && !params.getStarName().equals("")) {
        boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
    }
    // 价格
    if (params.getMinPrice() != null && params.getMaxPrice() != null) {
        boolQuery.filter(QueryBuilders
                         .rangeQuery("price")
                         .gte(params.getMinPrice())
                         .lte(params.getMaxPrice())
                        );
    }
    
    // 2.算分控制
    FunctionScoreQueryBuilder functionScoreQuery =
        QueryBuilders.functionScoreQuery(
        // 原始查询,相关性算分的查询
        boolQuery,
        // function score的数组
        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
            // 其中的一个function score 元素
            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                // 过滤条件
                QueryBuilders.termQuery("isAD", true),
                // 算分函数
                ScoreFunctionBuilders.weightFactorFunction(10)
            )
        });
    request.source().query(functionScoreQuery);
    }
    

    排序、分页查询

  • 通过修改from、size参数来控制要返回的分页结果

    GET /hotel/_search
    {
    "query": {
      "match_all": {}
    },
    "from": 990, // 分页开始的位置,默认为0
    "size": 10, // 期望获取的文档总数
    "sort": [
      {"price": "asc"}
    ]
    }
    
  • 代码示例 ```json @Test void testPageAndSort() throws IOException { // 页码,每页大小 int page = 1, 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); // 4.解析响应 handleResponse(response);

} ```