ElasticSearch 入门总结

ElasticSearch 入门

本篇为 ElasticSearch 入门学习总结笔记,课程视频地址:ElasticSearch 入门

一、ElasticSearch 简介

1.1、什么是ElasticSearch

​ 简称 ES

  • 基于Apache Lucene 构建的 开源搜索引擎,提供一个分布式多用户能力的全文搜索引擎
  • 采用 JAVA 编写的 ,提供简单易用的 RESTFul API(RESTFul web接口),当前流行的企业级搜索引擎
  • 轻松的 横向扩展,可支持PB级 的结构化或非结构化数据处理
  • 可以准实时地快速存储、搜索、分析海量的数据。(用于云计算中,能够达到实时搜索)

1.2、应用场景

  • 海量数据分析引擎(聚合搜索)
  • 站内搜索引擎
  • 数据仓库

1.3、谁在用ES呢?

英国卫报— 实时分析公众对文章的回应

维基百科、GitHub - 站内实时搜索

百度—实时日志监控平台

阿里、Google、小米、京东....

1.4、环境要求

工具 版本
IDE IDEA或Eclipse
JAVA JDK8
Maven 3.x 以上
NodeJS 6.0以上

二、ElasticSearch 安装

2.1、ES版本问题

  • 版本历史 : 1.x-》2.x-》5.x -》最新6.0
  • 版本选择 : 选择6.0进行演练!

2.2、ES安装-单实例

下载地址:ES Downloads,windows环境下载 ZIP sha ,Linux或者Mac环境下,下载TAR sha

windows 下安装elasticsearch(有访问页面)

ElasticSearch安装部署(Windows)(添加到windows)

2.2.1 单实例的安装(Windows)

1、下载ES zip包

2、安装Jdk8,并查看环境变量配置

3、解压ES包

  • 目录说明:
目录名 说明
config 配置文件
modules 模块存放目录
bin 脚本
lib 第三方库
plugins 第三方插件

4、启动验证服务

​ windows环境在/bin 下载双击 elasticsearch.bat 。启动成功,看到starting....最后是started!

[2018-08-18T22:18:41,578][INFO ][o.e.n.Node               ] [-Jy0w6-] starting ...
[2018-08-18T22:18:44,044][INFO ][o.e.t.TransportService   ] [-Jy0w6-] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}, {[::1]:9300}
{ml.machine_memory=8441524224, xpack.installed=true, ml.max_open_jobs=20, ml.enabled=true}, reason: apply cluster state (from master [master {-Jy0w6-}{-Jy0w6-QTKquaudbtTAQMQ}
[2018-08-18T22:18:48,923][INFO ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [-Jy0w6-] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[::1]:9200}
[2018-08-18T22:18:48,924][INFO ][o.e.n.Node               ] [-Jy0w6-] started

​ 然后访问 http://localhost:9200/ ,页面响应json格式数据,服务启动正常!

访问ElasticSeach

2.2.2 实用插件Head安装(Windows)

ES 默认返回的信息在页面上是Json格式显示!不太友好!

Head插件的优点

  • 提供了友好的web界面,解决数据在界面显示问题
  • 实现基本信息的查看和Restful 请求的模拟以及数据的基本检索

1、安装Head的步骤

ElasticSearch-5.0安装head插件

ES6.0版本安装head插件

​ 1)、安装Node 6.x以上J环境,安装 执行指令npm install -g grunt -cli进行安装grunt。

​ 2)、 下载elasticsearch-head包,在Github【elasticsearch-head】上面,直接选择下载的DownLoad zip到本地!

​ 3)、保存在 F:\elasticsearch_learn\ 文件夹下面并对 elasticsearch-head 进行解压!

​ 4)、在 F:\elasticsearch_learn\elasticsearch-head-master文件夹目录下面,使用 npm install !

F:\elasticsearch_learn\elasticsearch-head-master>npm install

