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

SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)

程序员文章站 2022-05-29 09:55:20
准备工作 环境准备 JAVA版本 ES版本 SpringBoot版本 开发工具使用的是 安装ES Elasticsearch介绍以及安装: "ElasticSearch入门 基本概念介绍以及安装" 开始 创建SpringBoot项目 1. 打开IDEA,在菜单中点击 在弹框中选择 然后 2. 填写项 ......

准备工作

环境准备

java版本

java version "1.8.0_121"
java(tm) se runtime environment (build 1.8.0_121-b13)
java hotspot(tm) 64-bit server vm (build 25.121-b13, mixed mode)

es版本

{
  "name": "pyafjhz",
  "cluster_name": "my-cluster",
  "cluster_uuid": "oc28y-cnqdugitc7qq5w8w",
  "version": {
    "number": "6.8.2",
    "build_flavor": "oss",
    "build_type": "tar",
    "build_hash": "b506955",
    "build_date": "2019-07-24t15:24:41.545295z",
    "build_snapshot": false,
    "lucene_version": "7.7.0",
    "minimum_wire_compatibility_version": "5.6.0",
    "minimum_index_compatibility_version": "5.0.0"
  },
  "tagline": "you know, for search"
}

springboot版本

2.1.7.release

开发工具使用的是idea

安装es

elasticsearch介绍以及安装:elasticsearch入门-基本概念介绍以及安装

开始

创建springboot项目

  1. 打开idea,在菜单中点击
    file > new > project...
    在弹框中选择spring initializr
    SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)
    然后next

  2. 填写项目名等,然后next
    SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)
  3. 选择依赖的jar包(一般我只选lombok,其他的自己手动加),然后next
    SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)

  4. 最后选择项目所在路径,点击finish
    SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)

搞定收工。至此,一个新的springboot项目就新鲜出炉了。

pom文件

当然,具体依赖的jar包肯定不止第2步选择的那些,其中springboot提供的操作es的jar包spring-boot-starter-data-elasticsearch当然也是必不可少的。

这里贴出最终的pom文件:

