Java Spring Boot中的Elasticsearch入门
Java和Elasticsearch都是公司使用的通用技术堆栈中的流行元素。Java是1996年发布的一种编程语言。Java由Oracle拥有,并且仍在积极开发中。
与Java相比,Elasticsearch是一项年轻的技术-它仅在2010年发布(比Java早14年)。它迅速流行起来,现在已被许多公司用作搜索引擎。
看到两者都流行,许多人和公司都希望将Java与Elasticsearch连接起来以开发自己的搜索引擎。在本文中,我想教你如何将Java Spring Boot 2与Elasticsearch连接。我们将学习如何创建一个调用Elasticsearch产生结果的API。
将Java与Elasticsearch连接
我们必须做的第一件事是将Spring Boot项目与Elasticsearch连接起来。最简单的方法是使用Elasticsearch提供的客户端库,我们可以将其添加到包管理器(如Maven或Gradle)中。
在本文中,我们将使用spring-data-elasticsearch
Spring Data提供的库,其中还包括Elasticsearch的High Level Client库。
开始我们的项目
让我们开始使用Spring Initialzr创建我们的Spring Boot项目。由于我们将使用高级客户端,因此将我的项目配置如下图所示。然后,我们可以使用Spring提供的便捷库,即Spring Data Elasticsearch:
将依赖项添加到Spring Data Elasticsearch
如果您遵循上一节中的Spring Initialzr配置,那么您的项目中应该已经具有Elasticsearch客户依赖项。但是,如果不这样做,则可以添加它:
<dependency> <groupId> org.springframework.boot </ groupId> <artifactId> spring-boot-starter-data-elasticsearch </ artifactId> </ dependency>
创建Elasticsearch客户的bean
有两种初始化bean的方法-您可以使用Spring Data Elasticsearch库中定义的bean,也可以创建自己的bean。
较简单的选项是使用由Spring Data Elasticsearch配置的Bean。
例如,您可以将这些属性添加到您的中application.properties
:
spring.elasticsearch.rest.uris =本地主机:9200 spring.elasticsearch.rest.connection-timeout = 1s spring.elasticsearch.rest.read-timeout = 1m spring.elasticsearch.rest.password = spring.elasticsearch.rest.username =
第二种方法涉及创建自己的bean。您可以通过创建RestHighLevelClient
bean来配置设置。如果该bean存在,Spring Data将使用它作为其配置。
@Configuration @RequiredArgsConstructor public class ElasticsearchConfiguration extends AbstractElasticsearchConfiguration { private final ElasticsearchProperties elasticsearchProperties; @Override @Bean public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(elasticsearchProperties.getHostAndPort()) .withConnectTimeout(elasticsearchProperties.getConnectTimeout()) .withSocketTimeout(elasticsearchProperties.getSocketTimeout()) .build(); return RestClients.create(clientConfiguration).rest(); } }
好的,在该代码中,我们两次调用了Elasticsearch,RestHighLevelClient,
稍后我们将在本文中学习。第一个调用是删除索引(如果已存在)。我们使用try
/ catch
,因为如果索引不存在。然后elasticsearch将引发错误,使我们的应用启动过程失败。
第二个调用是创建索引。由于我仅运行单节点Elasticsearch,因此我将分片配置为,将1
副本配置为0
。
如果一切正常,那么在检查Elasticsearch时应该会看到索引。要检查它,只需转到http://localhost:9200/_cat/indices?v
,您就可以在Elasticsearch中看到索引列表:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open hello-world 0NgzXS5gRxmj1eFTPMCynQ 1 1 0 0 208b 208b
恭喜!您只需将您的应用程序连接到Elasticsearch !!
其他连接方式
spring-data-elasticsearch
如果要使用Java连接到Elasticsearch,我建议您使用该库。但是,如果您不能使用该库,则可以使用另一种方法将您的应用程序连接到Elasticsearch。
高级客户
从上一节中可以知道,spring-data-elasticsearch
我们使用的库还包括Elasticsearch的High Level Client。如果您已经导入spring-data-elasticsearch
,则可以使用Elasticsearch的高级客户端。
如果您愿意,也可以直接使用高级客户端库,而不必依赖Spring Data。您只需要在依赖管理器中添加此依赖:
<dependency> <groupId> org.elasticsearch.client </ groupId> <artifactId > elasticsearch -rest-high-level-client </ artifactId> <version> 8.0.0 </ version> </ dependency>
我们还将在示例中使用此客户端,因为高级客户端中的功能比以下功能更完整 spring-data-elasticsearch.
有关更多信息,您可以阅读Elasticsearch文档。
低级客户
使用该库会比较麻烦,但是您可以对其进行更多自定义。要使用它,您可以添加以下依赖项:
<dependency> <groupId> org.elasticsearch.client </ groupId> <artifactId > elasticsearch -rest-client </ artifactId> <version> 8.0.0 </ version> </ dependency>
有关更多信息,您可以阅读Elasticsearch的文档。
运输客户
Elasticsearch还提供了传输客户端,这将使您的应用程序标识为Elasticsearch的节点之一。我不建议使用此方法,因为它将很快弃用。
REST TRANSPORT
连接到Elasticsearch的最后一种方法是执行REST调用。由于Elasticsearch使用REST API连接到其客户端,因此您基本上可以使用REST调用将您的应用程序连接到Elasticsearch。您可以使用OkHttp,Feign或Web客户端将应用程序与Elasticsearch连接。
我也不推荐这种方法,因为它很麻烦。由于Elasticsearch已经提供了客户端库,因此最好使用它们。仅在没有其他连接方式时才使用此方法。
使用Spring Data Elasticsearch
首先,让我们学习如何spring-data-elasticsearch
在Spring项目中使用。spring-data-elasticsearch
非常易于使用,并且可以使用高级库来访问Elasticsearch。
创建一个实体并配置我们的索引
将应用程序与Elasticsearch连接完成后,就该创建一个实体了!使用Spring Data,我们可以将元数据添加到我们的实体中,这将由我们创建的存储库bean读取。这样,由于我们不需要在服务级别中创建任何映射逻辑,因此代码将更加简洁和快速地开发。
让我们创建一个名为的实体Product
:
@Data @AllArgsConstructor @NoArgsConstructor @Builder @Document(indexName = "product", shards = 1, replicas = 0, refreshInterval = "5s", createIndex = true) public class Product { @Id private String id; @Field(type = FieldType.Text) private String name; @Field(type = FieldType.Keyword) private Category category; @Field(type = FieldType.Long) private double price; public enum Category { CLOTHES, ELECTRONICS, GAMES; } }
因此,让我解释一下上面的代码块中发生了什么。首先,我不会解释有关@Data
,@AllArgsConstructor
,@NoArgsConstructor
,和@Builder
。他们是从注释龙目岛库为constructor
,getter
,setter
,builder
,和其他的东西。
现在,让我们讨论实体中的第一个spring数据注释@Document
。的@Document
注释显示出类是包含Elasticsearch索引的设置的元数据的实体。要使用我们稍后将学习的Spring Data存储库,@Document
注释是必需的。
唯一必须使用的注释@Document
是indexName
。从名称中应该很清楚-我们应该在其中填充要用于实体的索引名称。在本文中,我们将使用与实体相同的名称product
。
@Document
要谈论的第二个参数是createIndex
参数。如果将设置createIndex
为true
,则启动索引的应用程序会自动创建一个索引(如果该索引尚不存在)。
的shards
,replicas
和refreshInterval
参数确定创建索引时,索引设置。如果在创建索引后更改这些参数的值,则不会应用设置。因此,仅在首次创建索引时才使用参数。
如果要在Elasticsearch中使用自定义ID,可以使用@Id
注释。如果使用@Id
批注,Spring Data将告诉Elasticsearch将ID存储在文档和文档源中。
的@Field
类型将确定字段映射的字段。像shards
,replicas
以及refreshInterval
,该@Field
类型只会影响Elasticsearch当第一次创建索引。如果在创建索引后添加新字段或更改类型,它将不会执行任何操作。
现在我们已经配置了实体,让我们尝试一下Spring Data的自动索引创建!当我们将createIndex
as配置true
为时,Spring Data将检查Elasticsearch中是否存在索引。如果不存在,Spring Data将使用我们在实体中创建的配置创建索引。
让我们启动我们的应用程序。运行之后,让我们检查设置,看看是否正确:
curl-请求GET \
--url http:// localhost:9200 / product / _settings
结果是:
{ "product": { "settings": { "index": { "routing": { "allocation": { "include": { "_tier_preference": "data_content" } } }, "refresh_interval": "5s", "number_of_shards": "1", "provided_name": "product", "creation_date": "1607959499342", "store": { "type": "fs" }, "number_of_replicas": "0", "uuid": "iuoO8lE6QyWVSoECxa0I8w", "version": { "created": "7100099" } } } } }
一切都如我们所配置!将refresh_interval
设置为5s
,将number_of_shards
是1
,将number_of_replicas
是0
。
现在,让我们检查一下映射:
curl-请求GET \
--url http:// localhost:9200 / product / _mappings
{ "product": { "mappings": { "properties": { "category": { "type": "keyword" }, "name": { "type": "text" }, "price": { "type": "long" } } } } }
映射也符合我们的预期。与我们在实体类中配置的相同。
具有Spring Data Repository界面的基本CRUD
创建实体之后,我们将拥有在Spring Boot中创建存储库接口所需的一切。让我们创建一个名为的存储库ProductRepository
。
创建接口时,请确保扩展ElasticsearchRepository<T, U>
。在这种情况下,T
对象是您的实体,并且是U
您要用于数据ID的对象类型。在我们的例子中,我们将使用Product
我们先前创建的实体T
和String
作为U
。
public interface ProductRepository extends ElasticsearchRepository<Product, String> { }
既然您的存储库接口已经完成,您就不需要关心实现了,因为Spring正在处理它。现在,您可以调用存储库扩展到的类中的每个函数。
有关CRUD的示例,您可以检查以下代码:
@Service @RequiredArgsConstructor public class SpringDataProductServiceImpl implements SpringDataProductService { private final ProductRepository productRepository; public Product createProduct(Product product) { return productRepository.save(product); } public Optional<Product> getProduct(String id) { return productRepository.findById(id); } public void deleteProduct(String id) { productRepository.deleteById(id); } public Iterable<Product> insertBulk(List<Product> products) { return productRepository.saveAll(products); } }
在上面的代码块中,我们创建了一个名为的服务类SpringDataProductServiceImpl
,该服务类自动连接到ProductRepository
我们之前创建的服务类中。
其中有四个基本的CRUD功能。第一个是createProduct
,顾名思义,它将在product
索引中创建一个新产品。第二个是,getProduct
获取我们已通过其ID索引的产品。该deleteProduct
功能可用于按ID删除索引中的产品。该insertBulk
功能可让您向Elasticsearch插入多个产品。
一切都做完了!我不会在本文中写有关API测试的文章,因为我想重点介绍我们的应用程序如何与Elasticsearch进行交互。但是,如果您想尝试使用该API,我将在文章结尾处留下一个GitHub链接,以便您可以克隆并尝试该项目。
Spring Data中的自定义查询方法
在上一节中,我们仅利用了在其他类中已经定义的基本方法。但是我们也可以创建自定义查询方法来使用。
Spring Data非常方便的是,您可以在存储库界面中创建一个方法,而无需编写任何实现代码。Spring数据库将读取存储库并自动为其创建实现。
让我们尝试按name
字段搜索产品:
public interface ProductRepository extends ElasticsearchRepository<Product, String> { List<Product> findAllByName(String name); }
是的,这就是在Spring Data存储库界面中创建函数所需要做的一切。
您还可以使用@Query
注释定义自定义查询,然后在参数中插入JSON查询。
public interface ProductRepository extends ElasticsearchRepository<Product, String> { List<Product> findAllByName(String name); @Query("{\"match\":{\"name\":\"?0\"}}") List<Product> findAllByNameUsingAnnotations(String name); }
我们创建的两种方法都具有相同的功能-使用match
带有查询name
的参数。如果尝试,将获得相同的结果。
使用ElasticsearchRestTemplate
如果您想进行更高级的查询(例如聚合,突出显示或建议),则可以使用ElasticsearchsearchRestTemplate
Spring数据库提供的查询。通过使用它,您可以创建自己的查询,使其变得更复杂。
例如,让我们创建一个函数来像以前一样match
查询name
字段:
public List<Product> getProductsByName(String name) { Query query = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery("name", name)) .build(); SearchHits<Product> searchHits = elasticsearchRestTemplate.search(query, Product.class); return searchHits.get().map(SearchHit::getContent).collect(Collectors.toList()); }
您应该注意到上面的代码比我们在中定义的代码更复杂ElasticserchRepository
。如果可以的话,建议使用Spring Data存储库。但是,对于更高级的查询(例如汇总,突出显示或建议),必须使用ElasticsearchRestTemplate
。
例如,让我们写一些代码来汇总一个术语:
public Map<String, Long> aggregateTerm(String term) { Query query = new NativeSearchQueryBuilder() .addAggregation(new TermsAggregationBuilder(term).field(term).size(10)) .build(); SearchHits<Product> searchHits = elasticsearchRestTemplate.search(query, Product.class); Map<String, Long> result = new HashMap<>(); searchHits.getAggregations().asList().forEach(aggregation -> { ((Terms) aggregation).getBuckets() .forEach(bucket -> result.put(bucket.getKeyAsString(), bucket.getDocCount())); }); return result; }
Elasticsearch RestHighLevelClient
如果您不使用Spring或不支持Spring版本spring-data-elasticsearch
,则可以使用Elasticsearch开发的Java库RestHighLevelClient
。
RestHighLevelClient
是一个库,您可以用来执行CRUD或管理Elasticsearch之类的基本操作。尽管名称暗示它是高级别的,但与相比,它实际上是更低的级别spring-data-elasticsearch
。
该库相对于Spring Data的优势在于,您还可以使用它来管理Elasticsearch。它提供了索引和Elasticsearch配置,与Spring Data相比,您可以更灵活地使用它。它还具有与Elasticsearch交互的更完善的功能。
该库相对于Spring Data的缺点是该库的级别较低,这意味着您必须编写更多代码。
使用RestHighLevelClient进行CRUD
让我们看看如何使用该库创建一个简单的函数,以便将其与以前使用的方法进行比较:
@Service @RequiredArgsConstructor @Slf4j public class HighLevelClientProductServiceImpl implements HighLevelClientProductService { private final RestHighLevelClient restHighLevelClient; private final ObjectMapper objectMapper; public Product createProduct(Product product) { IndexRequest indexRequest = new IndexRequest("product"); indexRequest.id(product.getId()); indexRequest.source(product); try { IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT); if (indexResponse.status() == RestStatus.ACCEPTED) { return product; } throw new RuntimeException("Wrong status: " + indexResponse.status()); } catch (Exception e) { log.error("Error indexing, product: {}", product, e); return null; } } }
如您所见,它现在变得更加复杂且难以实施。现在,您需要处理异常并将JSON结果转换为您的实体。建议使用Spring Data代替CRUD进行基本操作,因为RestHighLevelClient
它比较复杂。
我在GitHub项目中包括了其他CRUD函数。如果您有兴趣,可以查看一下。链接在本文结尾。
索引创建
RestHighLevelClient
与Spring Data Elasticsearch相比,本部分是拥有明显优势的地方。在上一节中创建索引及其映射和设置时,我们仅使用注释。这很容易做到,但是你做不到。
使用RestHighLevelClient
,您可以创建用于索引管理的方法,或者基本上可以创建Elasticsearch REST API允许的几乎所有方法的方法。
例如,让我们编写一些代码,这些代码将product
使用之前使用的设置和映射来创建索引:
public boolean createProductIndex() { CreateIndexRequest createIndexRequest = new CreateIndexRequest("product"); createIndexRequest.settings(Settings.builder() .put("number_of_shards", 1) .put("number_of_replicas", 0) .put("index.requests.cache.enable", false) .build()); Map<String, Map<String, String>> mappings = new HashMap<>(); mappings.put("name", Collections.singletonMap("type", "text")); mappings.put("category", Collections.singletonMap("type", "keyword")); mappings.put("price", Collections.singletonMap("type", "long")); createIndexRequest.mapping(Collections.singletonMap("properties", mappings)); try { CreateIndexResponse createIndexResponse = restHighLevelClient.indices() .create(createIndexRequest, RequestOptions.DEFAULT); return createIndexResponse.isAcknowledged(); } catch (Exception e) { e.printStackTrace(); } return false; }
因此,让我们看看我们在代码中做了什么:
- 我们
createIndexRequest
还在确定索引名称时初始化了when。 - 调用时,我们在请求中添加了设置
createIndexRequest.settings
。在设置中,我们还配置了fieldindex.requests.cache.enable
,这对于Spring数据库是不可能的。 - 我们制作了一个
Map
包含索引中字段的属性和映射的字段。 - 我们使用来调用Elasticsearch
restHighlevelClient.indices.create
。
如您所见,与RestHighLevelClient
Spring Data实体中的注释相比,使用,我们可以创建一个更自定义的调用来为Elasticsearch创建索引。RestHighLevelClient
Spring数据库中还没有其他功能。您可以阅读Elasticsearch的文档以获取有关该库的更多信息。
结论
在本文中,我们学习了两种连接到Elasticsearch的方法:使用Spring Data和通过Elasticsearch客户端。两者都是功能强大的库,但是仅在可能的情况下才应使用Spring Data。Spring Data Elasticsearch的代码更具可读性,更易于使用。
但是,如果您想要一个功能更强大的库,基本上可以完成Elasticsearch允许的任何事情,那么您也可以使用Elasticsearch High Level Client。如果您需要更强大的功能,也可以使用本文未介绍的低级客户端。
我还要感谢您阅读本文,希望本文能帮助您开始使用Java Spring Boot中的Elasticsearch。如果您想了解有关库的更多信息,可以查看Spring Data Elasticsearch文档和Elasticsearch的High Client Client文档。