SpringBoot2整合ElasticSearch(包含ElasticSearch入门+spring-boot-starter-data-elasticsearch)

前言

作为互联网热点知识的ElasticSearch,怎能不学。如果你有空余时间,欢迎入门;如果你没空余时间,也欢迎走马观花看一眼。走过如果不要错过,这是一篇自我感觉相对对入门者来说比较全面的文章了,希望对大家有帮助,有什么疑问或者建议欢迎留言讨论。

Why&What Elasticsearch?

  • 概念Elasticsearch 是一个开源分布式高扩展高实时的RESTful 搜索和分析引擎,基于Lucene
  • 背景Elasticsearch是与名为Logstash的数据收集和日志解析引擎以及名为Kibana的分析和可视化平台一起开发的。这三个产品被设计成一个集成解决方案,称为“Elastic Stack”(以前称为“ELK stack”)。
  • 价值Elasticsearch能很方便的使大量数据具有搜索分析探索的能力,能使数据在生产环境变得更有价值
  • 步骤:首先用户将数据提交到Elastic Search 数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名打分,再将返回结果呈现给用户。
  • 谁在用: 1. Wikipedia 使用 Elasticsearch 提供带有高亮片段的全文搜索,还有 search-as-you-type 和 did-you-mean 的建议。2. Stack Overflow 将地理位置查询融入全文检索中去,并且使用 more-like-this 接口去查找相关的问题和回答。3. GitHub 使用 Elasticsearch 对1300亿行代码进行查询。
  • 端口9200http协议的RESTful接口。9300tcp通讯端口,集群间和TCPClient都走的它。

ElasticSearch&DB的区别

DB ES
`database`数据库 `index`索引
`table`表 `type`类型
`row`行(一条数据) `document`文档
`column`列(字段名) `field`字段

