当前位置: 移动技术网 > IT编程>开发语言>Java > Spring Boot集成ElasticSearch实现搜索引擎的示例

Spring Boot集成ElasticSearch实现搜索引擎的示例

2019年07月19日  | 移动技术网IT编程  | 我要评论
elastic search是一个开源的,分布式,实时搜索和分析引擎。spring boot为elasticsearch及spring data elasticsearch

elastic search是一个开源的,分布式,实时搜索和分析引擎。spring boot为elasticsearch及spring data elasticsearch提供的基于它的抽象提供了基本的配置。spring boot提供了一个用于聚集依赖的spring-boot-starter-data-elasticsearch 'starterpom'。

elasticsearch作为搜索引擎,我们需要解决2大问题:

1,  如何将被搜索的数据在es上创建反向索引
2,  java代码如何与es交互

其中第一个大问题又分为两个小问题

1.1,如何初始化已有的数据
1.2,如何同步增量数据

第二个大问题也有两种集成方式

2.1 spring data 9300端口集成
2.2 restful api 9200端口集成

本篇先解决第二大问题。

第一种方式,利用restapi方式,也叫jest方式:

示例代码:

pom.xml:

<project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" 
 xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
 <modelversion>4.0.0</modelversion> 
 
 <groupid>yejingtao.demo.springcloud</groupid> 
 <artifactid>demo-jest-elasticsearch</artifactid> 
 <version>0.0.1-snapshot</version> 
 <packaging>jar</packaging> 
 
 <name>demo-jest-elasticsearch</name> 
 <url>http://maven.apache.org</url> 
 
 <properties> 
  <project.build.sourceencoding>utf-8</project.build.sourceencoding> 
 </properties> 
  
 <parent> 
    <groupid>org.springframework.boot</groupid> 
    <artifactid>spring-boot-starter-parent</artifactid> 
    <version>1.5.6.release</version> 
  </parent> 
   
  <dependencies> 
    <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>io.searchbox</groupid> 
      <artifactid>jest</artifactid> 
    </dependency> 
    <dependency> 
      <groupid>net.java.dev.jna</groupid> 
      <artifactid>jna</artifactid> 
    </dependency> 
  </dependencies> 
</project> 

application.yml:

server: 
 port: 7081 
 
spring: 
 elasticsearch: 
  jest: 
   uris: 
   - http://192.168.226.133:9200 
   read-timeout: 5000 

注意这里是9200端口

主程序:最简单的spring boot启动程序:

@springbootapplication 
public class esapplication { 
 
  public static void main(string[] args) { 
    springapplication.run(esapplication.class); 
  } 
} 

定义好es中的实体类和对es操作的接口:

public class entity implements serializable{ 
 
  private static final long serialversionuid = -763638353551774166l; 
   
  public static final string index_name = "index_entity"; 
   
  public static final string type = "tstype"; 
 
  private long id; 
   
  private string name; 
   
  public entity() { 
    super(); 
  } 
   
  public entity(long id, string name) { 
    this.id = id; 
    this.name = name; 
  } 
 
  public long getid() { 
    return id; 
  } 
 
  public void setid(long id) { 
    this.id = id; 
  } 
 
  public string getname() { 
    return name; 
  } 
 
  public void setname(string name) { 
    this.name = name; 
  } 
   
   
} 
public interface cityesservice { 
   
  void saveentity(entity entity); 
   
  void saveentity(list<entity> entitylist); 
   
  list<entity> searchentity(string searchcontent); 
} 

接口实现:

@service 
public class cityesserviceimpl implements cityesservice{ 
   
  private static final logger logger = loggerfactory.getlogger(cityesserviceimpl.class); 
   
  @autowired 
  private jestclient jestclient; 
   
