欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解spring-boot集成elasticsearch及其简单应用

程序员文章站 2024-02-13 13:08:28
介绍 记录将elasticsearch集成到spring boot的过程,以及一些简单的应用和helper类使用。 接入方式 使用spring-boot中的sprin...

介绍

记录将elasticsearch集成到spring boot的过程,以及一些简单的应用和helper类使用。

接入方式

使用spring-boot中的spring-data-elasticsearch,可以使用两种内置客户端接入

1、节点客户端(node client):

配置文件中设置为local:false,节点客户端以无数据节点(node-master或node-client)身份加入集群,换言之,它自己不存储任何数据,但是它知道数据在集群中的具体位置,并且能够直接转发请求到对应的节点上。

2、传输客户端(transport client):

配置文件中设置为local:true,这个更轻量的传输客户端能够发送请求到远程集群。它自己不加入集群,只是简单转发请求给集群中的节点。

两个java客户端都通过9300端口与集群交互,使用elasticsearch传输协议(elasticsearch transport protocol)。集群中的节点之间也通过9300端口进行通信。如果此端口未开放,你的节点将不能组成集群。

环境

版本兼容

请一定注意版本兼容问题。这关系到很多maven依赖。spring data elasticsearch spring boot version matrix

详解spring-boot集成elasticsearch及其简单应用

搭建环境

spring boot: 1.4.1.release

spring-data-elasticsearch: 用了最基础的spring-boot-starter-data-elasticsearch,选择高版本时需要对于提高es服务版本
elasticsearch: 2.3.0

maven依赖

<parent>
   <groupid>org.springframework.boot</groupid>
   <artifactid>spring-boot-starter-parent</artifactid>
   <version>1.4.1.release</version>
   <relativepath/> <!-- lookup parent from repository -->
</parent>
<dependency>
   <groupid>org.springframework.boot</groupid>
   <artifactid>spring-boot-starter-data-elasticsearch</artifactid>
 </dependency>

配置文件

bootstrap.yml

spring:
 data:
  elasticsearch:
   # 集群名
   cluster-name: syncwt-es
   # 连接节点,注意在集群中通信都是9300端口,否则会报错无法连接上!
   cluster-nodes: localhost:9300,119.29.38.169:9300
   # 是否本地连接
   local: false
   repositories:
    # 仓库中数据存储
    enabled: true

调试

启动

启动项目,日志出现以下说明代表成功。并且没有报错。

复制代码 代码如下:

 2017-03-30 19:35:23.078  info 20881 --- [           main] o.s.d.e.c.transportclientfactorybean     : adding transport node : localhost:9300

知识点

在elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以画一些简单的对比图来类比传统关系型数据库:

elasticsearch集群可以包含多个索引(indices)(数据库),每一个索引可以包含多个类型(types)(表),每一个类型包含多个文档(documents)(行),然后每个文档包含多个字段(fields)(列)

relational db -> databases -> tables -> rows -> columns
elasticsearch -> indices  -> types -> documents -> fields

demo

customer.java

/*
 * copyright 2012-2013 the original author or authors.
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

package com.syncwt.www.common.es;

import org.springframework.data.annotation.id;
import org.springframework.data.elasticsearch.annotations.document;

@document(indexname = "es-customer", type = "customer", shards = 2, replicas = 1, refreshinterval = "-1")
public class customer {

  @id
  private string id;

  private string firstname;

  private string lastname;

  public customer() {
  }

  public customer(string firstname, string lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }

  public string getid() {
    return this.id;
  }

  public void setid(string id) {
    this.id = id;
  }

  public string getfirstname() {
    return this.firstname;
  }

  public void setfirstname(string firstname) {
    this.firstname = firstname;
  }

  public string getlastname() {
    return this.lastname;
  }

  public void setlastname(string lastname) {
    this.lastname = lastname;
  }

  @override
  public string tostring() {
    return string.format("customer[id=%s, firstname='%s', lastname='%s']", this.id,
        this.firstname, this.lastname);
  }

}

customerrepository.java

/*
 * copyright 2012-2013 the original author or authors.
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

package com.syncwt.www.common.es;

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

import java.util.list;

public interface customerrepository extends elasticsearchrepository<customer, string> {

  public list<customer> findbyfirstname(string firstname);

  public list<customer> findbylastname(string lastname);

}

customercontroller.java

package com.syncwt.www.web;

import com.syncwt.www.response.message;
import com.syncwt.www.service.customerservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.restcontroller;

import java.io.ioexception;

/**
 * @version v0.0.1
 * @description customercontroller
 * @creation date 2017年03月30日 下午8:21
 * @modificationhistory who    when     what
 * --------  ----------  -----------------------------------
 */
