ES 基础用法

创建索引

# 创建索引 
# "dynamic": "strict"====》不允许动态添加字段,如果遇到新的字段,就抛出异常。
# "dynamic": "true"===》允许动态添加新的字段。这是默认值
# "dynamic": "false"===》忽略新的字段。在原有的映射基础上,当有新的字段时,不会主动的添加新的映射关系,只作为查询结果出现在查询中。
PUT my_blogs
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "author": {
        "type": "keyword"
      },
      "title": {
        "type": "keyword"
      },
      "content": {
        "type": "text"
      },
      "views": {
        "type": "integer"
      },
      "created": {
        "type": "date",
         "format":"yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

keyword与text的区别:text会被分词;

date类型的默认格式为:”strict_date_optional_time||epoch_millis”,文档

查看索引信息

GET my_blogs/_mapping

删除索引

DELETE /test_blogs

新增文档

# 指定文档id为blog1,如果未指定则会自动生成
# 新增
POST my_blogs/_doc/blog1
{
  "author": "lzc",
  "title":"Learning ElasticSearch",
  "content":"学习ElasticSearch技术",
  "views": 0,
   "created": "2020-05-06 08:44:00"
}

POST my_blogs/_doc/blog2
{
  "author": "lzc",
  "title":"Learning Java",
  "content":"学习java技术",
  "views": 0,
   "created": "2020-05-06 08:44:00"
}

POST my_blogs/_doc/
{
  "author": "lzc",
  "title":"Learning RocketMQ",
  "content":"学习RocketMQ技术",
  "views": 0,
   "created": "2020-05-06 08:56:00"
}

# 查询所有
GET /my_blogs/_search
{
  "query": {
    "match_all": {}
  }
}

查询

term、terms

term查询、terms查询会去倒排索引中寻找确切的term,它并不知道分词器的存在,这种查询适合keyword、numeric、date等明确值的。

# term查询:查询 author 字段里有某个关键词的文档
GET /my_blogs/_search
{
  "query": {
    "term": {
      "author": "lzc"
    }
  }
}
# terms 查询:查询 author 字段里有多个关键词的文档
GET /my_blogs/_search
{
  "query": {
    "terms": {
      "author": [
        "zhangsan",
        "lzc"
      ]
    }
  }
}

match、multi_match

match查询知道分词器的存在,会对field进行分词操作,然后再查询

# 它和term区别可以理解为term是精确查询,match是模糊查询;
# match会对"学习RocketMQ"分词,分词规则跟分词器有关,可能会被分为"学习""RocketMQ"
# term 可以认为这是一个单词
GET /my_blogs/_search
{
  "query": {
    "match": {
      "content": "学习RocketMQ"
    }
  }
}

# 查询多个字段
GET /my_blogs/_search
{
  "query": {
    "multi_match": {
      "query": "RocketMQ",
      "fields": ["content","xxxx"]
    }
  }
}

_source

指定需要返回的字段

# 只返回author、title字段
GET /my_blogs/_search
{
  "_source": ["author","title"], 
  "query": {
    "match_all": {}
  }
}

Bool查询

must

返回的文档必须满足must子句的条件,并且参与计算分值。

# term查询:查询 author 字段里有某个关键词的文档
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {"term":{"author":"lzc"}}
      ]
    }
  }
}
# 匹配多个字段
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {"term":{"title":"java"}},
        {"term":{"author":"zhangsan"}}
      ]
    }
  }
}

# terms 查询:查询某个字段里有多个关键词的文档
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {"terms":{"author":["lzc","zhangsan"]}}
      ]
    }
  }
}

must_not

返回的文档必须不满足must_not定义的条件。

GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must_not": [
        {"term":{"author":"zhangsan"}}
      ]
    }
  }
}

should

返回的文档可能满足should子句的条件。在一个Bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回。minimum_should_match参数定义了至少满足几个子句。

# 相当于or语句
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "should": [
        {"term":{"author":"lzc"}},
        {"term":{"author":"zhangsan"}}
      ]
    }
  }
}

filter

返回的文档必须满足filter子句的条件。但是不会像Must一样,参与计算分值。

# 类似于and语句
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "filter": [
        {"term":{"title":"java"}},
        {"term":{"author":"zhangsan"}}
      ]
    }
  }
}

