Sping Data与Elasticsearch整合

  • Post author:
  • Post category:其他



附上示例程序的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进行整合,让操作变得简单。

通过两者进行整合,用户可以像操作关系型数据库一样操作


ES





CURD


操作、排序、分页操作统统一步到位。唯一有一点不足的是,


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) {



return


null



;

}

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

);

}



return



chunk


;

}


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


);

}


/**


*


返回


CommandLineRunner





Bean


,在


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中已经创建了索引并且插入了几条数据了。



版权声明:本文为u011923621原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。