注意:第一次==执行失败了!==报了下面的错误(可能是网络不好的原因,网络好的时候在尝试执行一次),但是服务启动没有问题!

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 
npm ERR! node v6.10.3
npm ERR! npm  v3.10.10
npm ERR! code ELIFECYCLE
npm ERR! phantomjs-prebuilt@2.1.16 install: `node install.js`
npm ERR! Exit status 1
....
npm ERR! Please include the following file with any support request:
npm ERR!     F:\elasticsearch_learn\elasticsearch-head-master\npm-debug.log

​ 5)、doc窗口 在 F:\elasticsearch_learn\elasticsearch-head-master文件夹目录下面执行

 # npm run start  或者  grunt server 
 F:\elasticsearch_learn\elasticsearch-head-master>npm run start

​ 6)、验证服务,默认访问地址http://localhost:9100,如下图显示:

启动elasticsearch-head并验证

2、修改相应的配置

ElasticSearch和 elasticsearch-head 本质上是两个服务进程,访问存在跨域问题,需要进行一些设置。

在 elasticsearch安装目录 /config/elasticsearch.yml ,在这个配置文件的末尾追加下面两行配置信息并保存!

http.cors.enabled: true
http.cors.allow-origin: "*"
属性 缺省值 描述
http.cors.enabled true 如果启用了 HTTP 端口,那么此属性会指定是否允许跨源 REST 请求。
http.cors.allowed.origin localhost 如果 http.cors.enabled 的值为 true,那么该属性会指定允许 REST 请求来自何处。

更多属性配置,请查看 【Elasticsearch 服务配置属性

3、验证elasticsearch和 elasticsearch-head

  • 启动 elasticsearch服务
  • 启动elasticsearch-head 服务

访问 http://localhost:9100 地址!查看elasticsearch 集群健康值是否正常!正常如下图:

==注: 集群健康值==

red(差):集群 健康状况很差,虽然可以查询,但是已经出现了丢失数据的现象 ;

yellow(中):集群健康状况不是很好,但是集群可以正常使用 ;

green(优):集群健康状况良好,集群正常使用。

验证elasticsearch和 elasticsearch-head组合

2.3、ES安装-分布式安装

1)、安装说明,安装三个节点,一个master ,两个slave。

集群名称 ip-端口
myEsCluster (master) 127.0.0.1:9500
myEsCluster(slave) 127.0.0.1:9600
myEsCluster(slave) 127.0.0.1:9700

2)、Es安装包解压出三份ES,修改每个 elasticsearch安装目录 /config/elasticsearch.yml 这个配置文件

master配置说明:

# 设置支持elasticsearch-head
http.cors.enabled: true
http.cors.allow-origin: "*"

# 设置集群master配置信息
# 首先要指定 集群的名字 ,名字随便起,符合常规命名规则
cluster.name: myEsCluster
# 节点的名字,一般为master 或者 slave
node.name: master
# 节点是否为master,设置为true的话,说明此节点为master节点
node.master: true
# 设置网络,如果是本机的话就是127.0.0.1,其他服务器配置对应的IP地址即可
network.host: 127.0.0.1
# 默认端口为 9200,可以修改默认设置
http.port: 9500

slave配置说明:

# 设置集群slave配置信息
# 首先要指定 集群的名字 ,名字随便起,符合常规命名规则
cluster.name: myEsCluster
# 节点的名字,一般为master 或者 slave
node.name: slave1
# 节点是否为master,设置为true的话,说明此节点为master节点
node.master: false
# 默认端口为 9200,可以修改默认设置
http.port: 9600
# 设置网络,如果是本机的话就是127.0.0.1,其他服务器配置对应的IP地址即可
network.host: 127.0.0.1
# 设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点。可以是一个数组。
# #discovery.zen.ping.unicast.hosts: ["host1", "host2"]
discovery.zen.ping.unicast.hosts: ["127.0.0.1"]

==两个slave配置只需要改相应的端口号即可!一个slave1:9600 ,一个slave2:9700。==

更多配置参考

