后端开发之Elasticsearch篇----SpringBoot整合ES

SpringBoot整合ES

sb添加es的依赖和配置

在pom中添加如下依赖,现在的版本2.2.2.RELEASE对es的整合版本是6.4.3

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-data-elasticsearchartifactId>
	
	<version>2.2.2.RELEASEversion>
dependency>

然后,配置application.yml

spring:
  data:
    elasticsearch:
      cluster-name: es-cluster
      cluster-nodes: 192.168.0.101:9300 # 这里注意了,sb整合es中的通讯端口是9300不是9200

通讯端口是9300不是9200

添加一个配置类,和Application启动类同级或者创建一个config的目录放置这个配置类

package com.imooc;

import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class ESConfig {

    @PostConstruct
    void init() {
        System.setProperty("es.set.netty.runtime.available.processors","false");
    }

如果没有配置的话,运行会报错

ERROR SpringApplication Application run failed
 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchClient' defined in class path resource [org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'elasticsearchClient' threw exception; nested exception is java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:843)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'elasticsearchClient' threw exception; nested exception is java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
	... 42 more
Caused by: java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]
	at io.netty.util.NettyRuntime$AvailableProcessorsHolder.setAvailableProcessors(NettyRuntime.java:51)
	at io.netty.util.NettyRuntime.setAvailableProcessors(NettyRuntime.java:87)
	at org.elasticsearch.transport.netty4.Netty4Utils.setAvailableProcessors(Netty4Utils.java:83)
	at org.elasticsearch.transport.netty4.Netty4Transport.<init>(Netty4Transport.java:112)
	at org.elasticsearch.transport.Netty4Plugin.lambda$getTransports$0(Netty4Plugin.java:86)
	at org.elasticsearch.client.transport.TransportClient.buildTemplate(TransportClient.java:189)
	at org.elasticsearch.client.transport.TransportClient.<init>(TransportClient.java:283)
	at org.elasticsearch.transport.client.PreBuiltTransportClient.<init>(PreBuiltTransportClient.java:128)
	at org.elasticsearch.transport.client.PreBuiltTransportClient.<init>(PreBuiltTransportClient.java:114)
	at org.elasticsearch.transport.client.PreBuiltTransportClient.<init>(PreBuiltTransportClient.java:104)
	at org.springframework.data.elasticsearch.client.TransportClientFactoryBean.buildClient(TransportClientFactoryBean.java:85)
	at org.springframework.data.elasticsearch.client.TransportClientFactoryBean.afterPropertiesSet(TransportClientFactoryBean.java:80)
	at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration.elasticsearchClient(ElasticsearchAutoConfiguration.java:60)
	at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration$$EnhancerBySpringCGLIB$$b08b94eb.CGLIB$elasticsearchClient$0(<generated>)
	at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration$$EnhancerBySpringCGLIB$$b08b94eb$$FastClassBySpringCGLIB$$3ccdc4d6.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
	at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration$$EnhancerBySpringCGLIB$$b08b94eb.elasticsearchClient(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 43 more
ERROR TestContextManager Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@632241f5] to prepare test instance [com.test.ESTest@64bba0eb]
 java.lang.IllegalStateException: Failed to load ApplicationContext
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

sb中es的api

我们使用的是ElasticsearchTemplate这个模版类

@Autowired
private ElasticsearchTemplate esTemplate;

假设我们现在有一个Stu的学生类和stu这个索引关联着的

package com.imooc.es.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

@Document(indexName = "stu", type = "_doc")
public class Stu {

    @Id //有这个注解,es会根据这个属性值来构建_id
    private Long stuId;

    @Field(store = true)
    private String name;

    @Field(store = true)
    private Integer age;

    @Field(store = true)
    private Float money;

    @Field(store = true)
    private String sign;

    @Field(store = true)
    private String description;

    public Long getStuId() {
        return stuId;
    }

    public void setStuId(Long stuId) {
        this.stuId = stuId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "stuId=" + stuId +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                ", sign='" + sign + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}

创建索引
public void createIndexStu() {
        //创建一个Stu对象
        Stu stu = new Stu();
        stu.setStuId(1001L);
        stu.setName("spider man");
        stu.setAge(54);
        stu.setMoney(1999.8f);
        stu.setSign("i am spider man");
        stu.setDescription("i wish i can be spider man");

        //创建一个IndexQuery对象,使用其工厂类构建,使用withObject这个api来绑定一个对象,然后会根据这个对象来构建索引
        IndexQuery indexQuery = new IndexQueryBuilder().withObject(stu).build();
        esTemplate.index(indexQuery);
    }
删除索引
public void deleteIndexStu() {
        //使用es的模版对象的deleteIndex来删除索引,可以传入索引名称或者其绑定的对象类
        boolean flag = esTemplate.deleteIndex("stu");
        System.out.println(flag);
    }
更新文档
public void updateStuDoc() {

        //1.将要修改的字段和其修改后的值使用map来绑定,字段为key,修改值为value
        Map<String, Object> sourceMap = new HashMap<>();
        sourceMap.put("name", "doudou");

        //2.创建indexRequest对象,需要传入一个map来定义哪些字段要改成什么值
        IndexRequest indexRequest = new IndexRequest();
        indexRequest.source(sourceMap);

        //3.老套路,因为模版类需要一个updateQuery的对象,所以使用它的工厂类来创建,同时绑定文档的id和其对应的索引,使用withIndexRequest这个api来绑定IndexRequest对象
        UpdateQuery updateQuery = new UpdateQueryBuilder()
                                         .withId("1001")
                                         .withClass(Stu.class)                                       
                                         .withIndexRequest(indexRequest).build();
        esTemplate.update(updateQuery);
    }
根据id查询文档

这只能一次查一个文档

public void getStuDco() {
        //需要的GetQuery对象,不需要使用其工厂类来构建了,直接new出来就行了,和创建index,更新文档,删除index都不一样
        GetQuery getQuery = new GetQuery();
        getQuery.setId("1001");
        Stu stu = esTemplate.queryForObject(getQuery, Stu.class);
        System.out.println(stu);
    }
根据检索关键字来查询和分页功能
public void searchStuDoc() {

        Pageable pageable = PageRequest.of(0,5);

        //1.使用其原生类的工厂类,withQuery这个api来绑定QueryBuilder对象来检索,但是QueryBuilder对象使用其工具类来创建,可以接分页,使用withPageable这个api
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("description","spider"))
                .withPageable(pageable)   //绑定分页对象
                .build();


