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>

           org.springframework.boot

           spring-boot-starter-web

       dependency>

       <dependency>

           <groupId>org.springframework.bootgroupId>

           <artifactId>spring-boot-starter-data-elasticsearchartifactId>

       dependency>

       <dependency>

           <groupId>org.projectlombokgroupId>

           <artifactId>lombokartifactId>

       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 keywords;

}

5、定义Repository接口

这里继承了ElasticsearchRepository,包括了关系型数据库基本操作

 

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

 

interface ConferenceRepository extends ElasticsearchRepository {}

扩展方式跟mysql集成文章中提到的一样,列一个例子出来,主要根据名字来扩展,比如,

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

你可能感兴趣的:(Spring)