環(huán)境:springboot2.4.12 + ElasticSearch7.8.0
簡介
Elasticsearch是一個分布式搜索引擎,底層基于Lucene實現(xiàn)。它屏蔽了Lucene的底層細節(jié),提供了分布式特性,同時對外提供了Restful API。Elasticsearch以其易用性迅速贏得了許多用戶,被用在網(wǎng)站搜索、日志分析等諸多方面。由于ES強大的橫向擴展能力,甚至很多人也會直接把ES當做NoSQL來用。
為什么要使用ES?
- 分布式特性:Elasticsearch具有分布式本質特征,可以擴展至數(shù)百臺甚至數(shù)千臺服務器,并處理PB量級的數(shù)據(jù)。
- 全文搜索能力:Elasticsearch在Lucene基礎上構建,因此在全文本搜索方面表現(xiàn)十分出色。
- 近實時搜索:Elasticsearch是一個近實時的搜索平臺,從文檔索引操作到文檔變?yōu)榭伤阉鳡顟B(tài)之間的延時很短,一般只有一秒。因此,它非常適用于對時間有嚴苛要求的用例,例如安全分析和基礎設施監(jiān)測。
- 豐富的功能:Elasticsearch除了速度、可擴展性和彈性等優(yōu)勢以外,還有大量強大的內(nèi)置功能,例如數(shù)據(jù)匯總和索引生命周期管理,可以方便用戶更加高效地存儲和搜索數(shù)據(jù)。
- 簡化數(shù)據(jù)處理過程:通過與Beats和Logstash進行集成,用戶能夠在向Elasticsearch中索引數(shù)據(jù)之前輕松地處理數(shù)據(jù)。同時,Kibana不僅可針對Elasticsearch數(shù)據(jù)提供實時可視化,同時還提供UI以便用戶快速訪問應用程序性能監(jiān)測(APM)、日志和基礎設施指標等數(shù)據(jù)。
ES為什么那么快?
Elasticsearch之所以快,主要是因為它采用了分布式架構和近實時搜索技術。
首先,Elasticsearch是一個分布式搜索引擎,它由多個節(jié)點組成,每個節(jié)點都可以獨立地存儲和處理數(shù)據(jù)。這種分布式架構使得Elasticsearch可以輕松地擴展到數(shù)百臺甚至數(shù)千臺服務器,處理大量數(shù)據(jù)。
其次,Elasticsearch采用了近實時搜索技術。當文檔被索引時,它可以在幾秒鐘內(nèi)變?yōu)榭伤阉鳡顟B(tài)。這種近實時搜索技術使得Elasticsearch可以快速響應用戶的搜索請求,提高搜索性能和效率。
此外,Elasticsearch還采用了倒排索引技術,將文檔中的每個單詞都作為索引項,存儲在倒排索引中。這種倒排索引技術使得Elasticsearch可以快速地定位包含特定單詞的文檔,進一步提高了搜索性能。
最后,Elasticsearch還提供了豐富的查詢功能和優(yōu)化算法,可以根據(jù)用戶的查詢需求和數(shù)據(jù)特點進行智能優(yōu)化,提高搜索準確率和響應速度。
綜上所述,Elasticsearch之所以快,是因為它采用了分布式架構、近實時搜索技術、倒排索引技術和優(yōu)化算法等多種技術手段,使得它可以高效地處理大量數(shù)據(jù),快速響應用戶的搜索請求,并提高搜索準確率和響應速度。
接下來介紹如何在SpringBoot中整合ElasticSearch
相關依賴及應用配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
配置文件
spring:
elasticsearch:
rest:
uris:
- http://localhost:9201
---
logging:
level:
com.pack: debug
org.springframework.data.elasticsearch.core: debug
數(shù)據(jù)模型建立
@Document(createIndex = true, indexName = "products", shards = 3, replicas = 1)
public class Product {
@Id
private Long id ;
@Field(analyzer = "ik_max_word", type = FieldType.Text)
private String title ;
@Field(type= FieldType.Keyword)
private String category ;
@Field(type = FieldType.Double)
private Double price ;
@Field(type = FieldType.Keyword, index = false)
private String images ;
@Override
public String toString() {
return "Product [id=" + id + ", title=" + title + ", category=" + category + ", price=" + price + ", images="
+ images + "]";
}
}
ProductRepository
這里我們只需要繼承ElasticsearchRepository即可,是不是和data-jpa一樣一樣的的。
public interface ProductRepository extends ElasticsearchRepository<Product, Long> {
}
繼承ElasticsearchRepository后 我們也可以像data-jpa一樣使用findBy*等語法來寫相關查詢方法。
- 方法名中支持的關鍵字
圖片
方法返回值類型
- List<T>
- Stream<T>
- SearchHits<T>
- List<SearchHit<T>>
- Stream<SearchHit<T>>
- SearchPage<T>
Repository中也支持@Query注解的方式自定義查詢字符串。
public interface ProductRepository extends ElasticsearchRepository<Product, Long> {
List<Product> findByTitle(String title) ;
@Query("{"fuzzy": {"title": "?0"}}")
Page<Product> findByTitle(String sex,Pageable pageable);
// 自定義查詢
@Query("{"match": {"category": "?0"}}")
Page<Product> findByCategory(String category,Pageable pageable);
// 高亮設置
@Highlight(fields = {@HighlightField(name = "title"), @HighlightField(name = "category")})
List<SearchHit<Product>> findByTitleOrCategory(String title, String category,Pageable pageable) ;
}
除了使用Repository方式,我們還可以使用ElasticsearchRestTemplate的方式請求服務。如下測試
測試
@Resource
private ProductRepository productRepository ;
@Resource
private ElasticsearchRestTemplate elasticTemplate ;
@Test
public void testCreate() {
Product product = new Product() ;
product.setId(3L) ;
product.setCategory("配件") ;
product.setPrice(299.5d) ;
product.setImages("http://www.pack.com/memory.jpg") ;
product.setTitle("很牛逼的內(nèi)存條") ;
productRepository.save(product) ;
}
@Test
public void testQuery() {
Product product = productRepository.findById(1L).orElse(null) ;
System.out.println(product) ;
}
@Test
public void testFindAll() {
Pageable pageable = PageRequest.of(1, 2) ;
Page<Product> page = productRepository.findAll(pageable) ;
System.out.println(page.getTotalPages() + "n" + page.getContent()) ;
}
@Test
public void testTermSearch() {
for (Product p : productRepository.findByTitle("JAVA從入門到精通")) {
System.out.println(p) ;
}
}
@Test
public void testFindByTitle() {
Pageable pageable = PageRequest.of(0, 2) ;
Page<Product> page = productRepository.findByTitle("Java", pageable) ;
System.out.println(page.getTotalPages() + "n" + page.getContent()) ;
}
@Test
public void testFindByCategory() {
Pageable pageable = PageRequest.of(0, 2) ;
Page<Product> page = productRepository.findByCategory("書籍", pageable) ;
System.out.println(page.getTotalPages() + "n" + page.getContent()) ;
}
@Test
public void testCriteriaQuery() {
Criteria criteria = new Criteria("price").greaterThan(50).lessThan(80);
Query query = new CriteriaQuery(criteria);
SearchHits<Product> hits = elasticTemplate.search(query, Product.class, IndexCoordinates.of("products")) ;
for (SearchHit<Product> hit : hits) {
System.out.println(hit) ;
}
}
@Test
public void testStringQuery() {
Query query = new StringQuery("{ "match": { "category": { "query": "配件" } } } ");
SearchHits<Product> hits = elasticTemplate.search(query, Product.class);
for (SearchHit<Product> hit : hits) {
System.out.println(hit) ;
}
}
@Test
public void testStringQueryFuzzy() {
Query query = new StringQuery("{ "fuzzy":{"title":{"value":"Java"}} }");
HighlightQuery highlightQuery = null ;
HighlightBuilder highBuilder = new HighlightBuilder().preTags("<font color='red'>").postTags("</font>").field("title") ;
highlightQuery = new HighlightQuery(highBuilder) ;
query.setHighlightQuery(highlightQuery) ;
SearchHits<Product> hits = elasticTemplate.search(query, Product.class);
for (SearchHit<Product> hit : hits) {
System.out.println(hit + "n" + hit.getHighlightField("title")) ;
}
}
在啟動服務時會自動地為我們創(chuàng)建索引。
我們可以安裝Chrome插件 ElasticSearch Head非常方便地查看es的狀態(tài)及索引信息。
圖片
ES集群狀態(tài)情況
圖片
完畢!!!