exists

# 查询字段为author的值不为null或者不为[]
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "author"
          }
        }
      ]
    }
  }
}

高亮查询

单字段查询

GET /my_blogs/_search
{
  "query": {
    "match": {
      "content": "RocketMQ"
    }
  },
  "highlight": {
    "pre_tags": [
      "<tag1>"
    ],
    "post_tags": [
      "</tag1>"
    ],
    "fields": {
      "content": {}
    }
  }
}

查询结果:

{
    "_index" : "my_blogs",
    "_type" : "_doc",
    "_id" : "Bfh853EBC1bB8l9gL99x",
    "_score" : 1.092264,
    "_source" : {
        "author" : "lzc",
        "title" : "Learning RocketMQ",
        "content" : "学习RocketMQ技术",
        "views" : 0,
        "created" : "2020-05-06 08:56:00"
    },
    "highlight" : {
        "content" : [
            "学习<tag1>RocketMQ</tag1>技术"
        ]
    }
}

多字段查询

GET /my_blogs/_search
{
    "query" : {
        "multi_match": { 
          "query": "RocketMQ" ,
          "fields" : [ "content", "title" ]
        }
    },
    "highlight" : {
        "pre_tags" : ["<tag1>"],
        "post_tags" : ["</tag1>"],
        "fields" : {
            "title": {},
            "content" : {}
        }
    }
}

删除文档

# 通过id删除
DELETE my_blogs/_doc/blog9

# 通过条件删除
POST my_blogs/_delete_by_query
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "author": {
              "value": "zhangsan"
            }
          }
        }
      ]
    }
  }
}

修改文档

# 根据id修改
POST my_blogs/_update/blog1
{
  "doc": {
    "author":"lizc"
  }
}

# doc_as_upsert 设置 true, 如果id不存在就新增
POST my_blogs/_update/blog2
{
  "doc": {
    "author":"lizhencheng",
    "title":"好好学习"
  },
  "doc_as_upsert" : true
}

连接查询

ElasticSerch 的连接查询有两种方式实现

  • nested
  • parent和child关联查询

nested

创建索引

# 假设用来存储博客与博客评论
PUT my_blogs
{
  "mappings": {
    "properties": {
      "author": {
        "type": "keyword"
      },
      "title": {
        "type": "keyword"
      },
      "content": {
        "type": "text"
      },
      "views": {
        "type": "integer"
      },
      "created": {
        "type": "date",
         "format":"yyyy-MM-dd HH:mm:ss"
      },
      "comment": {
        "type": "nested",
        "properties": {
          "commentName":{
            "type":"keyword"
          },
          "commentContent":{
            "type":"keyword"
          },
          "commentCreated":{
            "type": "date",
            "format":"yyyy-MM-dd HH:mm:ss"
          }
        }
      }
    }
  }
}

新增数据

POST my_blogs/_doc/blog1
{
  "author": "lzc",
  "title":"Learning ElasticSearch",
  "content":"学习ElasticSearch技术",
  "views": 0,
  "created": "2020-05-06 08:00:00",
  "comment":[
    {
      "commentName":"zhangsan",
      "commentContent":"commentContent1",
      "commentCreated":"2020-05-07 08:00:00"
    },
    {
      "commentName":"lisi",
      "commentContent":"commentContent2",
      "commentCreated":"2020-05-07 10:00:00"
    },
    {
      "commentName":"wangwu",
      "commentContent":"commentContent3",
      "commentCreated":"2020-05-08 08:00:00"
    }
  ]
}

POST my_blogs/_doc/blog2
{
  "author": "xiaoli",
  "title":"Learning Java",
  "content":"学习Java技术",
  "views": 0,
  "created": "2020-05-07 08:00:00",
  "comment":[
    {
      "commentName":"zhangsan",
      "commentContent":"commentContent4",
      "commentCreated":"2020-05-08 08:00:00"
    }
  ]
}

查询