@restcontroller
public class customercontroller {
  @autowired
  private customerservice customerservice;


  @requestmapping(value = "/test", method = requestmethod.get)
  public message test() throws ioexception {
    customerservice.savecustomers();
    customerservice.fetchallcustomers();
    customerservice.fetchindividualcustomers();
    return message.success;
  }
}

customerservice.java

package com.syncwt.www.service;

import com.syncwt.www.common.es.customer;
import com.syncwt.www.common.es.customerrepository;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;

import java.io.ioexception;

/**
 * @version v0.0.1
 * @description 业务层
 * @creation date 2017年03月30日 下午8:19
 * @modificationhistory who    when     what
 * --------  ----------  -----------------------------------
 */
@service
public class customerservice {

  @autowired
  private customerrepository repository;

  public void savecustomers() throws ioexception {
    repository.save(new customer("alice", "smith"));
    repository.save(new customer("bob", "smith"));
  }

  public void fetchallcustomers() throws ioexception {
    system.out.println("customers found with findall():");
    system.out.println("-------------------------------");
    for (customer customer : repository.findall()) {
      system.out.println(customer);
    }
  }

  public void fetchindividualcustomers() {
    system.out.println("customer found with findbyfirstname('alice'):");
    system.out.println("--------------------------------");
    system.out.println(repository.findbyfirstname("alice"));

    system.out.println("customers found with findbylastname('smith'):");
    system.out.println("--------------------------------");
    for (customer customer : repository.findbylastname("smith")) {
      system.out.println(customer);
    }
  }
}

spring对es的操作方法

spring-data-elasticsearch查询方法的封装

1、封装数据库基本crud(创建(create)、更新(update)、读取(retrieve)和删除(delete))

public interface crudrepository<t, id extends serializable>
 extends repository<t, id> {

 <s extends t> s save(s entity);

 t findone(id primarykey);    

 iterable<t> findall();     

 long count();          

 void delete(t entity);     

 boolean exists(id primarykey); 

 // … more functionality omitted.
}

2、分页排序查询

public interface pagingandsortingrepository<t, id extends serializable>
 extends crudrepository<t, id> {

 iterable<t> findall(sort sort);

 page<t> findall(pageable pageable);
}

//accessing the second page by a page size of 20
pagingandsortingrepository<user, long> repository = // … get access to a bean
page<user> users = repository.findall(new pagerequest(1, 20));

3、计数

public interface userrepository extends crudrepository<user, long> {

 long countbylastname(string lastname);
}

4、删除

public interface userrepository extends crudrepository<user, long> {

 long deletebylastname(string lastname);

 list<user> removebylastname(string lastname);

}

5、自定义查询方法自动注入

声明一个接口继承repository<t, id>

interface personrepository extends repository<person, long> { … }

接口中自定义方法,在方法名中包含t中字段名

查询关键字包括find…by, read…by, query…by, count…by, and get…by,熟悉直接可以用and and or连接

interface personrepository extends repository<person, long> {
list<person> findbylastname(string lastname);
}

保证注入了elasticsearch配置

在bootstrap.yml中写入了spring-data-elasticsearch的配置文件将自动注入

注入调用

public class someclient {

 @autowired
 private personrepository repository;

 public void dosomething() {
  list<person> persons = repository.findbylastname("matthews");
 }
}

6、支持java8 stream查询和sql语句查询