<?xml version="1.0" encoding="utf-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.1.7.release</version>
        <relativepath/> <!-- lookup parent from repository -->
    </parent>
    <groupid>com.lifengdi</groupid>
    <artifactid>search</artifactid>
    <version>0.0.1-snapshot</version>
    <name>search</name>
    <description>elasticsearch</description>

    <properties>
        <java.version>1.8</java.version>
        <testng.version>6.14.2</testng.version>
        <spring-cloud-dependencies.version>greenwich.release</spring-cloud-dependencies.version>
        <kibana-logging-spring-boot-starter.version>1.2.4</kibana-logging-spring-boot-starter.version>
        <fastjson.version>1.2.47</fastjson.version>
        <alarm-spring-boot-starter.version>1.0.15-snapshot</alarm-spring-boot-starter.version>
    </properties>

    <dependencymanagement>
        <dependencies>
            <dependency>
                <groupid>org.springframework.cloud</groupid>
                <artifactid>spring-cloud-dependencies</artifactid>
                <version>${spring-cloud-dependencies.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencymanagement>

    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!--elasticsearch-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-data-elasticsearch</artifactid>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-configuration-processor</artifactid>
            <optional>true</optional>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
        <!--测试-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupid>org.testng</groupid>
            <artifactid>testng</artifactid>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- 日期处理 -->
        <dependency>
            <groupid>joda-time</groupid>
            <artifactid>joda-time</artifactid>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupid>com.alibaba</groupid>
            <artifactid>fastjson</artifactid>
            <version>${fastjson.version}</version>
        </dependency>
        <!--feign-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-openfeign</artifactid>
        </dependency>

        <dependency>
            <groupid>org.apache.commons</groupid>
            <artifactid>commons-lang3</artifactid>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
            </plugin>
        </plugins>
    </build>

</project>

application.yml文件

application.yml文件配置如下:

server:
  port: 8080
  servlet:
    context-path: /search
spring:
  application:
    name: search
  data:
    elasticsearch:
      cluster-name: my-cluster
      cluster-nodes: localhost:9300
  jackson:
    default-property-inclusion: non_null

logging:
  file: application.log
  path: .
  level:
    root: info
    com.lifengdi.store.client: debug

index-entity:
  configs:
    - doccode: store
      indexname: store
      type: base
      documentpath: com.lifengdi.document.storedocument

spring.data.elasticsearch.cluster-name:集群名称

spring.data.elasticsearch.cluster-nodes:集群节点地址列表,多个节点用英文逗号(,)分隔

创建es文档和映射

首先创建一个java对象,然后通过注解来声明字段的映射属性。
spring提供的注解有@document@id@field,其中@document作用在类,@id@field作用在成员变量,@id标记一个字段作为id主键。

package com.lifengdi.document;

import com.lifengdi.document.store.*;
import com.lifengdi.search.annotation.definitionquery;
import com.lifengdi.search.enums.querytypeenum;
import lombok.data;
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.annotations.fieldtype;

import java.util.list;

/**
 * 门店document
 *
 * @author 李锋镝
 * @date create at 19:31 2019/8/22
 */
@document(indexname = "store", type = "base")
@data
@definitionquery(key = "page", type = querytypeenum.ignore)
@definitionquery(key = "size", type = querytypeenum.ignore)
@definitionquery(key = "q", type = querytypeenum.fulltext)
public class storedocument {

    @id
    @definitionquery(type = querytypeenum.in)
    @definitionquery(key = "id", type = querytypeenum.in)
    @field(type = fieldtype.keyword)
    private string id;

    /**
     * 基础信息
     */
    @field(type = fieldtype.object)
    private storebaseinfo baseinfo;

    /**
     * 标签
     */
    @field(type = fieldtype.nested)
    @definitionquery(key = "tagcode", mapped = "tags.key", type = querytypeenum.in)
    @definitionquery(key = "tagvalue", mapped = "tags.value", type = querytypeenum.and)
    @definitionquery(key = "_tagvalue", mapped = "tags.value", type = querytypeenum.in)
    private list<storetags> tags;

}

创建索引

elasticsearchtemplate提供了四个createindex()方法来创建索引,可以根据类的信息自动生成,也可以手动指定indexname和settings

@override
public <t> boolean createindex(class<t> clazz) {
    return createindexifnotcreated(clazz);
}

@override
public boolean createindex(string indexname) {
    assert.notnull(indexname, "no index defined for query");
    return client.admin().indices().create(requests.createindexrequest(indexname)).actionget().isacknowledged();
}
@override
public boolean createindex(string indexname, object settings) {
    createindexrequestbuilder createindexrequestbuilder = client.admin().indices().preparecreate(indexname);
    if (settings instanceof string) {
        createindexrequestbuilder.setsettings(string.valueof(settings), requests.index_content_type);
    } else if (settings instanceof map) {
        createindexrequestbuilder.setsettings((map) settings);
    } else if (settings instanceof xcontentbuilder) {
        createindexrequestbuilder.setsettings((xcontentbuilder) settings);
    }
    return createindexrequestbuilder.execute().actionget().isacknowledged();
}

@override
public <t> boolean createindex(class<t> clazz, object settings) {
    return createindex(getpersistententityfor(clazz).getindexname(), settings);
}

创建映射

elasticsearchtemplate提供了三个putmapping()方法来创建映射

@override
public <t> boolean putmapping(class<t> clazz) {
    if (clazz.isannotationpresent(mapping.class)) {
        string mappingpath = clazz.getannotation(mapping.class).mappingpath();
        if (!stringutils.isempty(mappingpath)) {
            string mappings = readfilefromclasspath(mappingpath);
            if (!stringutils.isempty(mappings)) {
                return putmapping(clazz, mappings);
            }
        } else {
            logger.info("mappingpath in @mapping has to be defined. building mappings using @field");
        }
    }
    elasticsearchpersistententity<t> persistententity = getpersistententityfor(clazz);
    xcontentbuilder xcontentbuilder = null;
    try {

        elasticsearchpersistentproperty property = persistententity.getrequiredidproperty();

        xcontentbuilder = buildmapping(clazz, persistententity.getindextype(),
                property.getfieldname(), persistententity.getparenttype());
    } catch (exception e) {
        throw new elasticsearchexception("failed to build mapping for " + clazz.getsimplename(), e);
    }
    return putmapping(clazz, xcontentbuilder);
}

@override
public <t> boolean putmapping(class<t> clazz, object mapping) {
    return putmapping(getpersistententityfor(clazz).getindexname(), getpersistententityfor(clazz).getindextype(),
            mapping);
}

@override
public boolean putmapping(string indexname, string type, object mapping) {
    assert.notnull(indexname, "no index defined for putmapping()");
    assert.notnull(type, "no type defined for putmapping()");
    putmappingrequestbuilder requestbuilder = client.admin().indices().prepareputmapping(indexname).settype(type);
    if (mapping instanceof string) {
        requestbuilder.setsource(string.valueof(mapping), xcontenttype.json);
    } else if (mapping instanceof map) {
        requestbuilder.setsource((map) mapping);
    } else if (mapping instanceof xcontentbuilder) {
        requestbuilder.setsource((xcontentbuilder) mapping);
    }
    return requestbuilder.execute().actionget().isacknowledged();
}

测试代码如下

@test
public void testcreate() {
    system.out.println(elasticsearchtemplate.createindex(storedocument.class));
    system.out.println(elasticsearchtemplate.putmapping(storedocument.class));
}

删除索引

elasticsearchtemplate提供了2个deleteindex()方法来删除索引

@override
public <t> boolean deleteindex(class<t> clazz) {
    return deleteindex(getpersistententityfor(clazz).getindexname());
}

@override
public boolean deleteindex(string indexname) {
    assert.notnull(indexname, "no index defined for delete operation");
    if (indexexists(indexname)) {
        return client.admin().indices().delete(new deleteindexrequest(indexname)).actionget().isacknowledged();
    }
    return false;
}

新增&修改文档

在elasticsearch中文档是不可改变的,不能修改它们。相反,如果想要更新现有的文档,需要重建索引或者进行替换。

所以可以使用和新增同样的接口来对文档进行修改操作。区分的依据就是id。

下面提供新增&修改文档的其中两种方法,一种是通过elasticsearchtemplate提供的index()方法:

@override
public string index(indexquery query) {
    string documentid = prepareindex(query).execute().actionget().getid();
    // we should call this because we are not going through a mapper.
    if (query.getobject() != null) {
        setpersistententityid(query.getobject(), documentid);
    }
    return documentid;
}

示例代码如下:

/**
 * 更新索引
 * @param indexname 索引名称
 * @param type 索引类型
 * @param id id
 * @param jsondoc json格式的文档
 * @param refresh 是否刷新索引
 * @return id
 */
public string index(string indexname, string type, string id, jsonnode jsondoc, boolean refresh)
            throws jsonprocessingexception {

        log.info("abstractdocumentindexservice更新索引.indexname:{},type:{},id:{},jsondoc:{}", indexname, type, id, jsondoc);
        indexquery indexquery = new indexquerybuilder()
                .withindexname(indexname)
                .withtype(type)
                .withid(id)
                .withsource(objectmapper.writevalueasstring(jsondoc))
                .build();
        try {
            if (elasticsearchtemplate.indexexists(indexname)) {
                string index = elasticsearchtemplate.index(indexquery);
                if (refresh) {
                    elasticsearchtemplate.refresh(indexname);
                }
                return index;
            }
        } catch (exception e) {
            log.error("更新索引失败,刷新es重试", e);
            elasticsearchtemplate.refresh(indexname);
            return elasticsearchtemplate.index(indexquery);
        }
        throw baseexception.index_not_exists_exception.build();
    }

另一种则是通过repository接口。spring提供的es的repository接口为elasticsearchcrudrepository,所以我们就可以直接定义额新的接口,然后实现elasticsearchcrudrepository即可:

package com.taoche.docindex.repo;

import com.taoche.document.storedocument;
import org.springframework.data.elasticsearch.repository.elasticsearchrepository;

/**
 * 门店repository
 * @author 李锋镝
 * @date create at 09:30 2019/8/23
 */
public interface storerepository extends elasticsearchrepository<storedocument, string> { }

示例代码如下:

@test
public void testsave() {

    storedocument storedocument = new storedocument();
    storedocument.setid("1");
    storebaseinfo baseinfo = new storebaseinfo();
    baseinfo.setstoreid("1");
    baseinfo.setcreatedtime(datetime.now());
    storedocument.setbaseinfo(baseinfo);

    storerepository.save(storedocument);
}

查询

es的主要功能就是查询,elasticsearchrepository也提供了基本的查询接口,比如findbyid()findall()findallbyid()search()等方法;当然也可以使用spring data提供的另外一个功能:spring data jpa——通过方法名创建查询,当然需要遵循一定的规则,比如你的方法名叫做findbytitle(),那么它就知道你是根据title查询,然后自动帮你完成,这里就不仔细说了。

上边说的基本能满足一般的查询,复杂一点的查询就无能为力了,这就需要用到自定义查询,这里可以查看我的另一篇博客springboot使用注解的方式构建elasticsearch查询语句,实现多条件的复杂查询,这里边有详细的说明。

另外还有一个比较厉害的功能,elasticsearch的聚合;聚合主要实现的是对数据的统计、分析。这个暂时没有用到的,所以要看聚合功能的小伙伴们可能要失望了~ 哈哈哈~~~

聚合功能以后有时间会再单独说的~都会有的。

至此,springboot整合elasticsearch基本结束,有什么不明白的地方请留言~

源码

git项目地址:

如果觉得有帮助的话,请帮忙点赞、点星小小的支持一下~

谢谢~~

原文链接: