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文檔。