Contents

ElasticSearch 简介

1. 概述

在本文中,我们将深入探讨与全文搜索引擎相关的一些关键概念,特别关注 Elasticsearch。

由于这是一篇面向 Java 的文章,我们不会提供有关如何设置 Elasticsearch 并展示它如何在幕后工作的详细分步教程。相反,我们将针对 Java 客户端,以及如何使用主要功能,如indexdeletegetsearch

2. 设置

为简单起见,我们将为我们的 Elasticsearch 实例使用 docker 映像,尽管任何侦听端口 9200 的 Elasticsearch 实例都可以

我们首先启动我们的 Elasticsearch 实例:

docker run -d --name es762 -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2

默认情况下,Elasticsearch 在 9200 端口上侦听即将到来的 HTTP 查询。我们可以通过在您喜欢的浏览器中打开http://localhost:9200/ URL来验证它是否已成功启动:

{
  "name" : "M4ojISw",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "CNnjvDZzRqeVP-B04D3CmA",
  "version" : {
    "number" : "7.6.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "2f4c224",
    "build_date" : "2020-03-18T23:22:18.622755Z",
    "build_snapshot" : false,
    "lucene_version" : "8.4.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.8.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

3. Maven配置

现在我们已经启动并运行了基本的 Elasticsearch 集群,让我们直接跳到 Java 客户端。首先,我们需要在pom.xml文件中声明以下Maven 依赖项

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.6.2</version>
</dependency>

您可以随时通过之前提供的链接查看 Maven Central 托管的最新版本。

4. Java API

在我们直接跳到如何使用主要的 Java API 功能之前,我们需要启动RestHighLevelClient

ClientConfiguration clientConfiguration =
    ClientConfiguration.builder().connectedTo("localhost:9200").build();
RestHighLevelClient client = RestClients.create(clientConfiguration).rest();

4.1.索引文件

*index()*函数允许存储任意 JSON 文档并使其可搜索:

@Test
public void givenJsonString_whenJavaObject_thenIndexDocument() {
  String jsonObject = "{\"age\":10,\"dateOfBirth\":1471466076564,"
    +"\"fullName\":\"John Doe\"}";
  IndexRequest request = new IndexRequest("people");
  request.source(jsonObject, XContentType.JSON);
  
  IndexResponse response = client.index(request, RequestOptions.DEFAULT);
  String index = response.getIndex();
  long version = response.getVersion();
    
  assertEquals(Result.CREATED, response.getResult());
  assertEquals(1, version);
  assertEquals("people", index);
}

请注意,可以使用**任何 JSON Java 库 **来创建和处理您的文档。如果你不熟悉这些,你可以使用 Elasticsearch 助手来生成你自己的 JSON 文档

XContentBuilder builder = XContentFactory.jsonBuilder()
  .startObject()
  .field("fullName", "Test")
  .field("dateOfBirth", new Date())
  .field("age", "10")
  .endObject();
  IndexRequest indexRequest = new IndexRequest("people");
  indexRequest.source(builder);
  IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
  assertEquals(Result.CREATED, response.getResult());

4.2. 查询索引文档

现在我们已经索引了一个键入的可搜索 JSON 文档,我们可以继续使用*search()*方法进行搜索:

SearchRequest searchRequest = new SearchRequest();
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] searchHits = response.getHits().getHits();
List<Person> results = 
  Arrays.stream(searchHits)
    .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class))
    .collect(Collectors.toList());

search()方法返回的结果称为Hits,每个Hit引用一个匹配搜索请求的 JSON 文档。

在这种情况下,results列表包含存储在集群中的所有数据。请注意,在此示例中,我们使用FastJson 库将 JSON string转换为 Java 对象。

我们可以通过添加其他参数来增强请求,以便使用QueryBuilders方法自定义查询:

SearchSourceBuilder builder = new SearchSourceBuilder()
  .postFilter(QueryBuilders.rangeQuery("age").from(5).to(15));
SearchRequest searchRequest = new SearchRequest();
searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
searchRequest.source(builder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

4.3. 检索和删除文档

*get()*和 *delete()*方法允许使用其id 从集群中获取或删除 JSON 文档:

GetRequest getRequest = new GetRequest("people");
getRequest.id(id);
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
// process fields
    
DeleteRequest deleteRequest = new DeleteRequest("people");
deleteRequest.id(id);
DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

语法非常简单,您只需要在对象的 id 旁边指定索引。

5. QueryBuilders示例

QueryBuilders提供了多种静态方法,用作动态匹配器来查找集群中的特定条目。在使用*search()*方法在集群中查找特定的 JSON 文档时,我们可以使用查询构建器来自定义搜索结果。

以下是QueryBuilders API最常见用途的列表。

matchAllQuery()方法返回一个匹配集群中所有文档的QueryBuilder对象:

QueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();

*rangeQuery()*匹配字段值在特定范围内的文档:

QueryBuilder matchDocumentsWithinRange = QueryBuilders
  .rangeQuery("price").from(15).to(100)

提供一个字段名称 - 例如fullName和相应的值 - 例如John Doe,*matchQuery()*方法匹配所有具有这些确切字段值的文档:

QueryBuilder matchSpecificFieldQuery= QueryBuilders
  .matchQuery("fullName", "John Doe");

我们也可以使用*multiMatchQuery()*方法来构建匹配查询的多字段版本:

QueryBuilder matchSpecificFieldQuery= QueryBuilders.matchQuery(
  "Text I am looking for", "field_1", "field_2^3", "*_field_wildcard");

我们可以使用插入符号 (^) 来提升特定字段

在我们的示例中,field_2 的 boost 值设置为 3,使其比其他字段更重要。请注意,可以使用通配符和正则表达式查询,但在性能方面,处理通配符时要注意内存消耗和响应时间延迟,因为像 *_apples 这样的东西可能会对性能产生巨大影响。

重要性系数用于对执行*search()*方法后返回的命中结果集进行排序。

如果您更熟悉 Lucene 查询语法,可以使用*simpleQueryStringQuery()*方法自定义搜索查询:

QueryBuilder simpleStringQuery = QueryBuilders
  .simpleQueryStringQuery("+John -Doe OR Janette");

正如您可能猜到的,我们可以使用 Lucene 的 Query Parser 语法来构建简单但功能强大的查询。以下是一些可与AND/OR/NOT运算符一起用于构建搜索查询的基本运算符:

  • 必需的运算符 ( + ):要求在文档的字段中某处存在特定的一段文本。
  • 禁止运算符 ( - ):排除所有包含在 ( - ) 符号后声明的关键字的文档。