# 查询 author = lzc 并且 commentName = zhangsan
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "author": {
              "value": "lzc"
            }
          }
        },
        {
          "nested": {
            "path": "comment",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "comment.commentName": {
                        "value": "zhangsan"
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

parent-child

创建索引

PUT my_blogs
{
  "mappings": {
    "properties": {
      "blog_comment_relation": {
        "type": "join",
        "relations": {
          "blog": [
            "comment"
          ]
        }
      },
      "author": {
        "type": "keyword"
      },
      "title": {
        "type": "keyword"
      },
      "content": {
        "type": "text"
      },
      "views": {
        "type": "integer"
      },
      "created": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "commentName": {
        "type": "keyword"
      },
      "commentContent": {
        "type": "keyword"
      },
      "commentCreated": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

新增数据

# 新增父节点
POST my_blogs/_doc/blog1
{
  "author": "lzc",
  "title":"Learning ElasticSearch",
  "content":"学习ElasticSearch技术",
  "views": 0,
   "created": "2020-05-06 08:55:00",
   "blog_comment_relation": {
     "name":"blog"
   }
}
POST my_blogs/_doc/blog2
{
  "author": "zhangsan",
  "title":"Learning Java",
  "content":"学习java技术",
  "views": 0,
   "created": "2020-05-06 08:58:00",
   "blog_comment_relation": {
     "name":"blog"
   }
}

# 新增子节点记录
# 需要指定routing为父节点的id,使得他们处于同一个分片。无论是子节点还是孙子节点,routing都是指向父节点id
# parent 指定父元素id
PUT my_blogs/_doc/comment1?routing=blog1
{
  "commentUserName": "lili",
  "commentContent": "I am learning ElasticSearch",
  "commentCreated":"2020-05-07 08:00:00",
  "blog_comment_relation": {
    "name": "comment",
    "parent": "blog1"
  }
}
PUT my_blogs/_doc/comment2?routing=blog1
{
  "commentUserName": "jack",
  "commentContent": "I am learning Java",
  "commentCreated":"2020-05-07 09:00:00",
  "blog_comment_relation": {
    "name": "comment",
    "parent": "blog1"
  }
}
PUT my_blogs/_doc/comment3?routing=blog2
{
  "commentUserName": "jackma",
  "commentContent": "so easy",
  "commentCreated":"2020-05-08 09:00:00",
  "blog_comment_relation": {
    "name": "comment",
    "parent": "blog2"
  }
}

查询

# Has Child查询,返回父文档
# "inner_hits": {} ==> 加了这个会将子文档一起查询出来, 默认只会返回三条数据
# "inner_hits"里面有四个属性
# from: 返回结果的偏移量,"from": 2 ==> 从索引为2的位置返回size条数据
# size: inner_hits返回的最大数量。默认值为3。
# size: inner_hits => 默认情况最大可设置为100,可通过index.max_inner_result_window来设置
# sort: 如何对内部命中进行排序,如 "sort": [{"commentCreated":"desc"}]
# name: 用于响应中特定内部命中定义的名称
GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "_id": {
              "value": "blog1"
            }
          }
        },
        {
          "has_child": {
            "type": "comment",
            "query": {
              "bool": {
                "must": [
                  {
                    "match_all": {}
                  }
                ]
              }
            },
            "inner_hits": {
              "size": 10,
              "sort": [{"commentCreated":"desc"}]
            }
          }
        }
      ]
    }
  }
}

# Has Parent查询,返回子文档
# "inner_hits": {} ==> 加了这个会将父文档一起查询出来
POST /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "has_parent": {
            "parent_type": "blog",
            "query": {
              "match": {
                "_id": "blog1"
              }
            },
            "inner_hits": {}
          }
        }
      ]
    }
  }
}

比较

nested parent/child
优点 读取性能高 父子文档可以独立更新
缺点 每次更新需要更新整个文档 关联关系,需要额外的内存,查询效率相对较差
场景 频繁查询 频繁更新

聚合

# size为0则不会返回文档信息,只会返回聚合信息
GET /my_blogs/_search
{
    "size" : 0,
    "aggs" : { 
        "author_blog_counts" : { 
            "terms" : { 
              "field" : "author"
            }
        }
    }
}

返回结果

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "author_blog_counts" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "lzc",
          "doc_count" : 2
        },
        {
          "key" : "zhangsan",
          "doc_count" : 1
        }
      ]
    }
  }
}

Reindex

将文档从一个索引复制到另一个索引。