3)、配置后完成后,启动master的,然后启动Slave,也启动elasticsearch-head服务,此时页面可以查看Es集群的状态!如下图所示:

Es集群

2.4、 总结

从上面的安装来看,Es的安装相对还是比较简单,并且在安装分布式的时候,通过简单的配置就可以快速扩展多个ES节点,能够快速的进行扩容,非常灵活,方便!

三、ElasticSearch 基础概念

3.1、基本概念

集群和节点

集群:是由一个或者多个ES节点组成的集合。 每一个集群都有一个唯一的名字,默认是ElasticSearch。每个节点也都有唯一名字。

集群和节点

索引

索引: 含有相同属性的文档集合,比如产品的索引,用户的索引等

类型:索引可以定义一个或多个类型,文档必须属于一个类型(一般会定义有相同字段的文档为一个类型)

文档:文档是可以被所有的基本数据单位,是整个ES中最小的存储单位

==索引在Es中是通过一个名字来识别的,而且它的名字必须是 英文字母小写,且不能有中划线。==

都是通过这个名字来进行增删查改。

ES 数据库
索引 实例database
类型 表-table
文档 一行记录

举例:图书索引 ---> 文学类型、技能类型...---->每一本书可以看成一个文档

分片和备份

分片: 每个索引都有多个分片,每个分片是一个Lucene索引

​ 分片的好处:假如数据量很大的话,就会造成硬盘的压力很大,同时搜索速度也会出现瓶颈,将索引分成多个分片,从而分摊压力提供效率。

备份:拷贝一份分片就完成了分片的备份,提供可用性

ES默认在创建索引的时候,会创建5个分片,1个备份。这个默认的数量是可以修改的。

​ number_of_shards: 每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。

​ number_of_replicas:每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。

==注意==:索引的分片只能在创建索引的时候指定,而不能再后期进行修改。备份是可以动态修改的。

四、ElasticSearch 基本用法

ES是以Restful API的风格来命名的。

  • API 的基本格式

    http://<ip>:<port>/<索引>/<类型>/<文档id>

  • 常用HTTP动词

    GET/PUT/POST/DELETE

4.1、索引的创建

问题:如何确定一个索引是非结构化还是结构的化的呢?

结合Head插件,索引-->索引信息,查看JSON数据中mappings,如果数据为空,则说明是非结构化索引。

非结构化创建

使用Head插件进行索引的创建,在tab标签上,选择索引,点 新建索引,输入索引的名称,注意名称的命名规则。(我自己验证,中文、有中划线的索引名字都可以创建成功!但是字母必须是小写!)

创建成功每个框框,都是Es索引的分片,粗线框是主分片 和 细线框 是分片的备份。

索引的创建-1

结构化创建

1)、使用Head插件进行索引的创建,在tab标签上,选择复合查询,进行如下配置:

创建结构化索引

2)、或者使用PostMan工具进行索引的创建

说明:

在Elasticsearch 6.0.0或更高版本中创建的索引只包含一个mapping type。 在5.x中使用multiple mapping types创建的索引将继续像以前一样在Elasticsearch 6.x中运行。 Mapping types将在Elasticsearch 7.0.0中完全删除。

Indices created in Elasticsearch 6.0.0 or later may only contain a single mapping type. Indices created in 5.x with multiple mapping types will continue to function as before in Elasticsearch 6.x. Mapping types will be completely removed in Elasticsearch 7.0.0.

PUT 127.0.0.1:9500/people

请求参数:

{
    "settings":{
        "number_of_shards":3,
        "number_of_replicas":1
    },
    "mappings":{
        "man":{
            "properties":{
                "name":{
                    "type":"text"
                },
                "age":{
                    "type":"integer"
                },
                "birth":{
                    "type":"date",
                    "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
                },
                "country":{
                    "type":"keyword"
                }
            }
        },
        "woman":{}
    
    }
}
image

结果:

{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "people"
}

如果一个mappings中有两个type的话,在提交的时候会报如下错误:

PUT 127.0.0.1:9500/people2

参数:

{
    "settings":{
        "number_of_shards":3,
        "number_of_replicas":1
    },
    "mappings":{
        "man":{
            "properties":{
                "name":{
                    "type":"text"
                },
                "age":{
                    "type":"integer"
                },
                "birth":{
                    "type":"date",
                    "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
                },
                "country":{
                    "type":"keyword"
                }
            }
        },
        "woman":{}
    
    }
}

结果:

{
    "error": {
        "root_cause": [
            {
                "type": "illegal_argument_exception",
                "reason": "Rejecting mapping update to [people2] as the final mapping would have more than 1 type: [woman, man]"
            }
        ],
        "type": "illegal_argument_exception",
        "reason": "Rejecting mapping update to [people2] as the final mapping would have more than 1 type: [woman, man]"
    },
    "status": 400
}

4.2、插入

文档ID是一个唯一索引值,指向我们的文档数据。

指定文档ID插入

PUT 127.0.0.1:9500/people/man/1

指定插入文档的ID为 1.

参数:

{
    "name":"dufy",
    "age":27,
    "birth":"1992-09-28",
    "country":"China"
}

请求结果:

{
    "_index": "people",
    "_type": "man",
    "_id": "1", # id 为1 
    "_version": 1,
    "result": "created", # 标识创建一个文档
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

使用Head插件查看数据:

使用ID插入文档

自动产生文档ID插入

自动产生ID和上面指定ID插入不同的两个地方是:

(1)、在请求地址上不需要指定 ID,让ES自动帮我们创建ID!

(2)、请求方式为POST方式

POST 127.0.0.1:9500/people/man

请求参数:

{
    "name":"dufy_auto_id",
    "age":37,
    "birth":"1982-09-28",
    "country":"China"
}

请求结果:

{
    "_index": "people",
    "_type": "man",
    "_id": "zmLSUGUBWifqa4luPRp5", # 自动创建ID
    "_version": 1,
    "result": "created", # 标识创建一个文档
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

使用Head插件查看数据:

自动生成文档ID

4.3 、修改

直接修改文档

POST 127.0.0.1:9500/people/man/1/_update

注意:需要在请求的地址后面加上 _update,标识是一个修改操作。

请求参数:

{
    "doc":{
        "name":"dufy_update" 
    }
}

请求响应结果:

{
    "_index": "people",
    "_type": "man",
    "_id": "1", # 标识文档的ID为1
    "_version": 2,
    "result": "updated", # 标识修改一个文档
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "_seq_no": 2,
    "_primary_term": 1
}

通过Head插件查看,发现文档ID为1 name已经修改了。

脚本修改文档

脚本修改和直接修改的API是一样的,只是参数有些区别。

POST 127.0.0.1:9500/people/man/1/_update

第一种方式请求参数:

{
    "script":{
        "lang":"painless" , # ES 脚本语言
        "inline":"ctx._source.age += 10" #ctx:es上下文,_source:代表es当前的文档,age:字段
    }
}

第二种方式请求参数:

{
    "script":{
        "lang":"painless" ,
        "inline":"ctx._source.age = params.age",
        "params":{  #使用params 指定数据值
            "age":80
        }
    }
}

4.4、删除

删除文档

DELETE 127.0.0.1:9500/people/man/1

使用delete请求,通过文档的ID进行删除操作。

{
    "_index": "people",
    "_type": "man",
    "_id": "1",
    "_version": 5,
    "result": "deleted", # 标识删除一个文档
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "_seq_no": 5,
    "_primary_term": 1
}

删除索引

删除操作是一个比较危险的操作,因为索引一旦删除,对应的所有数据全部被删除了。

  • 可以通过Head插件删除。确认输入删除,索引就删除了。

  • 使用 PostMan进行删除,使用 请求,只输入请求的索引

    127.0.0.1:9500/people

4.5、查询-重点

查询功能是使用过操作比较频繁的。

首先进行数据初始化,创建一个book 的索引,然后插入一些数据。具体的创建索引和插入数据,查看之前内容。

PUT 127.0.0.1:9500/book

结构化创建:

{
    "mappings":{
        "novel":{
            "properties":{
                "author":{
                    "type":"text"
                },
                "word_count":{
                    "type":"integer"
                },
                "publish_date":{
                    "type":"date",
                    "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
                },
                "title":{
                    "type":"keyword"
                }
            }
        }
    }
}

插入文档数据:

POST 127.0.0.1:9500/book/novel/1

{
    "title":"Nginx",
    "word_count":5000,
    "publish_date":"2992-09-28",
    "author":"zhangyc"
}
book索引初始化数据

简单查询

GET 127.0.0.1:9500/book/novel/4

{
    "_index": "book",
    "_type": "novel",
    "_id": "4",
    "_version": 1,
    "found": true,
    "_source": {  # 查询出来索引为book,类型是novel的ID=4 的文档数据
        "title": "Dubbo",
        "word_count": 8000,
        "publish_date": "1992-09-28",
        "author": "alibb"
    }
}

条件查询

POST 127.0.0.1:9500/book/_search

  • 查询全部数据
{
    "query":{ # 查询是以query为关键词的
        "match_all":{} # 查询全部的数据
    }
}

返回结果:

{
    "took": 8, # 接口响应花费的时间/ms
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": { 
        "total": 5,  # 总共数据5条
        "max_score": 1,
        "hits": [ # 击中,也就是响应的全部数据,hits默认返回的数据为10条,可以修改!
            {
                "_index": "book",
                "_type": "novel",
                "_id": "5",
                "_score": 1,
                "_source": {
                    "title": "Nginx",
                    "word_count": 5000,
                    "publish_date": "2992-09-28",
                    "author": "zhangyc"
                }
            },
            {....} #其他的数据信息 
            }
        ]
    }
}