Download&Install Elasticsearch

  1. https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.8.1.zip下载6.8.1版本 如果从官网https://www.elastic.co/cn/downloads/elasticsearch下载Elasticsearch,下载到的版本太新,客户端不支持
  2. 运行bin\elasticsearch.bat
  3. 访问http://127.0.0.1:9200/ ,见到以下返回信息(目前推荐ES6.8.1的版本,比较靠谱,minimum_index_compatibility_version=5.X的,最新版ES7最低的client版本是6.X,会有兼容问题,请看末尾版本兼容问题部分)
{
    "name": "sUR4zrr",
    "cluster_name": "elasticsearch",
    "cluster_uuid": "ODsZw-P1QMe9CDeOqEQQvA",
    "version": {
        "number": "6.7.1",
        "build_flavor": "default",
        "build_type": "zip",
        "build_hash": "2f32220",
        "build_date": "2019-04-02T15:59:27.961366Z",
        "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"
}
  1. 下载Chrome插件 elasticsearch-head https://www.chromefor.com/elasticsearch-head_v0-1-4/dejavu-elasticsearch-web-ui https://www.chromefor.com/dejavu-elasticsearch-web-ui_v3-4-0/

ElasticSearch 倒排索引(Inverted Index)| 什么是倒排索引?

篇幅较长,请看 https://blog.csdn.net/moshowgame/article/details/99413448

ElasticSearch之analyzer分词器+ICU分词插件

篇幅较长,请看 https://zhengkai.blog.csdn.net/article/details/99448661

Elasticsearch REST API

这里补充几个常用的API,以便大家对ES的REST API有简单的了解:

  1. curl -X PUT \ http://localhost:9200/blog ,PUT请求,后面的blog是要新建的index,也就是这个api相当于新建一个数据库
  2. curl -X POST \ http://localhost:9200/blog/article \ -d ' { "author": "zhengkai.blog.csdn.net", "createtime": 1563689639575, "id": 2, "text": "Elasticsearch是一个开源的分布式、高扩展、高实时的RESTful 搜索和分析引擎,基于Lucene......", "title": "SpringBoot整合ElasticSearch" }' ,POST请求,如果不存在叫article的type类型/table表,则新增,并插入记录。
  3. curl -X GET \ 'http://localhost:9200/article_info/doc/61969/_termvectors?fields=title' 格式为/${index}/${type}/${id}/_termvectors?fields=${fields_name} ,GET请求,查询分词结果。返回的JSON中term_vectors.field.terms里面就是分词结果。
  4. curl -X POST \http://localhost:9200/_search -d '{"query":{"bool":{"must":[{"wildcard":{"title.keyword":{"wildcard":"*SpringBoot*","boost":1}}}],"disable_coord":false,"adjust_pure_negative":true,"boost":1}}}' ,POST请求,进行搜索。

2019.7.23 补充。这个查询情况比较复杂,补充一些**【ElasticSearch分词和查询相关】**东西:

  • 情况一:{"query":{"wildcard": { "title": "*springboot*" }}} 可以查询到包含SpringBoot和springboot忽略大小写的标题,走分词,所以只能用小写查。而{"query":{"wildcard": { "title": "*SpringBoot*" }}}什么都查询不出来。。。
  • 情况二:{"query":{"wildcard": { "title.keyword": "*SpringBoot*" }}} 可以查询到包含SpringBoot的大小写必须一致的标题,不走分词。而{"query":{"wildcard": { "title.keyword": "*springboot*" }}} `什么都查询不出来。。。
  • 情况三:{"query":{"match": { "title": "bootelasticsearch" }}},可以查询出来,而且无论bootelasticsearch中什么字母是大写或者小写,忽略大小写。。。
  • 情况四:{"query":{"term": { "title": "bootelasticsearch" }}},可以查询出来,且必须小写字母才能查询出来,其他都不行。。。

以上情况很奇怪对吧,为什么呢,请看下文分解。。

  • bool(boolQuery)和must(boolQuery.must)的关系,就像sql的whereand一样,作为条件连接的桥梁来使用,多个条件的话,就使用多个must。
  • wildcard模糊查询,跟SQL的LIKE查询很类似,条件"*SpringBoot*"表示包含SpringBoot的文本。
  • wildcard的情况下,查询字段是text类型的话(es5以后没有string类型呢,分text和keyword,string数据put到es5中,默认是text。),直接title查询是走分词的,例如查询springboot,将会查询到Spring;用title.keyword的话这样可以不走分词,进行精准匹配。
  • es默认分词器为standard analyzer,大写字母全部转为了小写字母,并存入了倒排索引以供搜索。”SpringBootElasticSearch”会被分解成[bootelasticsearch]写入倒排索引(使用3的分词结果接口可以看到。坑啊!不是应[该spring,boot,elasticsearch]吗!!!)。 所以term query 查询的是倒排索引中确切的term,必须要匹配到大写的BootElasticSearch;而match query 会对filed进行分词操作,然后在查询,忽略大小写

布尔过滤器bool query

一个 bool 过滤器由三部分组成:

{
   "bool" : {
      "must" :     [],
      "should" :   [],
      "must_not" : [],
   }
}
  • must 所有的语句都 必须(must) 匹配,与 AND 等价。
  • must_not 所有的语句都 不能(must not) 匹配,与 NOT 等价。
  • should 至少有一个语句要匹配,与 OR 等价。

下面是一个简单明了的SQL->Elasticsearch QL

SELECT product
FROM   products
WHERE  (price = 20 OR productID = "MOSHOW IN ACTION-#fJ3")
  AND  (price != 30)

用es写就是:

{
   "query" : {
      "filtered" : { 
         "filter" : {
            "bool" : {
              "should" : [
                 { "term" : {"price" : 20}}, 
                 { "term" : {"productID" : "MOSHOW IN ACTION-#fJ3"}} 
              ],
              "must_not" : {
                 "term" : {"price" : 30} 
              }
           }
         }
      }
   }
}

匹配查询 match query

匹配查询 match 是个 核心 查询。无论需要查询什么字段, match 查询都应该会是首选的查询方式。 它是一个高级全文查询 ,这表示它既能处理全文字段,又能处理精确字段。

{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}

执行上面这个 match 查询的步骤是:

扫描二维码关注公众号,回复: 8726780 查看本文章
  1. 检查字段类型 。标题 title 字段是一个 string 类型( analyzed )已分析的全文字段,这意味着查询字符串本身也应该被分析。
  2. 分析查询字符串 。将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询(查询小写的quick)。
  3. 查找匹配文档 。用 term 查询在倒排索引中查找 quick 然后获取一组包含该项的文档,例如结果是文档包含“The quick brown fox”/“The quick brown fox jumps over the quick dog”/“The quick brown fox jumps over the lazy dog”,以上三个都会被查询出来。
  4. 为每个文档评分 。用 term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。

wildcard 通配符与regexp正则表达式查询

与 prefix 前缀查询的特性类似, wildcard 通配符查询也是一种底层基于词的查询, 与前缀查询不同的是它允许指定匹配的正则式。它使用标准的 shell 通配符查询: ? 匹配任意字符, * 匹配 0 或多个字符
这个查询会匹配例如包含 W1F 7HWW2F 8HW 的文档:

{
    "query": {
        "wildcard": {
            "postcode": "W?F*HW" 
        }
    }
}

这个查询会匹配邮政编码:

{
    "query": {
        "regexp": {
            "postcode": "W[0-9].+" 
        }
    }
}

查询精确值term query

term 查询对于查找单个值非常有用,但通常我们可能想搜索多个值。 如果我们想要查找价格字段值为 $20 或 $30 的文档该如何处理呢?

SELECT product
FROM   products
WHERE  (price = 20 OR price = 30)

不需要使用多个 term 查询,我们只要用单个 terms 查询(注意末尾的 s ), terms 查询好比是 term 查询的复数形式(以英语名词的单复数做比)。

它几乎与 term 的使用方式一模一样,与指定单个价格不同,我们只要将 term 字段的值改为数组即可。另外,与 term 查询一样,也需要将其置入 filter 语句的constant_score常量评分查询中使用:

{
    "query" : {
        "constant_score" : {
            "filter" : {
                "terms" : { 
                    "price" : [20, 30]
                }
            }
        }
    }
}

范围查询range query

range 查询可同时提供包含(inclusive)和不包含(exclusive)这两种范围表达式,可供组合的选项如下:

gt: > 大于(greater than)
lt: < 小于(less than)
gte: >= 大于或等于(greater than or equal to)
lte: <= 小于或等于(less than or equal to)
SELECT document
FROM   products
WHERE  price BETWEEN 20 AND 40
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lt"  : 40
                    }
                }
            }
        }
    }
}

项目实战


前面都是基础,下面才是开始Coding。

开源项目

项目源码已经上传到github,有需要可以参考或者下载使用。

Maven依赖

	<dependencies>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
    </dependencies>

Application配置

这里是application.yml的配置

server:
  port: 9999
  servlet:
      context-path: /es
tomcat:
    remote-ip-header: x-forward-for
    uri-encoding: UTF-8
    max-threads: 10
    background-processor-delay: 30
spring:
    http:
      encoding:
        force: true
        charset: UTF-8
    application:
        name: spring-cloud-study-security-elasticsearch
        author: zhengkai.blog.csdn.net
    data:
        elasticsearch:
          cluster-nodes: 127.0.0.1:9300
          cluster-name: elasticsearch

Entity实体

import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import lombok.Data;

@Data
@Document(indexName = "article_info", type = "doc")
public class Article {
	@Id
    private Integer id;

    private String title;

    private String text;

    private Date createtime;

    private String author;

}

ElasticsearchRepository

ArticleRepository 接口继承ElasticsearchRepository来完成基本的CRUD分页操作的,功能强大并且和普通JPA没有什么区别。保存的话直接用save就可以了,以下是search相关的方法:

  • Iterable search(QueryBuilder query);
  • Page search(QueryBuilder query, Pageable pageable);
  • Page search(SearchQuery searchQuery);
  • Page searchSimilar(T entity, String[] fields, Pageable pageable);
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/**
 * ElasticsearchRepository --> ElasticsearchCrudRepository --> PagingAndSortingRepository --> CrudRepository
 * @author zhengkai.blog.csdn.net
 */
@Repository
public interface ArticleRepository extends ElasticsearchRepository<Article, Integer> {

}

RestController

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.softdev.system.demo.entity.Article;
import com.softdev.system.demo.repository.ArticleRepository;
import com.softdev.system.demo.util.ApiReturnUtil;

import cn.hutool.core.util.RandomUtil;
/**
 *  ElasticSearch控制器
 * @author  zhengkai.blog.csdn.net
 * */
@RestController
public class ArticleController {

	@Autowired
	private ElasticsearchTemplate elasticsearchTemplate;
	
	@Autowired
	private ArticleRepository articleRepository;

	@GetMapping("new")
	public Object newArticle(@RequestParam(defaultValue = "SpringBootElasticSearch") String title, @RequestParam(defaultValue = "`Elasticsearch` 是一个`开源`的`分布式`、`高扩展`、`高实时`的RESTful `搜索和分析引擎`,基于`Lucene`......") String text){
		//构建并保存Article
		Article article = new Article();
		article.setId(RandomUtil.randomInt(10000,99999));
		article.setTitle(title);
		article.setText(text);
		article.setCreatetime(new Date());
		article.setAuthor("zhengkai.blog.csdn.net");
		return ApiReturnUtil.success(article);
	}

	@GetMapping("save")
	public Object save(@RequestParam(defaultValue = "SpringBootElasticSearch") String title, @RequestParam(defaultValue = "`Elasticsearch` 是一个`开源`的`分布式`、`高扩展`、`高实时`的RESTful `搜索和分析引擎`,基于`Lucene`......") String text){
		//构建并保存Article
		Article article = new Article();
		article.setId(RandomUtil.randomInt(10000,99999));
		article.setTitle(title);
		article.setText(text);
		article.setCreatetime(new Date());
		article.setAuthor("zhengkai.blog.csdn.net");
		articleRepository.save(article);
		return ApiReturnUtil.success(article);
	}

	/**
	 * ElasticSearch之Search封装查询
	 * @author  zhengkai.blog.csdn.net
	 * @param title   搜索标题
	 * @param pageable page = 第几页参数(第一页是0), value = 每页显示条数
	 */
	@GetMapping("search")
	public Object search(@RequestParam(defaultValue = "SpringBoot") String title, @PageableDefault(page = 0, value = 10) Pageable pageable){
		//以下查询等同于封装了{"query":{"bool":{"must":[{"wildcard":{"title.keyword":{"wildcard":"*SpringBoot*","boost":1}}}],"disable_coord":false,"adjust_pure_negative":true,"boost":1}}}
		//按标题进行模糊查询
		QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("title.keyword", "*SpringBoot*");
		//按照顺序构建builder,bool->must->wildcard ,有了上文的JSON,顺序就很好理解了
		BoolQueryBuilder must = QueryBuilders.boolQuery().must(queryBuilder);
		//封装pageable分页
		Page<Article> queryResult =  articleRepository.search(must,pageable);
		//返回
		return ApiReturnUtil.success(queryResult.getContent());
	}
	/**
	 * ElasticSearch之elasticsearchTemplate查询
	 * @author  zhengkai.blog.csdn.net
	 * @param title   搜索标题
	 */
	@GetMapping("originSearch")
	public Object originSearch(@RequestParam(defaultValue = "SpringBoot") String title) {
		BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
		QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("title.keyword", "*SpringBoot*");
		BoolQueryBuilder must = boolQuery.must(queryBuilder);
		NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
		NativeSearchQuery build = nativeSearchQueryBuilder.withQuery(must).build();
		List<Article> queryForList = elasticsearchTemplate.queryForList(build, Article.class);
		
		return ApiReturnUtil.success(queryForList);
	}
}

Result效果演示

  1. 连续调用6次左右,http://localhost:9999/es/save进行保存(如果你想用API保存,可以http://localhost:9999/es/new访问,得到一些JSON数据)
  2. 调用一次http://localhost:9999/es/save?title=ElasticSearch保存一条title不包含SpringBoot的数据(下文根据SpringBoot来检索)
  3. 打开浏览器ElasticSearch HeadDejavu UI插件进行查看(前提是安装好插件!!!否则请跳过。。。)
  • chrome-extension://ffmkiejjmecolpfloofpjologoblkegm/elasticsearch-head/index.html
  • chrome-extension://jopjeaiilkcibeohjdmejhoifenbnmlh/index.html?appname=article_info&url=http://localhost:9200&mode=edit在这里插入图片描述
  1. 在Header插件的Structured Query界面中选择 must+doc.title.keyword+wildcard+*SpringBoot*的查询条件,成功查询到数据。在这里插入图片描述
    5.访问http://localhost:9999/es/search
    在这里插入图片描述

或者用ES的RESTful API ,POST以下链接:

http://localhost:9200/_search
{“query”:{“bool”:{“must”:[{“wildcard”:{“title.keyword”:{“wildcard”:“SpringBoot”,“boost”:1}}}],“disable_coord”:false,“adjust_pure_negative”:true,“boost”:1}}}

es返回结果json解析:

  • took:整个搜索请求花费了多少毫秒
  • hits.total:本次搜索,返回了几条结果
  • hits.max_score:本次搜索的所有结果中,最大的相关度分数是多少,每一条document对于search的相关度,越相关,_score分数越大,排位越靠前
  • hits.hits:默认查询前10条数据,完整数据,_score降序排序
  • shards:shards fail的条件(primary和replica全部挂掉),不影响其他shard。默认情况下来说,一个搜索请求,会打到一个index的所有primary shard上去,当然了,每个primary shard都可能会有一个或多个replic shard,所以请求也可以到primary shard的其中一个replica shard上去。
  • timeout:默认无timeout,latency平衡completeness,手动指定timeout,timeout查询执行机制,格式:timeout=10ms,timeout=1s,timeout=1mGET /_search?timeout=10m
    在这里插入图片描述

杩滅▼涓绘満寮鸿揩鍏抽棴浜嗕竴涓幇鏈夌殑杩炴帴銆?

这个报错转换过来就是:远程主机强迫关闭了一个现有的连接

exception caught on transport layer [Netty4TcpChannel{localAddress=/127.0.0.1:9300, remoteAddress=/127.0.0.1:53494}], closing connection
java.io.IOException: 杩滅▼涓绘満寮鸿揩鍏抽棴浜嗕竴涓幇鏈夌殑杩炴帴銆?
        at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[?:?]
        at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43) ~[?:?]
        at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:276) ~[?:?]
        at sun.nio.ch.IOUtil.read(IOUtil.java:245) ~[?:?]
        at sun.nio.ch.IOUtil.read(IOUtil.java:223) ~[?:?]
        at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:358) ~[?:?]
        at io.netty.buffer.PooledHeapByteBuf.setBytes(PooledHeapByteBuf.java:261) ~[netty-buffer-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1132) ~[netty-buffer-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:347) ~[netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:148) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:656) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:556) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:510) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:470) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:909) [netty-common-4.1.32.Final.jar:4.1.32.Final]
        at java.lang.Thread.run(Thread.java:834) [?:?]

版本兼容问题

minimum_index_compatibility_version这里定义的最低版本会受spring-boot-starter-data-elasticsearch的版本影响。

所以目前推荐ES6,6.8.1左右的版本,比较靠谱。我一开始就是到官网下载最新版本,就被坑了。

如果你看到JAVA控制台有以下错误,基本就是版本兼容性问题了,直接访问localhost:9200获取版本信息。

org.elasticsearch.client.transport.NoNodeAvailableException: None of the configured nodes are available

推断错误的话。可以从一下日志查找思路,特别是看ES控制台输出的内容,可以清楚的看到

closing connection
java.lang.IllegalStateException: Received handshake message from unsupported version: [5.0.0] minimal compatible version is: [6.8.0]

{
  "name" : "WORKPC-MOSHOW",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "fDlrxuUcTjqgvfpWbl3Hcg",
  "version" : {
    "number" : "7.2.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "508c38a",
    "build_date" : "2019-06-20T15:54:18.811730Z",
    "build_snapshot" : false,
    "lucene_version" : "8.0.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
[/es] threw exception [Request processing failed; nested exception is NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{mQ1hg9-tT3mvRnfLIAaZHg}{127.0.0.1}{127.0.0.1:9300}]]] with root cause

org.elasticsearch.client.transport.NoNodeAvailableException: None of the configured nodes are available: [{#transport#-1}{mQ1hg9-tT3mvRnfLIAaZHg}{127.0.0.1}{127.0.0.1:9300}]
	at org.elasticsearch.client.transport.TransportClientNodesService.ensureNodesAreAvailable(TransportClientNodesService.java:347) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.elasticsearch.client.transport.TransportClientNodesService.execute(TransportClientNodesService.java:245) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.elasticsearch.client.transport.TransportProxyClient.execute(TransportProxyClient.java:59) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.elasticsearch.client.transport.TransportClient.doExecute(TransportClient.java:366) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:408) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.elasticsearch.action.ActionRequestBuilder.execute(ActionRequestBuilder.java:80) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.elasticsearch.action.ActionRequestBuilder.execute(ActionRequestBuilder.java:54) ~[elasticsearch-5.6.11.jar:5.6.11]
	at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.index(ElasticsearchTemplate.java:571) ~[spring-data-elasticsearch-3.0.10.RELEASE.jar:3.0.10.RELEASE]
	at org.springframework.data.elasticsearch.repository.support.AbstractElasticsearchRepository.save(AbstractElasticsearchRepository.java:156) ~[spring-data-elasticsearch-3.0.10.RELEASE.jar:3.0.10.RELEASE]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
	at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:641) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:590) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at com.sun.proxy.$Proxy88.save(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
	at org.zeroturnaround.javarebel.integration.util.ReloadingProxyFactory$ReloadingMethodHandler.invoke(SourceFile:74) ~[na:201907051008]
	at com.sun.proxy.$Proxy88.save(Unknown Source) ~[na:na]
	at com.softdev.system.demo.controller.ArticleController.save(ArticleController.java:38) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) ~[spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:41002) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135) [na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) [na:na]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.34.jar:8.5.34]
	at java.base/java.lang.Thread.run(Thread.java:844) [na:na]
[WARN ][o.e.t.TcpTransport       ] [WORKPC-MOSHOW] exception caught on transport layer [Netty4TcpChannel{localAddress=/127.0.0.1:9300, remoteAddress=/127.0.0.1:64883}], closing connection
java.lang.IllegalStateException: Received handshake message from unsupported version: [5.0.0] minimal compatible version is: [6.8.0]
        at org.elasticsearch.transport.InboundMessage.ensureVersionCompatibility(InboundMessage.java:137) ~[elasticsearch-7.2.0.jar:7.2.0]
        at org.elasticsearch.transport.InboundMessage.access$000(InboundMessage.java:39) ~[elasticsearch-7.2.0.jar:7.2.0]
        at org.elasticsearch.transport.InboundMessage$Reader.deserialize(InboundMessage.java:76) ~[elasticsearch-7.2.0.jar:7.2.0]
        at org.elasticsearch.transport.InboundHandler.messageReceived(InboundHandler.java:116) ~[elasticsearch-7.2.0.jar:7.2.0]
        at org.elasticsearch.transport.InboundHandler.inboundMessage(InboundHandler.java:105) ~[elasticsearch-7.2.0.jar:7.2.0]
        at org.elasticsearch.transport.TcpTransport.inboundMessage(TcpTransport.java:660) [elasticsearch-7.2.0.jar:7.2.0]
        at org.elasticsearch.transport.netty4.Netty4MessageChannelHandler.channelRead(Netty4MessageChannelHandler.java:62) [transport-netty4-client-7.2.0.jar:7.2.0]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:323) [netty-codec-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:297) [netty-codec-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:241) [netty-handler-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:682) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:582) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:536) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496) [netty-transport-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906) [netty-common-4.1.35.Final.jar:4.1.35.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-common-4.1.35.Final.jar:4.1.35.Final]
        at java.lang.Thread.run(Thread.java:834) [?:?]

补充

2019.8.13补充。es返回结果json解析。
2019.8.12补充。bool query/range query/match query/term query/wildcard&regex query等几个关键查询,更多详情请看官方文档 全文搜索|中文Query DSL|英文

2019.7.23 补充。这个查询情况比较复杂,补充一些**【ElasticSearch分词和查询相关】**东西。

发布了293 篇原创文章 · 获赞 401 · 访问量 111万+

猜你喜欢

转载自blog.csdn.net/moshowgame/article/details/96768494