# 将my_blogs中的数据复制到my_blogs_copy
POST _reindex
{
  "source": {
    "index": "my_blogs"
  },
  "dest": {
    "index": "my_blogs_copy"
  }
}

# 设置数量, 默认情况, size为1000
POST _reindex
{
  "size": 1, 
  "source": {
    "index": "my_blogs"
  },
  "dest": {
    "index": "my_blogs_copy"
  }
}

# 根据条件进行复制
POST _reindex
{
  "size": 1000, 
  "source": {
    "index": "my_blogs",
    "query": {
      "bool": {
        "must": [
          {
            "term": {
              "author": {
                "value": "lzc"
              }
            }
          }
        ]
      }
    }
  },
  "dest": {
    "index": "my_blogs_copy"
  }
}

自动匹配

www.elastic.co/guide/en/elasticsea...

分页

scroll

www.elastic.co/guide/en/elasticsea...

www.elastic.co/guide/en/elasticsea...

Java操作

配置

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.1.1</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>7.1.1</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.1.1</version>
</dependency>

与SpringBoot进行整合

elasticsearch.ip=localhost:9200

配置类

@Configuration
public class ESConfig {
    /**
     * 超时时间设为5分钟
     */
    private static final int TIME_OUT = 5 * 60 * 1000;
    private static final int ADDRESS_LENGTH = 2;
    private static final String HTTP_SCHEME = "http";

    @Value("${elasticsearch.ip}")
    String[] ipAddress;

    @Bean
    public RestClientBuilder restClientBuilder() {
        HttpHost[] hosts = Arrays.stream(ipAddress)
                .map(this::makeHttpHost)
                .filter(Objects::nonNull)
                .toArray(HttpHost[]::new);
        return RestClient.builder(hosts);
    }

    @Bean(name = "restHighLevelClient")
    public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) {
        restClientBuilder.setRequestConfigCallback(
                new RestClientBuilder.RequestConfigCallback() {
                    @Override
                    public RequestConfig.Builder customizeRequestConfig(
                            RequestConfig.Builder requestConfigBuilder) {
                        return requestConfigBuilder.setSocketTimeout(TIME_OUT);
                    }
                });
        return new RestHighLevelClient(restClientBuilder);
    }

    private HttpHost makeHttpHost(String s) {
        assert !StringUtils.isEmpty(s);
        String[] address = s.split(":");
        if (address.length == ADDRESS_LENGTH) {
            String ip = address[0];
            int port = Integer.parseInt(address[1]);
            System.err.println(ip + "+" + port);
            return new HttpHost(ip, port, HTTP_SCHEME);
        } else {
            return null;
        }
    }
}

创建索引

PUT my_blogs
{
  "mappings": {
    "properties": {
      "blog_comment_relation": {
        "type": "join",
        "relations": {
          "blog": [
            "comment"
          ]
        }
      },
      "author": {
        "type": "keyword"
      },
      "title": {
        "type": "keyword"
      },
      "content": {
        "type": "text"
      },
      "views": {
        "type": "integer"
      },
      "created": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "commentName": {
        "type": "keyword"
      },
      "commentContent": {
        "type": "keyword"
      },
      "commentCreated": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

编写代码

官方文档:www.elastic.co/guide/en/elasticsea...

新增

www.elastic.co/guide/en/elasticsea...

@Autowired
private RestHighLevelClient restHighLevelClient;

@Test
public void insert() throws IOException {
    Map<String, Object> blog = new HashMap();
    blog.put("author","lzc");
    blog.put("title","Learning ElasticSearch12");
    blog.put("content","学习ElasticSearch技术");
    Map<String, Object> blogCommentRelation1 = new HashMap();
    blogCommentRelation1.put("name","blog");
    blog.put("blog_comment_relation",blogCommentRelation1);
    IndexResponse indexResponse1 = insert("my_blogs", "blog1", null, blog);

    Map<String, Object> comment1 = new HashMap();
    comment1.put("commentName","commentName1");
    comment1.put("commentContent","commentContent1");
    Map<String, Object> blogCommentRelation2 = new HashMap();
    blogCommentRelation2.put("name","comment");
    blogCommentRelation2.put("parent","blog1");
    comment1.put("blog_comment_relation",blogCommentRelation2);
    IndexResponse indexResponse2 = insert("my_blogs", "comment1", "blog1", comment1);

    Map<String, Object> comment2 = new HashMap();
    comment2.put("commentName","commentName2");
    comment2.put("commentContent","commentContent2");
    Map<String, Object> blogCommentRelation3 = new HashMap();
    blogCommentRelation3.put("name","comment");
    blogCommentRelation3.put("parent","blog1");
    comment1.put("blog_comment_relation",blogCommentRelation2);
    IndexResponse indexResponse3 = insert("my_blogs", "comment2", "blog1", comment2);
}

/**
* @param index 索引名
* @param id 主键id, 如果传入null则会自动生成
* @param routing
* @param doc 文档内容
*/
public IndexResponse insert(String index, String id, String routing, Map<String, Object> doc) throws IOException {
    IndexRequest indexRequest = new IndexRequest(index).source(doc);
    if (id != null) {
        indexRequest = indexRequest.id(id);
    }
    if (routing != null) {
        indexRequest = indexRequest.routing(routing);
    }
    IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    return indexResponse;
}

新增or修改

www.elastic.co/guide/en/elasticsea...

@Autowired
private RestHighLevelClient restHighLevelClient;


@Test
public void updateOrInsert() throws IOException {
    Map<String, Object> blog = new HashMap();
    blog.put("author","lzc");
    blog.put("title","Learning ElasticSearch");
    blog.put("content","学习ElasticSearch技术");
    Map<String, Object> blogCommentRelation1 = new HashMap();
    blogCommentRelation1.put("name","blog");
    blog.put("blog_comment_relation",blogCommentRelation1);
    UpdateResponse updateResponse = updateOrInsert("my_blogs", "blog2", null, blog);

    Map<String, Object> comment = new HashMap();
    comment.put("commentName","commentName3");
    comment.put("commentContent","commentContent3");
    Map<String, Object> blogCommentRelation2 = new HashMap();
    blogCommentRelation2.put("name","comment");
    blogCommentRelation2.put("parent","blog2");
    comment.put("blog_comment_relation",blogCommentRelation2);
    IndexResponse indexResponse2 = insert("my_blogs", "comment2", "blog2", comment);
}


/**
* 新增或修改
* @param index 文档索引
* @param id 文档id
* @param routing
* @param doc 文档字段
*/
public UpdateResponse updateOrInsert(String index, String id, String routing, Map<String, Object> doc) throws IOException {
    UpdateRequest request = new UpdateRequest(index,id);
    if (routing != null) {
        request = request.routing(routing);
    }
    request.doc(doc).docAsUpsert(true).retryOnConflict(5);
    return restHighLevelClient.update(request, RequestOptions.DEFAULT);
}

通过id查找

www.elastic.co/guide/en/elasticsea...

@Autowired
private RestHighLevelClient restHighLevelClient;

@Test
public void getDocumentById() throws IOException {
    Map<String, Object> blogMap = getDocumentById("my_blogs", "comment1");
}

/**
* @param index 索引名
* @param id 文档id
*/
public Map<String, Object> getDocumentById(String index, String id) throws IOException {
    GetRequest getRequest = new GetRequest(index, id);
    GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
    if (getResponse.isExists()) {
        return getResponse.getSourceAsMap();
    }
    return null;
}

通过id删除

www.elastic.co/guide/en/elasticsea...

@Autowired
private RestHighLevelClient restHighLevelClient;

@Test
public void deleteDocumentById() throws IOException {
    deleteDocumentById("my_blogs", "comment1");
}

/**
* @param index 索引名
* @param id 文档id
*/
public DeleteResponse deleteDocumentById(String index, String id) throws IOException {
    DeleteRequest deleteRequest = new DeleteRequest(index, id);
    DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    return deleteResponse;
}

条件查询

www.elastic.co/guide/en/elasticsea...

@Test
public void searchRequest() throws IOException {
    SearchRequest searchRequest = new SearchRequest("my_blogs");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    QueryBuilder queryBuilder = QueryBuilders.matchAllQuery(); // 构造查询条件
    searchSourceBuilder.query(queryBuilder);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    System.out.println();
}

QueryBuilders.matchAllQuery()相当于如下查询

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

term查询

// 构造查询条件
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); 
// 构造term
queryBuilder.must(QueryBuilders.termQuery("title", "Learning ElasticSearch")); 

相当于如下查询

GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "title": {
              "value": "Learning ElasticSearch"
            }
          }
        }
      ]
    }
  }
}