修改 hits默认返回数据的总数。

{
    "query":{ # 查询是以query为关键词的
        "match_all":{} # 查询全部的数据
    },
    "size":2 # 返回数据的总数
}
  • 关键词查询

    POST 127.0.0.1:9500/book/_search

{
    "query":{ 
        "match":{
            "title": "Redis实战"
        },
        # 默认 排序 使用 _score 
        "sort":[ # 如何查询数据较多,可以进行排序操作
            {"publish_date":{"order":"desc"}}
        ]
    }
}
{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "book",
                "_type": "novel",
                "_id": "3",
                "_score": 0.2876821,
                "_source": {
                    "title": "Redis实战",
                    "word_count": 2000,
                    "publish_date": "1582-09-28",
                    "author": "marry"
                }
            }
        ]
    }
}

聚合查询

POST 127.0.0.1:9500/book/_search

  • 对书籍的字数进行聚合

查询参数:

{
    "aggs":{
        "groud_word_count":{ # 自定义聚合名称
            "terms":{
                "field":"word_count"
            }
        }
    }
}

返回结果:

{
    # 其他数据省略
    "aggregations": {
        "groud_word_count": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": 5000,
                    "doc_count": 2 # 文档总数
                },
                {
                    "key": 2000,
                    "doc_count": 1
                },
                {
                    "key": 8000,
                    "doc_count": 1
                },
                {
                    "key": 10000,
                    "doc_count": 1
                }
            ]
        }
    }
}

上面这个是单个聚合,多个聚合同理:

{
    "aggs":{
        "groud_word_count":{ # 自定义聚合名称
            "terms":{
                "field":"word_count"
            }
        },
        "groud_title":{ # 自定义聚合名称
            "terms":{
                "field":"title"
            }
        }
    }
}
  • 进行函数计算
{
    "aggs":{
        "grades_word_count":{
            "stats":{  #统计数据 min 、max、avg、avg
                "field":"word_count"
            }
        }
    }
}

返回结果:

"aggregations": { # 聚合
        "grades_word_count": {
            "count": 5,
            "min": 2000,
            "max": 10000,
            "avg": 6000,
            "sum": 30000
        }
    }

五、ElasticSearch 高级查询

Elasticsearch Reference 6

子条件查询

特定字段查询 所指特定值

1、Query Context

在查询过程中,除了判断文档是否满足查询条件外,ES还会计算一个_score来标识匹配的程度,旨在判断目标文档和查询条件匹配的有多好(匹配度)!

  • 全文本查询 —— 针对文本类型数据
  • 字段级别查询——针对结构化数据,如数字、日期等
{
    "query":{
        "multi_match":{ # 多条件查询
            "query":"Redis",
            "fields":["title","author"]
        }
    }
    
}

语法查询:

{
    "query":{
        "query_string":{
            "query":"入门 OR Redis",
            "fields":["title","author"]
        }
    }
    
}

字段级别的查询:

{
    "query":{
        "term":{
            "word_count":5000
        }
    }
    
}

字数或者日期范围:

{
    "query":{
        "range":{
            "word_count":{
                "gte":1000,
                "lte":5000
            }
        }
    }
    
}
2、Filter Context
{
    "query":{
        "bool":{
            "filter":{
                "term":{
                    "word_count":5000
                }
            }
        }
    }
    
}

复合条件查询

以一定的逻辑组合子条件查询,结合了Query和Filter Context

  • 固定分数查询

  • 布尔查询

六、ElasticSearch 实战演练

1、配置Spring boot ElasticSearch开发环境

1、创建Spring boot web项目,spring boot 使用2.0.4版本, elasticsearch为5.6.10

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

 <!--引入ELasticSearch的依赖包,默认使用elasticsearch5.6.10版本-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
 <!--需要引入transport-netty3-client,否则会启动报错-->
        <dependency>
            <groupId>org.elasticsearch.plugin</groupId>
            <artifactId>transport-netty3-client</artifactId>
            <version>5.6.10</version>
        </dependency>

2、配置代码

/**
 * ES 配置类
 * @author:dufyun
 * @version:1.0.0
 * @date 2018/8/19
 * @update:[日期YYYY-MM-DD] [更改人姓名][变更描述]
 */
@Configuration
public class ElasticSearchConfig {

    private static final Logger logger = LoggerFactory.getLogger(ElasticSearchConfig.class);

    @Bean
    public TransportClient transportClient() {
        logger.info("初始化开始。。。。。");
        TransportClient client = null;
        try {
            TransportAddress transportAddress = new InetSocketTransportAddress(InetAddress.getByName("localhost"),
                    Integer.valueOf(9300));

            // 配置信息
            Settings esSetting = Settings.builder()
                    .put("cluster.name","myEsCluster")
                    .build();

            //配置信息Settings自定义,下面设置为EMPTY
            client = new PreBuiltTransportClient(esSetting);

            client.addTransportAddresses(transportAddress);


        } catch (Exception e) {
            logger.error("elasticsearch TransportClient create error!!!", e);
        }

        return client;
    }
}

也可以使用配置文件!

es.hostName=localhost
es.transport=9300
es.cluster.name=myEsCluster
@Configuration
@PropertySource(value={"classpath:elasticsearch.properties"}) // 加载 指定的配置,只支持properti
public class ElasticSearchConfig {

    private static final Logger logger = LoggerFactory.getLogger(ElasticSearchConfig.class);

    @Value("${es.hostName}")
    private String hostName;

    @Value("${es.transport}")
    private Integer transport;

    @Value("${es.cluster.name}")
    private String clusterName;

    @Bean
    public TransportClient transportClient() {
        LOGGER.info("初始化开始。。。。。");
        TransportClient client = null;
        try {
            TransportAddress transportAddress = new InetSocketTransportAddress(InetAddress.getByName(hostName),
                    Integer.valueOf(transport));

            // 配置信息
            Settings esSetting = Settings.builder()
                    .put("cluster.name",clusterName)
                    .build();

            //配置信息Settings自定义,下面设置为EMPTY
            client = new PreBuiltTransportClient(esSetting);

            client.addTransportAddresses(transportAddress);


        } catch (Exception e) {
            logger.error("elasticsearch TransportClient create error!!!", e);
        }

        return client;
    }
}

