Sping Data與Elasticsearch整合

附上示例程序的github地址:https://github.com/bjtudujunlin/SpringDataExample

1、簡介

Elasticsearch(簡稱ES) 是一個建立在全文搜索引擎 Apache Lucene(TM) 基礎上的搜索引擎,可以說 Lucene 是當今最先進,最高效的全功能開源搜索引擎框架。

    ES可以做哪些工作呢,概括起來有以下四點:

(1)  全文搜索功能,這個是最簡單也最重要的功能;

(2)  分佈式實時文件存儲,並將每一個字段都編入索引,使其可以被搜索。

(3)  實時分析的分佈式搜索引擎

(4)  可以擴展到上百臺服務器,處理PB級別的結構化或非結構化數據。

所有這些功能,最低配置下在一臺服務器上就能跑起來。客戶端怎麼與ES進行交互呢,可以看看ES的教程http://www.learnes.net/getting_started/what_is_it.html,ES支持java客戶端也支持Restful協議進行交互,使用起來還是蠻方便的。就是命令有點多原始的。

在這裏,隆重退出Spring Data與ES進行整合,讓操作變得簡單。通過兩者進行整合,用戶可以像操作關係型數據庫一樣操作ESCURD操作、排序、分頁操作統統一步到位。唯一有一點不足的是,Spring Data目前不支持ES的高亮和全文檢索功能,這兩個功能需要利用ES自身的客戶端來實現,後面例子我會提到。前面標個紅,我覺得這個對於系統選型有點作用,根據自己實際情況選擇是否用這種方案。對於Spring Data爲什麼不支持ES的高亮和全文等操作,我的理解是爲了統一,因爲Spring Data還需要繼承各類關係型數據庫、非關係型數據庫,高亮等操作是其它數據庫不支持的,所以也就不方便提供統一的接口,爲了保持接口的一致性,Spring Data所有集成案例能做到的都是提供關係型數據庫的操作。具體操作來看後面的。

2、首先添加maven依賴

這裏可以注意到,dependency標籤裏面沒有添加版本信息,因爲版本信息都在parent的pom文件裏面進行了統一配置,解決不同jar包版本不兼容的問題。詳細的配置文件大家參考github的pom文件吧,這裏主要有兩個依賴庫,一是spring-boot-starter-data-elasticsearch,這個是spring-data提供的es集成庫,另一個是lombok,這個庫比較有意思,它通過註解的方式爲java類自動生成構造函數,爲成員變量生成get、set方法,並且按照建造者模式提供java類的訪問接口,省去了苦憋呵呵的寫各類方法,例子用用在了Conference類上。

        <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-web</artifactId>

       </dependency>

       <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-data-elasticsearch</artifactId>

       </dependency>

       <dependency>

           <groupId>org.projectlombok</groupId>

           <artifactId>lombok</artifactId>

       </dependency>

3、配置數據源

這裏我們用java配置類的方式在生成數據源,而不是像spring-data和mysql集成中採用的配置文件方式  。首先需要配置一個Client類,這個類是由ES提供的,作用就是以客戶端方式與ES建立連接,調用時設置好ES的IP地址和端口就行,當然還有中結點方式加入ES,再生成Client,這裏就不介紹了。然後配置ElasticsearchTemplate類,該類的參數有一個Client,就是之前注入的。

    @Bean

    public Client client() {

       TransportClient client = null;

       try {

           client = TransportClient.builder().build()

                  .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.1.144"), 9300));

       } catch (Exception e) {

           log.error(e.toString());

       }

       return client;

    }

 

    @Bean

    public ElasticsearchTemplate elasticsearchTemplate(Client client) throws Exception {

       return new ElasticsearchTemplate(client);

    }

4、創建實體類

這裏實體類爲Conference,包含了多個成員變量,大家注意下@Data@Builder@NoArgsConstructor@AllArgsConstructor,這是個就是lombok的註解,自動生成大量方法,字面意思挺清楚的,想了解更多lombok的註解,可以看看這篇博客:

http://www.blogjava.net/fancydeepin/archive/2012/07/12/382933.html

@Document這個註解呢是配置了ES相關信息,包括索引、類型、分片、備份等。ES和關係型數據庫的元素可以這樣簡單對等:ES中的索引對應數據庫,類型對應表,文檔就對應一條記錄

import static org.springframework.data.elasticsearch.annotations.FieldType.*;

 

import java.util.List;

 

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.core.geo.GeoPoint;

 

import lombok.AllArgsConstructor;

import lombok.Builder;

import lombok.Data;

import lombok.NoArgsConstructor;

 

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

@Document(indexName = "conference-index", type = "geo-class-point-type", shards = 1, replicas = 0,

       refreshInterval = "-1")

public class Conference {

 

    private @Id String id;

    private String name;

    private @Field(type = Date) String date;

    private GeoPoint location;

    private List<String> keywords;

}

5、定義Repository接口

這裏繼承了ElasticsearchRepository,包括了關係型數據庫基本操作

 

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

 

interface ConferenceRepository extends ElasticsearchRepository<Conference, String> {}

擴展方式跟mysql集成文章中提到的一樣,列一個例子出來,主要根據名字來擴展,比如,

