Java操作ElasticSearch
Java API
我的ElasticSearch集群的版本是6.2.4,導入elasticsearch相關的maven依賴也是6.2.4,不同版本的api可能會有差異
一:maven依賴
<!--elasticsearch核心依賴--> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>6.2.4</version> </dependency> <!--elasticsearch客戶端--> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>6.2.4</version> </dependency> <!--日志--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.9.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.24</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <!--單元測試--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>compile</scope> </dependency> <!--實體轉json--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.10.2</version> </dependency> <!--實體類簡化工具--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency>
2.單元測試的共有方法
TransportClient client; @Before //創建連接 public void connection() throws UnknownHostException { //創建客戶端連接對象 //參數為config\elasticsearch.yml中對應的值 也就是集群名稱 Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build(); client = new PreBuiltTransportClient(settings); client.addTransportAddress(new TransportAddress(InetAddress.getByName("192.168.118.3"), 9300)); client.addTransportAddress(new TransportAddress(InetAddress.getByName("192.168.118.4"), 9300)); client.addTransportAddress(new TransportAddress(InetAddress.getByName("192.168.118.5"), 9300)); } @After //釋放資源 public void close() { client.close(); } //遍歷結果集 public void iteratorSearchHit(SearchHits searchHits) { System.out.println("符合查詢條件有:" + searchHits.getTotalHits() + "條"); //獲取迭代器 Iterator<SearchHit> iterator = searchHits.iterator(); System.out.println("開始進行遍歷------------------------------"); while (iterator.hasNext()) { //獲取每個查詢對象 SearchHit searchHit = iterator.next(); //獲取字符串類型並進行打印 System.out.println(searchHit.getSourceAsString()); Map<String, Object> sourceAsMap = searchHit.getSourceAsMap(); System.out.println("符合搜索條件的標題是:=======" + sourceAsMap.get("title")); /* for (Map.Entry<String,Object> map:sourceAsMap.entrySet()){ System.out.println("==================================="); System.out.println(map.getKey()); System.out.println(map.getValue()); } */ } }
3.常規操作
@Test //創建索引庫 public void createIndex() { //索引名成必須小寫 client.admin().indices().prepareCreate("myindex1").get(); } @Test //添加映射 public void mapping() throws IOException, ExecutionException, InterruptedException { XContentBuilder builder = XContentFactory.jsonBuilder().startObject() .startObject("type1") .startObject("properties") .startObject("id") .field("type", "integer") .field("store", "true") .endObject() .startObject("title") .field("type", "text") .field("store", "true") .field("analyzer", "ik_smart") .endObject() .startObject("content") .field("type", "text") .field("store", "true") .field("analyzer", "ik_smart") .endObject() .endObject() .endObject() .endObject(); PutMappingRequest mappingRequest = Requests.putMappingRequest("myindex1") .type("type1") .source(builder); client.admin().indices().putMapping(mappingRequest).get(); } @Test //給myindex1的type1創建文檔 public void createDocument() throws IOException { XContentBuilder xContentBuilder = XContentFactory.jsonBuilder(); xContentBuilder.startObject() .field("id", 1) .field("title", "ElasticSearch是一個基於Lucene的搜索服務器") .field("content", "它提供了一個分布式多用戶能力的全文搜索引擎,基於RESTful web接口。Elasticsearch是用 Java開發的,並作為Apache許可條款下的開放源碼發布,是當前流行的企業級搜索引擎。設計用於雲計算中,能夠達到 實時搜索,穩定,可靠,快速,安裝使用方便。") .endObject(); //參數1:索引名 2:類型 3:id client.prepareIndex("myindex1", "type1", "1").setSource(xContentBuilder).get(); } @Test //通過實體轉json創建文檔 public void jsonDocument() throws JsonProcessingException { Type1 type1 = new Type1(); type1.setId(2); type1.setTitle("通過實體轉json創建文檔"); type1.setContent("通過創建實體的方式,將實體轉json"); ObjectMapper objectMapper = new ObjectMapper(); client.prepareIndex("myindex1", "type1", type1.getId().toString()) .setSource(objectMapper.writeValueAsString(type1).getBytes(), XContentType.JSON) .get(); } @Test //關鍵詞查詢 public void termQuery() { //設置搜索條件,目標索引庫,目標類型,目標域 SearchResponse searchResponse = client.prepareSearch("myindex1") .setTypes("type1") .setQuery(QueryBuilders.termQuery("content", "實體")).get(); SearchHits hits = searchResponse.getHits(); iteratorSearchHit(hits); } @Test //字符串查詢 public void stringQuery() { //設置搜索條件,目標索引庫,目標類型 SearchResponse searchResponse = client.prepareSearch("myindex1") .setTypes("type1") .setQuery(QueryBuilders.queryStringQuery("搜索實體")).get(); SearchHits hits = searchResponse.getHits(); iteratorSearchHit(hits); } @Test //根據文檔ID進行查詢 public void idQuery() { SearchResponse searchResponse = client.prepareSearch("myindex1") .setQuery(QueryBuilders.idsQuery("type1").addIds("2")) .get(); iteratorSearchHit(searchResponse.getHits()); } //分頁之前的操作,批量添加文檔 @Test public void batchInsert() throws JsonProcessingException { Type1 type1; for (int i = 1; i <= 50; i++) { type1 = new Type1(); type1.setId(i); type1.setTitle(i + "通過實體轉json創建文檔"); type1.setContent(i + "通過創建實體的方式,將實體轉json"); ObjectMapper objectMapper = new ObjectMapper(); client.prepareIndex("myindex1", "type1", type1.getId().toString()) .setSource(objectMapper.writeValueAsString(type1).getBytes(), XContentType.JSON) .get(); } } //搜索文檔並分頁顯示 @Test public void pageDocument() { SearchResponse searchResponse = client.prepareSearch("myindex1") .setTypes("type1") .setQuery(QueryBuilders.matchAllQuery()) //setFrom():從第幾條開始檢索,默認是0。 // setSize():每頁最多顯示的記錄數默認是十 .setFrom(0) .setSize(5) .get(); iteratorSearchHit(searchResponse.getHits()); } //模擬搜索結果高亮顯示 @Test public void highlight() { //設置查詢條件 SearchRequestBuilder searchRequestBuilder = client.prepareSearch("myindex1") .setTypes("type1") .setQuery(QueryBuilders.termQuery("title", "實體")); //高亮設置 HighlightBuilder highlightBuilder = new HighlightBuilder(); //開頭的標簽信息 highlightBuilder.preTags("<font style='color:red'>"); //結尾的標簽 highlightBuilder.postTags("</font>"); highlightBuilder.field("title"); searchRequestBuilder.highlighter(highlightBuilder); SearchResponse searchResponse = searchRequestBuilder.get(); Iterator<SearchHit> iterator = searchResponse.getHits().iterator(); while (iterator.hasNext()) { SearchHit hit = iterator.next(); System.out.print("正常String方式打印文檔搜索內容===="); System.out.println(hit.getSourceAsString()); Map<String, HighlightField> highlightFields = hit.getHighlightFields(); //遍歷集合,輸出高亮數據 //方式1 Text[] titles = highlightFields.get("title").getFragments(); for (Text str : titles) { System.out.println(str); } } }
Spring Data ElasticSearch
Spring Data是一個用於簡化數據庫訪問,並支持雲服務的開源框架。其主要目標是使得對數據的訪問變得方便快捷,並支持map-reduce框架和雲計算數據服務。 Spring Data可以極大的簡化JPA的寫法,可以在幾乎不用寫實現的情況下,實現對數據的訪問和操作。除了CRUD外,還包括如分頁、排序等一些常用的功能。
筆者的ElasticSearch集群的版本是6.2.4,導入spring-data-elasticsearch的maven依賴也是3.1.5.RELEASE,不同版本的api可能會有差異
一:准備maven依賴
<!--elasticsearch核心依賴--> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>6.2.4</version> </dependency> <!--elasticsearch客戶端--> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>6.2.4</version> </dependency> <!--日志--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.9.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.24</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <!--單元測試--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>compile</scope> </dependency> <!--實體類簡化工具--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <!--springframework--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!--spring-data-elasticsearch--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-elasticsearch</artifactId> <version>3.1.5.RELEASE</version> <exclusions> <exclusion> <groupId>org.elasticsearch.plugin</groupId> <artifactId>transport‐netty4‐client</artifactId> </exclusion> </exclusions> </dependency>
二:創建實體
package com.yjc.entity; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; //lombok相當於get,set,toSting等一系列方法 @Data //代表文檔對象(索引庫,類型) @Document(indexName = "myindex2",type = "article") public class Article { //文檔主鍵,唯一 @Id //store是否存儲(默認false),index是否分詞(默認true),type類型 @Field(store = true,index = false,type = FieldType.Integer) private Integer id; //下面配置代表 進行存儲並以ik_smart方式分詞,(默認開啟分詞)保持的類型為text,進行查詢的時候按照ik_smart方式進行分詞 @Field(store = true,analyzer ="ik_smart",searchAnalyzer = "ik_smart",type = FieldType.Text) private String title; //下面配置代表 進行存儲並以ik_smart方式分詞,保持的類型為text,進行查詢的時候按照ik_smart方式進行分詞 @Field(store = true,analyzer ="ik_smart",searchAnalyzer = "ik_smart",type = FieldType.Text) private String content; }
三:創建DAO和Service
ArticleDao
package com.yjc.dao; import com.yjc.entity.Article; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository /** * ElasticsearchRepository<要操作的實體類,ID在實體類中類型> * */ public interface ArticleDao extends ElasticsearchRepository<Article,Integer> { //根據標題查詢 List<Article> findByTitle(String condition); //根據標題查詢(含分頁) Page<Article> findByTitle(String condition, Pageable pageable); //根據id List<Article> findById(String condition); }
ArticleService
package com.yjc.service; import com.yjc.entity.Article; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import java.util.List; public interface ArticleService { //添加 void save(Article article); //刪除 void delete(Article article); //查詢全部 Iterable<Article> findAll(); //分頁查詢 Page<Article> findAll(Pageable pageable); //根據標題查詢 List<Article> findByTitle(String condition); //根據標題查詢(含分頁) Page<Article> findByTitle(String condition, Pageable pageable); //根據內容搜索並分頁 List<Article> findById(String condition); }
ArticleServiceImpl
package com.yjc.service.impl; import com.yjc.dao.ArticleDao; import com.yjc.entity.Article; import com.yjc.service.ArticleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @Service public class ArticleServiceImpl implements ArticleService { @Autowired ArticleDao articleDao; @Override public void save(Article article) { articleDao.save(article); } @Override public void delete(Article article) { articleDao.delete(article); } @Override public Iterable<Article> findAll() { return articleDao.findAll(); } @Override public Page<Article> findAll(Pageable pageable) { return articleDao.findAll(pageable); } @Override public List<Article> findByTitle(String condition) { return articleDao.findByTitle(condition); } @Override public Page<Article> findByTitle(String condition, Pageable pageable) { return articleDao.findByTitle(condition,pageable); } @Override public List<Article> findById(String condition) { return articleDao.findById(condition); } }
四:創建Spring的核心配置文件
applicationContext.xml,注意更換命名空間
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd "> <!--創建包掃描器,掃描Dao,自動創建實例--> <elasticsearch:repositories base-package="com.yjc.dao"/> <!--掃描Service--> <context:component-scan base-package="com.yjc.service"/> <!--配置es的連接--> <elasticsearch:transport-client id="client" cluster-nodes="192.168.118.3:9300" cluster-name="elasticsearch"/> <!--es模板對象--> <bean id="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate"> <constructor-arg name="client" ref="client"/> </bean> </beans>
五:測試
package com.yjc.test; import com.yjc.entity.Article; import com.yjc.service.ArticleService; import org.elasticsearch.client.transport.TransportClient; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) //啟動單元測試的時候加載的配置文件 @ContextConfiguration(locations = "classpath:applicationContext.xml") public class SpringDataESTest { @Autowired ArticleService articleService; //注入連接對象 @Autowired TransportClient client; //注入模板對象 @Autowired ElasticsearchTemplate elasticsearchTemplate; @Test //創建索引 public void createIndex() { //配置在實體類中已經配置好了,直接創建並添加映射 elasticsearchTemplate.createIndex(Article.class); elasticsearchTemplate.putMapping(Article.class); } @Test //保存文檔 public void saveArticle() { Article article = new Article(); article.setId(101); article.setTitle("測試SpringData ElasticSearch"); article.setContent("Spring Data ElasticSearch 基於 spring data API 簡化 elasticSearch操 作,將原始操作elasticSearch的客戶端API 進行封裝 \n" + " Spring Data為Elasticsearch Elasticsearch項目提供集成搜索引擎"); articleService.save(article); } @Test //修改文檔 public void update() { Article article = new Article(); article.setId(100); article.setTitle("修改SpringData ElasticSearch"); article.setContent("修改Spring Data ElasticSearch 基於 spring data API 簡化 elasticSearch操 作,將原始操作elasticSearch的客戶端API 進行封裝 \n" + " Spring Data為Elasticsearch Elasticsearch項目提供集成搜索引擎"); articleService.save(article); } @Test //刪除文檔 public void delete(){ Article article = new Article(); article.setId(100); articleService.delete(article); } @Test //分頁查詢 public void findAllPage(){ Pageable pageable=PageRequest.of(0,10); Page<Article> all = articleService.findAll(pageable); for (Article article:all.getContent()){ System.out.println(article); } } @Test //帶條件查詢 public void findByTitle(){ List<Article> articles = articleService.findByTitle("測試"); for (Article article:articles){ System.out.println(article); } } @Test //待條件查詢加分頁 public void findByTitlePage(){ Pageable pageable=PageRequest.of(0,1); Page<Article> articles = articleService.findByTitle("引擎", pageable); for (Article article:articles.getContent()){ System.out.println(article); } } @Test //根據id進行查詢 public void findByContent(){ List<Article> articles = articleService.findById("100"); for (Article article:articles){ System.out.println(article); } } }