3、使用单元测试进行验证

@Autowired
    private TransportClient client;

    @Test
    public void contextLoads() {

        System.out.println(client);
       //org.elasticsearch.transport.client.PreBuiltTransportClient@6c9b44bf
    }

2、接口开发

1、新增图书信息功能开发

2、修改图书信息功能开发

3、删除功能开发

4、综合查询接口开发

代码如下:

/**
 * ES book索引 控制器
 * @author:dufyun
 * @version:1.0.0
 * @date 2018/8/19
 * @update:[日期YYYY-MM-DD] [更改人姓名][变更描述]
 */
@Controller
public class BookController {

    @Autowired
    private TransportClient client;

    /**
     * http://localhost:8080/get/book/novel?id=2
     * 获取Es中数据
     * @param id
     * @return
     */
    @GetMapping(value = "/get/book/novel")
    @ResponseBody
    public ResponseEntity getBook(@RequestParam("id") String id) {
        if (StringUtils.isEmpty(id)) {
            return new ResponseEntity("ID不能为空!", HttpStatus.NOT_FOUND);
        }
        GetResponse result = client.prepareGet("book", "novel", id).get();

        if (!result.isExists()) {
            return new ResponseEntity("资源没有找到!", HttpStatus.NOT_FOUND);
        }

        return new ResponseEntity(result.getSource(), HttpStatus.OK);

    }