List<Conference> findByNameAndDate (String name, String date);

 

附帶一些擴展例子:

關鍵字

例子

Elasticsearch查詢語句

And

findByNameAndPrice

{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}

Or

findByNameOrPrice

{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}

Is

findByName

{"bool" : {"must" : {"field" : {"name" : "?"}}}}

Not

findByNameNot

{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}

LessThanEqual

findByPriceLessThan

{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

GreaterThanEqual

findByPriceGreaterThan

{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}

Before

findByPriceBefore

{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

After

findByPriceAfter

{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}

Like

findByNameLike

{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}

StartingWith

findByNameStartingWith

{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}

EndingWith

findByNameEndingWith

{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}

Contains/Containing

findByNameContaining

{"bool" : {"must" : {"field" : {"name" : {"query" : "?","analyze_wildcard" : true}}}}}

In

findByNameIn(Collectionnames)

{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}

NotIn

findByNameNotIn(Collectionnames)

{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}

Near

findByStoreNear

暫不支持

True

findByAvailableTrue

{"bool" : {"must" : {"field" : {"available" : true}}}}

False

findByAvailableFalse

{"bool" : {"must" : {"field" : {"available" : false}}}}

OrderBy

findByAvailableTrueOrderByNameDesc

{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

 

開篇提到了,springdata支持的只是關係型數據庫相關的接口,那麼ES常用的全文檢索,結果高亮等操作怎麼實現呢,暫時只能用ES原生的接口來做了,附一段代碼給大家參考一下, operations就是ElasticsearchOperations對象,這裏實現了高亮、排序、分頁、全文檢索等操作。

public List<Test> moreLikeByContent(String content) {

       SearchResponse response = operations.getClient().prepareSearch("testindex").setTypes("testtype")

               .setSearchType(SearchType.DFS_QUERY_THEN_FETCH).setQuery(QueryBuilders.matchQuery("content", content)) // 全文檢索

              .addHighlightedField("content")// 高亮,可以設置前綴和後綴

              .setFrom(0).setSize(10).setExplain(true)// 分頁

              .addSort(new ScoreSortBuilder().order(SortOrder.DESC))// 排序

              .setTrackScores(true)// 獲取得分

              .execute().actionGet();

 

       List< Test > chunk = new ArrayList<>();

       for (SearchHit searchHit : response.getHits()) {

           if (response.getHits().getHits().length <= 0) {

              returnnull;

           }

           System.out.println(searchHit.getScore());

           Test test = new Test();

           test.setId(searchHit.getId());

           test.setContent((String) searchHit.getSource().get("content"));

           test.setHighlight(searchHit.getHighlightFields().get("content").fragments()[0].toString());

           chunk.add(test);

       }

       returnchunk;

    }

6、運行程序

實體類和repository都有了,現在就剩把程序跑起來,定義Application類,內容如下。SpringBootApplication註解表明這是一個springboot的應用,EnableElasticsearchRepositories添加了jpa支持,demo方法利用@bean註解同時返回了CommandLineRunner對象,表明這個方法會在springboot啓動前加載運行,同時它的參數repository自動注入,springboot會在當前目錄和子目錄下搜索ElasticsearchOperations類型的接口自動注入。前面註解類中並沒有ElasticsearchOperations,原因是ElasticsearchTemplate是從它派生來的。

@SpringBootApplication

@EnableElasticsearchRepositories

public class Application {

    private static final Logger log = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {

       SpringApplication.run(Application.class);

    }

 

    /**

     * 返回CommandLineRunnerBean,在spring boot啓動前加載並且執行

     *

     * @param repository

     * @return

     */

    @Bean

    public CommandLineRunner demo(ElasticsearchOperations repository) {

       return (args) -> {

           // Remove all documents

       repository.deleteAll();

       operations.refresh(Conference.class);

 

       // Save data sample

       repository.save(Conference.builder().date("2014-11-06").name("Spring eXchange 2014 - London")

           .keywords(Arrays.asList("java", "spring")).location(new GeoPoint(51.500152D, -0.126236D)).build());

 

       repository.save(Conference.builder().date("2014-12-07").name("Scala eXchange 2014 - London")

              .keywords(Arrays.asList("scala", "play", "java")).location(new GeoPoint(51.500152D, -0.126236D))

              .build());

 

       repository.save(Conference.builder().date("2014-11-20").name("Elasticsearch 2014 - Berlin")

              .keywords(Arrays.asList("java", "elasticsearch", "kibana"))

              .location(new GeoPoint(52.5234051D, 13.4113999)).build());

 

       repository.save(Conference.builder().date("2014-11-12").name("AWS London 2014")

              .keywords(Arrays.asList("cloud", "aws")).location(new GeoPoint(51.500152D, -0.126236D)).build());

       repository.save(Conference.builder().date("2014-10-04").name("JDD14 - Cracow")

              .keywords(Arrays.asList("java", "spring")).location(new GeoPoint(50.0646501D, 19.9449799)).build());

       };

    }

 

}

    在Application類中右鍵運行程序,就可以看見ES中已經創建了索引並且插入了幾條數據了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章