  @override 
  public void saveentity(entity entity) { 
    index index = new index.builder(entity).index(entity.index_name).type(entity.type).build(); 
    try { 
      jestclient.execute(index); 
      logger.info("es 插入完成"); 
    } catch (ioexception e) { 
      e.printstacktrace(); 
      logger.error(e.getmessage()); 
    } 
  } 
   
   
  /** 
   * 批量保存内容到es 
   */ 
  @override 
  public void saveentity(list<entity> entitylist) { 
    bulk.builder bulk = new bulk.builder(); 
    for(entity entity : entitylist) { 
      index index = new index.builder(entity).index(entity.index_name).type(entity.type).build(); 
      bulk.addaction(index); 
    }     
    try { 
      jestclient.execute(bulk.build()); 
      logger.info("es 插入完成"); 
    } catch (ioexception e) { 
      e.printstacktrace(); 
      logger.error(e.getmessage()); 
    } 
  } 
   
  /** 
   * 在es中搜索内容 
   */ 
  @override 
  public list<entity> searchentity(string searchcontent){ 
    searchsourcebuilder searchsourcebuilder = new searchsourcebuilder(); 
    //searchsourcebuilder.query(querybuilders.querystringquery(searchcontent)); 
    //searchsourcebuilder.field("name"); 
    searchsourcebuilder.query(querybuilders.matchquery("name",searchcontent)); 
    search search = new search.builder(searchsourcebuilder.tostring()) 
        .addindex(entity.index_name).addtype(entity.type).build(); 
    try { 
      jestresult result = jestclient.execute(search); 
      return result.getsourceasobjectlist(entity.class); 
    } catch (ioexception e) { 
      logger.error(e.getmessage()); 
      e.printstacktrace(); 
    } 
    return null;     
  } 
} 

这里插入数据的方式给了两种,一种是单次api直接插入,一种是利用es的bulk批量插入。

做一个controller方面我们测试:

启动后在浏览器中请求http://www.lhsxpumps.com/_localhost:7081/entitycontroller/search?name=%e4%ba%ba%e6%89%8b%e4%ba%95

得到结果:

这里只返回了9条记录,而理论上es默认的size是10,应该不是分页的问题,而是只能检索出9条匹配记录,用kibana连上相同的搜索确认下:

这里用的是standard分词方式,将每个中文都作为了一个term,凡是包含“人”“手”“井”的都被搜索了出来,只是评分不同,如果想支持只能中文索引需要依赖ik插件

ok,restful方式对elasticsearch的检索已经搞定了,更多的扩展可以慢慢研究下querybuilders里的源码和批注。

第二种方式,利用spring data客户端方式:

事先说明此方式有个弊端,让我掉了坑里好久才爬上来,spring data elasticsearch必须与elasticsearch版本相匹配,否则在对接时es端会报版本不匹配错误,例如我es是5.6.1版本,spring boot是1.5.6版本,错误如下:

为解决这个问题我查找了一些资料,spring data与elasticsearch版本对应关系如下:

spring data elasticsearch

elasticsearch

3.0.0.rc2

5.5.0

3.0.0.m4

5.4.0

2.0.4.release

2.4.0

2.0.0.release

2.2.0

1.4.0.m1

1.7.3

1.3.0.release

1.5.2

1.2.0.release

1.4.4

1.1.0.release

1.3.2

1.0.0.release

1.1.1

而我用的spring boot 1.5.6版本对应的spring data elasticsearch是2.1.6版本,不支持5.x的es,所以报错。到本博文撰写为止,spring boot的release版本最新的是1.5.8,对应的spring data elasticsearch是2.1.8,仍不支持5.x的es,所以如果一定要使用java客户端方式集成es只能放弃spring boot直接使用spring data和spring mvc,或者降低es的版本使之与spring boot匹配。

示例代码:

pom.xml依赖:

<project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" 
 xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
 <modelversion>4.0.0</modelversion> 
 
 <groupid>yejingtao.demo.springcloud</groupid> 
 <artifactid>demo-data-elasticsearch</artifactid> 
 <version>0.0.1-snapshot</version> 
 <packaging>jar</packaging> 
 
 <name>demo-data-elasticsearch</name> 
 <url>http://maven.apache.org</url> 
 
 <properties> 
  <project.build.sourceencoding>utf-8</project.build.sourceencoding> 
 </properties> 
  
 <parent> 
    <groupid>org.springframework.boot</groupid> 
    <artifactid>spring-boot-starter-parent</artifactid> 
    <version>1.5.8.release</version> 
  </parent> 
   
  <dependencies> 
    <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> 
  </dependencies> 
</project> 

不再引用jest。

application.yml:

server: 
 port: 7081 
 
spring: 
 data: 
  elasticsearch: 
   cluster-nodes: 192.168.226.133:9300 
   cluster-name: my-es 
   repositories: 
    enabled: true 

注意这里是9300端口

controller、主程序、service接口同jest项目不变,不再罗列

实体类稍作变化,指定es中的index和type:

@document(indexname="index_entity", type="tstype") 

多一个repository接口,无需实现类,spring data标准用法:

/** 
 * entity es操作类 
 * @author yejingtao 
 * 
 */ 
public interface entityrepository extends elasticsearchrepository<entity,long>{ 
 
} 

service实现类与jest的天壤之别了,从语法上可以看出更像是对数据库层的操作:

@service 
public class cityesserviceimpl implements cityesservice{ 
   
  private static final logger logger = loggerfactory.getlogger(cityesserviceimpl.class); 
   
  int page_size = 15; //默认分页大小 
   
  int page_number = 0; //默认当前分页 
   
  string score_mode_sum = "sum"; //权重分求和模式 
   
  float min_score = 10.0f; //由于无相关性的分值默认为1, 设置权重分最小值为10 
   
  @autowired 
  entityrepository entityrepository; 
   
  /** 
   * 保存内容到es 
   */ 
  @override 
  public long saveentity(entity entity) { 
    entity entityresult = entityrepository.save(entity); 
    return entityresult.getid(); 
  } 
   
  /** 
   * 在es中搜索内容 
   */ 
  @override 
  public list<entity> searchentity(int pagenumber, int pagesize, string searchcontent){ 
    if(pagesize==0) { 
      pagesize = page_size; 
    } 
    if(pagenumber<0) { 
      pagenumber = page_number; 
    } 
     
    searchquery searchquery = getentitysearchquery(pagenumber,pagesize,searchcontent); 
     
    logger.info("\n searchcity: searchcontent [" + searchcontent + "] \n dsl = \n "  
        + searchquery.getquery().tostring()); 
 
     
    page<entity> citypage = entityrepository.search(searchquery); 
    return citypage.getcontent(); 
  } 
   
  /** 
   * 组装搜索query对象 
   * @param pagenumber 
   * @param pagesize 
   * @param searchcontent 
   * @return 
   */ 
  private searchquery getentitysearchquery(int pagenumber, int pagesize, string searchcontent) { 
    functionscorequerybuilder functionscorequerybuilder = querybuilders.functionscorequery() 
        .add(querybuilders.matchphrasequery("name", searchcontent), 
            scorefunctionbuilders.weightfactorfunction(1000)) 
        //.add(querybuilders.matchphrasequery("other", searchcontent), 
            //scorefunctionbuilders.weightfactorfunction(1000)) 
        .scoremode(score_mode_sum).setminscore(min_score); 
    //设置分页,否则只能按照es默认的分页给 
    pageable pageable = new pagerequest(pagenumber, pagesize); 
    return new nativesearchquerybuilder().withpageable(pageable).withquery(functionscorequerybuilder).build(); 
  } 
   
} 

测试方式同jest。

这两种方式,从设计上来讲属于两种思路,spring data的思路就是将elasticsearch当自家的数据仓库来管理,直接通过java客户端代码操作es;jest的思路是将elasticsearch当为独立的服务端,自己作为客户端用兼容性最强的restful格式来与之交互。
个人比较倾向于jest方式,第一兼容性好,不需要考虑版本的问题。第二,从elasticsearch本身的设计上来分析,9200是对外服务端口,9300是内部管理和集群通信端口,请求9200获取搜索服务更符合es的设计初衷,不会影响集群内部的通信。
以上比较分析仅代表个人观点,欢迎大神么交流批评。希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网