    /**
     * 添加文档数据
     *  http://localhost:8080/add/book/novel
     *  form-data
     * @param title
     * @param author
     * @param word_count
     * @param publish_date
     * @return
     */
    @PostMapping(value = "/add/book/novel")
    @ResponseBody
    public ResponseEntity addBook(@RequestParam("title") String title, @RequestParam("author") String author,
            @RequestParam("word_count") Integer word_count,
            @RequestParam("publish_date") String publish_date) {

        try {
            /**
             * 要加 startObject,否则出现 Can not write a field name, expecting a value 错误!
             */
            XContentBuilder builder = XContentFactory.jsonBuilder().startObject().field("title", title)
                    .field("author", author).field("word_count", word_count)
                    .field("publish_date", publish_date).endObject();

            IndexResponse indexResponse = client.prepareIndex("book", "novel").setSource(builder).get();

            return new ResponseEntity(indexResponse.getId(), HttpStatus.INTERNAL_SERVER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            new ResponseEntity("内部服务错误!", HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity("插入出错!", HttpStatus.INTERNAL_SERVER_ERROR);

    }


    /**
     * 删除Es中数据
     * localhost:8080/del/book/novel?id=sdfhUWUB1-6vN1-K8u5f
     * @param id
     * @return
     */
    @DeleteMapping(value = "/del/book/novel")
    @ResponseBody
    public ResponseEntity delBook(@RequestParam("id") String id) {
        if (StringUtils.isEmpty(id)) {
            return new ResponseEntity("ID不能为空!", HttpStatus.NOT_FOUND);
        }
        DeleteResponse result = client.prepareDelete("book", "novel", id).get();

        return new ResponseEntity(result.toString(), HttpStatus.OK);

    }

    /**
     * 更新文档
     *
     * @param id
     * @param title
     * @param author
     * @param word_count
     * @param publish_date
     * @return
     */
    @PutMapping(value = "/update/book/novel")
    @ResponseBody
    public ResponseEntity updateBook(@RequestParam("id") String id,
            @RequestParam(value = "title",required = false) String title,
            @RequestParam(value = "author",required = false) String author,
            @RequestParam(value = "word_count",required = false) Integer word_count,
            @RequestParam(value = "publish_date",required = false) String publish_date) {
        if (StringUtils.isEmpty(id)) {
            return new ResponseEntity("ID不能为空!", HttpStatus.NOT_FOUND);
        }
        UpdateRequest updateRequest = new UpdateRequest("book","novel",id);

        try {
            XContentBuilder builder = XContentFactory.jsonBuilder().startObject();
            if (!StringUtils.isEmpty(title)){
                builder.field("title", title);
            }
            if (!StringUtils.isEmpty(author)){
                builder.field("author", author);
            }
            if (!StringUtils.isEmpty(word_count)){
                builder.field("word_count", word_count);
            }
            if (!StringUtils.isEmpty(publish_date)){
                builder.field("publish_date", publish_date);
            }

            builder.endObject();

            updateRequest.doc(builder);

            UpdateResponse updateResponse = client.update(updateRequest).get();

            return new ResponseEntity(updateResponse.getId(), HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity("内部错误!", HttpStatus.INTERNAL_SERVER_ERROR);
        }

    }

    /**
     * localhost:8080/query/book/novel
     * 复合查询接口
     * @param title
     * @param author
     * @param gt_word_conut
     * @param lt_word_conut
     * @return
     */
    @PostMapping(value = "/query/book/novel")
    @ResponseBody
    public ResponseEntity queryBook(
            @RequestParam(value = "title",required = false) String title,
            @RequestParam(value = "author",required = false) String author,
            @RequestParam(value = "gt_word_conut",defaultValue = "0") int gt_word_conut,
            @RequestParam(value = "lt_word_conut",required = false) Integer lt_word_conut) {


        BoolQueryBuilder boolQuy = QueryBuilders.boolQuery();

        if(!StringUtils.isEmpty(title)){
            boolQuy.must(QueryBuilders.matchQuery("title", title));
        }
        if(!StringUtils.isEmpty(author)){
            boolQuy.must(QueryBuilders.matchQuery("author", author));
        }

        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("word_count").from(gt_word_conut);

        if(lt_word_conut != null && gt_word_conut >0){
            rangeQueryBuilder.to(lt_word_conut);
        }
        boolQuy.filter(rangeQueryBuilder);

        SearchRequestBuilder searchRequestBuilder = client.prepareSearch("book")
                .setTypes("novel")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQuy)
                .setFrom(0)
                .setSize(10);
        SearchResponse searchResponse = searchRequestBuilder.get();
        List<Map<String, Object>> lists = new ArrayList<>();
        for (SearchHit hit:searchResponse.getHits().getHits()) {
            lists.add(hit.getSource());
        }

        return new ResponseEntity(lists,HttpStatus.OK);
    }

}

七、ElasticSearch 总结

  • ES简介——使用场景(重要性),提高竞争力
  • 安装ES-单机-分布式
  • 核心基础概念
  • 基本使用方法 ——增删查改
  • 高级查询语法——重点和难点
  • 实战演练
ES课程总结

实战演练源码

springboot-elasticsearch


如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到,谢谢!

如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!


欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

© 每天都在变得更好的阿飞

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • Elastic+logstash+head简单介绍 一. 概述 ElasticSearch是一个基于Lucene的...
    柒月失凄阅读 4,152评论 0 4
  • ElasticSearch优化系列一:集群节点规划 - 简书http://www.jianshu.com/p/4c...
    葡萄喃喃呓语阅读 7,116评论 1 19
  • 简介 Elasticsearch是一个高可扩展的开源全文搜索和分析引擎,它允许存储、搜索和分析大量的数据,并且这个...
    零度沸腾_yjz阅读 5,488评论 0 8
  • 我想要把秘密说给风听,让它吹过你的耳边,用你听不懂的语言,跟你讲述世界的奇怪。 我想要把故事讲给树听,让它们沙沙私...
    扶木生阅读 128评论 0 0
  • 听一个结婚的男同事提过一件事:每次他开车回家,到了楼下还会在车里坐很久。 我原来不是很理解,但是偶然看到知乎上...
    随心远行_67ad阅读 120评论 0 0