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

  • 准备 Request 对象
  • 准备请求参数
  • 发起请求
  • 解析响应

快速入门

我们以 match_all 查询为例

发起查询请求

  1. @Test
  2. void testMatchAll() throws IOException {
  3. // 1.准备Request
  4. SearchRequest request = new SearchRequest("hotel");
  5. // 2.组织DSL参数
  6. request.source().query(QueryBuilders.matchAllQuery());
  7. // 3.发送请求,得到响应结果
  8. SearchResponse response = client.search(request, RequestOptions.DEFAULT);
  9. // ...解析响应结果
  10. }

代码解读:

  • 第一步,创建 SearchRequest 对象,指定索引库名
  • 第二步,利用 request.source() 构建 DSL,DSL 中可以包含查询、分页、排序、高亮等
    • query():代表查询条件,利用 QueryBuilders.matchAllQuery() 构建一个 match_all 查询的 DSL
  • 第三步,利用 client.search() 发送请求,得到响应

这里关键的 API 有两个:

  • 一个是 request.source(),其中包含了查询、排序、分页、高亮等所有功能。
  • 另一个是 QueryBuilders,其中包含 match、term、function_score、bool 等各种查询:

解析响应

ElasticSearch 返回的结果是一个 JSON 字符串,结构包含:

{
   "took" : 0,
   "timed_out" : false,
   "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "heima",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "info" : "Java讲师",     "name" : "赵云",
       }
      },
      // ...
    ]
  }
}
  • hits:命中的结果
    • total:总条数,其中的 value 是具体的总条数值
    • max_score:所有结果中得分最高的文档的相关性算分
    • hits:搜索结果的文档数组,其中的每个文档都是一个 JSON 对象
      • _source:文档中的原始数据,也是 JSON 对象

因此,我们解析响应结果,就是逐层解析 JSON 字符串,流程如下:

@Test
void testMatchAll() throws IOException {
    // ... 略
    // 4.解析结果
    SearchHits searchHits = response.getHits();
    // 4.1.查询的总条数
    long total = searchHits.getTotalHits().value;
    // 4.2.查询的结果数组
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        // 4.3.得到source
        String json = hit.getSourceAsString();
        // 4.4.打印
        System.out.println(json);
    }
}
  • SearchHits:通过 response.getHits() 获取,就是 JSON 中的最外层的hits,代表命中的结果
    • SearchHits#getTotalHits().value:获取总条数信息
    • SearchHits#getHits():获取 SearchHit 数组,也就是文档数组
      • SearchHit#getSourceAsString():获取文档结果中的_source,也就是原始的 JSON 文档数据

完整代码

    @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);
        // 4.解析结果
        SearchHits searchHits = response.getHits();
        // 4.1.查询的总条数
        long total = searchHits.getTotalHits().value;
        System.err.println("total = " + total);
        // 4.2.查询的结果数组
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            // 4.3.得到source
            String json = hit.getSourceAsString();
            // 反序列化
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
            // 4.4.打印
            System.out.println(hotelDoc);
        }
    }

快速入门小结

查询的基本步骤是:

  1. 创建 SearchRequest 对象
  2. 准备 Request.source(),也就是 DSL。
    • QueryBuilders 来构建查询条件
    • 传入 Request.source()query() 方法
  3. 发送请求,得到结果
  4. 解析结果(参考 JSON 结果,从外到内,逐层解析)

match 查询

全文检索的 match 和 multi_match 查询与 match_all 的 API 基本一致。差别是查询条件,也就是 query 的部分。

GET /hotel/_search
{
  "query": {
    "match_all": {}
  }
}

GET /hotel/_search
{
  "query": {
    "match": {
      "all": "如家"
    }
  }
}

GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "如家",
      "fields": ["brand", "name"]
    }
  }
}

因此,Java 代码上的差异主要是 request.source().query() 中的参数了。同样是利用 QueryBuilders 提供的方法:

// 单字段查询
QueryBuilders.matchQuery("all", "如家");
// 多字段查询
QueryBuilders.multiMatchQuery("如家", "name", "business");

而结果解析代码则完全一致,可以抽取并共享。

完整代码如下:

@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);
}

IDEA 代码抽取 Ctrl + Alt + M

精确查询

精确查询主要是两者:

  • term:词条精确匹配
  • range:范围查询

与之前的查询相比,差异同样在查询条件,其它都一样。

GET /hotel/_search
{
  "query": {
    "term": {
      "city": "杭州"
    }
  }
}
GET /hotel/_search
{
  "query": {
    "range": {
      "price": { "gte": 100, "lte": 150 }
    }
  }
}

查询条件构造的 API 如下:

// 词条查询
QueryBuilders.termQuery("city", "杭州"); 
// 范围查询
QueryBuilders.rangeQuery("price").gte(100).lte(150);

布尔查询

布尔查询是用 must、must_not、filter 等方式组合其它查询,代码示例如下:

// 创建布尔查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 添加must条件
boolQuery.must(QueryBuilders.termQuery("city", "杭州")); 
// 添加filter条件
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": { "city": "杭州" }
        }
      ],
      "filter": [
        {
          "range": {
            "price": { "lte": 250 }
          }
        }
      ]
    }
  }
}

可以看到,API 与其它查询的差别同样是在查询条件的构建,QueryBuilders,结果解析等其他代码完全不变。

示例代码:

@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);
}