        // 使用queryForPage这个api来检索,传入一个searchQuery后面加索引绑定的对象类
        AggregatedPage<Stu> aggregatedPage = esTemplate.queryForPage(searchQuery, Stu.class);
        List<Stu> stuList = aggregatedPage.getContent();

        for(Stu stu:stuList) {
            System.out.println(stuList);
        }

    }
  • 这些解释以下,因为withQuery中需要传入的参数是以恶搞QueryBuilder的对象,我们现在使用QueryBuilders这个工具类同时设置了match条件来创建一个QueryBuilder对象
  • queryForPage的返回对象是一个AggregatedPage,其getContent获取的是List对象,其get这个api获取的是Stream对象,熟悉流式处理的同学可以使用get
高亮

这里的代码有点长,主要我们是通过SearchResultMapper这个接口中的mapResult方法中的SearchResponse的对象来得到高亮的内容

public void highlightStuDoc() {

        String preTag = "";
        String postTag = "";

        Pageable pageable = PageRequest.of(0,5);

        //定义queryForPage的第一个参数:SearchQuery
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("description","spider"))
                .withHighlightFields(new HighlightBuilder.Field("description")
                                                                  .preTags(preTag)  // 设置高亮的前标签
                                                                  .postTags(postTag)) //设置高亮得到后标签
                .withPageable(pageable)
                .build();

        //使用高亮也是模版类的queryForPage,就是后面接多个SearchResultMapper的接口
        AggregatedPage<Stu> aggregatedPage = esTemplate.queryForPage(searchQuery, Stu.class, new SearchResultMapper() {
            /**
             *
             * @param response 这个是查询后的响应结果,高亮部分可以从这里取出来
             * @param clazz
             * @param pageable
             * @param 
             * @return
             */
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

                System.out.println(response);

                List<Stu> stuList = new ArrayList<>();

                //1.和数据有关系的部分是hits部分
                SearchHits hits = response.getHits();
                for(SearchHit h:hits){

                    //获取高亮字段
                    HighlightField highlightField = h.getHighlightFields().get("description");
                    String description = highlightField.getFragments()[0].toString();

                    Object stuId = h.getSourceAsMap().get("stuId");
                    String name = (String)h.getSourceAsMap().get("name");
                    Integer age = (Integer)h.getSourceAsMap().get("age");
                    String sign = (String)h.getSourceAsMap().get("sign");
                    Object money = (Object)h.getSourceAsMap().get("money");

                    Stu stu = new Stu();
                    stu.setStuId(Long.valueOf(stuId.toString()));
                    stu.setName(name);
                    stu.setAge(age);
                    stu.setSign(sign);
                    stu.setMoney(Float.valueOf(money.toString()));
                    stu.setDescription(description);

                    stuList.add(stu);
                }

                if (stuList.size() > 0) {
                    return  new AggregatedPageImpl<>((List<T>)stuList);
                }

                return null;
            }
        });

        List<Stu> stuList = aggregatedPage.getContent();
        for(Stu stu:stuList){
            System.out.println(stu);
        }
    }

我们使用断点的方式来查看以下SearchResponse的内容
后端开发之Elasticsearch篇----SpringBoot整合ES_第1张图片
后端开发之Elasticsearch篇----SpringBoot整合ES_第2张图片
可以转换成json体来看

{
	"took":5,
	"timed_out":false,
	"_shards":{
		"total":5,
		"successful":5,
		"skipped":0,
		"failed":0
	},
	"_clusters":{
		"total":0,
		"successful":0,
		"skipped":0
	},
	"hits":{
		"total":1,
		"max_score":0.2876821,
		"hits":[
			{
				"_index":"stu",
				"_type":"_doc",
				"_id":"1001",
				"_version":2,
				"_score":0.2876821,
				"_source":{
					"stuId":1001,
					"name":"doudou",
					"age":54,
					"money":1999.8,
					"sign":"i am spider man",
					"description":"i wish i can be spider man"
				},
				"highlight":{
					"description":[
						"i wish i can be spider man"]
					}
				}
			]
		}
	}

SearchResponse的getHits方法将直接获取到第二个hits的对象内容,再从其highlight属性中可以获取标有高亮的属性和其值,直接是highlightFields[0]

你可能感兴趣的:(后端)