@query("select u from user u")
stream<user> findallbycustomqueryandstream();

stream<user> readallbyfirstnamenotnull();

@query("select u from user u")
stream<user> streamallpaged(pageable pageable);

try (stream<user> stream = repository.findallbycustomqueryandstream()) {
 stream.foreach(…);
}

7、支持异步查询

@async
future<user> findbyfirstname(string firstname);        

@async
completablefuture<user> findonebyfirstname(string firstname);

@async
listenablefuture<user> findonebylastname(string lastname);

支持原生es javaapi

1、nativesearchquerybuilder构建查询

@autowired
private elasticsearchtemplate elasticsearchtemplate;

searchquery searchquery = new nativesearchquerybuilder()
  .withquery(matchallquery())
  .withfilter(boolfilter().must(termfilter("id", documentid)))
  .build();

page<sampleentity> sampleentities =
  elasticsearchtemplate.queryforpage(searchquery,sampleentity.class);

2、利用scan和scroll进行大结果集查询

searchquery searchquery = new nativesearchquerybuilder()
  .withquery(matchallquery())
  .withindices("test-index")
  .withtypes("test-type")
  .withpageable(new pagerequest(0,1))
  .build();
string scrollid = elasticsearchtemplate.scan(searchquery,1000,false);
list<sampleentity> sampleentities = new arraylist<sampleentity>();
boolean hasrecords = true;
while (hasrecords){
  page<sampleentity> page = elasticsearchtemplate.scroll(scrollid, 5000l , new resultsmapper<sampleentity>()
  {
    @override
    public page<sampleentity> mapresults(searchresponse response) {
      list<sampleentity> chunk = new arraylist<sampleentity>();
      for(searchhit searchhit : response.gethits()){
        if(response.gethits().gethits().length <= 0) {
          return null;
        }
        sampleentity user = new sampleentity();
        user.setid(searchhit.getid());
        user.setmessage((string)searchhit.getsource().get("message"));
        chunk.add(user);
      }
      return new pageimpl<sampleentity>(chunk);
    }
  });
  if(page != null) {
    sampleentities.addall(page.getcontent());
    hasrecords = page.hasnextpage();
  }
  else{
    hasrecords = false;
  }
  }
}

3、获取client实例进行节点操作,可以自行封装util方法

@autowired
private elasticsearchtemplate elasticsearchtemplate;

public void searchhelper() throws ioexception {

    //节点客户端
    // on startup
//    node node = nodebuilder().clustername("syncwt-es").client(true).node();
//    client nodeclient = node.client();

    //传输客户端
//    settings settings = settings.settingsbuilder().build();
//    client transportclient = transportclient.builder().settings(settings).build();

    client transportclient = elasticsearchtemplate.getclient();

    customer customer = new customer("alice", "smith");

    // instance a json mapper
    objectmapper mapper = new objectmapper(); // create once, reuse

    // generate json
    string json = mapper.writevalueasstring(customer);
    system.out.println("--------------------------------jackson mapper");
    system.out.println(json);

    xcontentbuilder builder = jsonbuilder()
        .startobject()
        .field("firstname", "alice")
        .field("latname", "smith")
        .endobject();
    system.out.println("--------------------------------jsonbuilder");
    system.out.println(builder.string());

    indexresponse response = transportclient.prepareindex("es-customer", "customer")
        .setsource(jsonbuilder()
            .startobject()
            .field("firstname", "alice")
            .field("latname", "smith")
            .endobject()
        )
        .execute()
        .actionget();

    system.out.println("--------------------------------response");
    system.out.println(response.tostring());

    // on shutdown
//    node.close();
//    nodeclient.close();
    transportclient.close();

  }

总结

4、spring-data-elasticsearch对es有很好的支持,但很多高版本在spring-boot中不是很友好。所以,除了spring-boot自动配置的方法,最好掌握代码动态配置方法。

5、为了操作的便利性,我们往往需要动态索引,因为同一个索引(固定)是无法满足集群中多业务的。所以后续封装一个esutil类作为基本操作公交类

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。