has_child

@Test
public void searchRequest() throws IOException {
    SearchRequest searchRequest = new SearchRequest("my_blogs");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); // 构造查询条件
    QueryBuilder childQueryBuilder = JoinQueryBuilders
        .hasChildQuery(
        "comment",
        QueryBuilders.matchAllQuery(),
        ScoreMode.None).innerHit(new InnerHitBuilder().setSize(100));
    queryBuilder.must(childQueryBuilder);

    searchSourceBuilder.query(queryBuilder);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

    // 下面是对结果进行解析
    SearchHits searchHits = searchResponse.getHits();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        System.out.println("index = " + hit.getIndex());
        Map<String, Object> blogAsMap = hit.getSourceAsMap();
        System.out.println("id:" + hit.getId());
        for (Map.Entry<String, Object> kv : blogAsMap.entrySet()) {
            System.out.println(kv.getKey() + ":" + kv.getValue().toString());
        }
        System.out.println("[InnerHits]");
        Map<String, SearchHits> innerHits = hit.getInnerHits();
        for (Map.Entry<String, SearchHits> commentHit : innerHits.entrySet()) {
            String key = commentHit.getKey(); // 这个例子里面, key = comment,因为InnerHits里面只有comment类型的
            for (SearchHit commentSource : commentHit.getValue().getHits()) {
                if (commentSource != null) {
                    System.out.println("index = " + commentSource.getIndex());
                    System.out.println("id = " + commentSource.getId());
                    Map<String, Object> commentAsMap = commentSource.getSourceAsMap();
                    for (Map.Entry<String, Object> kv2 : commentAsMap.entrySet()) {
                        System.out.println(kv2.getKey() + ":" + kv2.getValue().toString());
                    }
                }
            }
        }
        System.out.println("------------------------------------------------------");
    }
}

相当于如下查询语句

GET /my_blogs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "has_child": {
            "query": {
              "match_all": {}
            },
            "type": "comment",
            "score_mode": "none",
            "inner_hits": {
              "size": 100
            }
          }
        }
      ]
    }
  }
}

聚合

@Test
public void aggs() throws IOException {
    SearchRequest searchRequest = new SearchRequest("my_blogs");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    QueryBuilder queryBuilder = QueryBuilders.matchAllQuery(); // 构造查询条件
    searchSourceBuilder.query(queryBuilder);
    // 设置为0后只返回聚合结果
    searchSourceBuilder.size(0);

    // 设置聚合字段
    List<String> aggKeys = new ArrayList<>();
    aggKeys.add("author");
    aggKeys.add("commentName");
    aggKeys.forEach(aggKey -> {
        searchSourceBuilder.aggregation(
            AggregationBuilders.terms(aggKey).field(aggKey)
            .size(10) // 针对这个字段的聚合结果, 最多返回的个数
        );
    });

    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

    // 获取聚合结果
    Aggregations aggregations = searchResponse.getAggregations();

    // 解析聚合结果
    aggKeys.forEach(aggKey -> {
        Terms terms = aggregations.get(aggKey);
        if (terms.getBuckets().size() > 0) {
            for (Terms.Bucket bucket : terms.getBuckets()) {
                String name = bucket.getKeyAsString(); // 聚合字段
                long count = bucket.getDocCount();
                System.out.println(name + ":" + count);
            }
        }
    });
}

相当于如下查询语句

GET /my_blogs/_search
{
  "size": 0,
  "query": {
    "match_all": {
      "boost": 1
    }
  },
  "aggregations": {
    "author": {
      "terms": {
        "field": "author",
        "size": 10,
        "order": [
          {
            "_count": "desc"
          },
          {
            "_key": "asc"
          }
        ]
      }
    },
    "commentName": {
      "terms": {
        "field": "commentName",
        "size": 10,
        "order": [
          {
            "_count": "desc"
          },
          {
            "_key": "asc"
          }
        ]
      }
    }
  }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!