本篇为 ElasticSearch 入门学习总结笔记,课程视频地址:ElasticSearch 入门
简称 ES
英国卫报— 实时分析公众对文章的回应
维基百科、GitHub - 站内实时搜索
百度—实时日志监控平台
阿里、Google、小米、京东…
工具 | 版本 |
---|---|
IDE | IDEA或Eclipse |
JAVA | JDK8 |
Maven | 3.x 以上 |
NodeJS | 6.0以上 |
下载地址:ES Downloads,windows环境下载 ZIP sha
,Linux或者Mac环境下,下载TAR sha
。
windows 下安装elasticsearch(有访问页面)
ElasticSearch安装部署(Windows)(添加到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格式数据,服务启动正常!
ES 默认返回的信息在页面上是Json格式显示!不太友好!
Head插件的优点:
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](https://github.com/mobz/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,如下图显示:
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
访问 http://localhost:9100 地址!查看elasticsearch 集群健康值是否正常!正常如下图:
注: 集群健康值
red(差):集群 健康状况很差,虽然可以查询,但是已经出现了丢失数据的现象 ;
yellow(中):集群健康状况不是很好,但是集群可以正常使用 ;
green(优):集群健康状况良好,集群正常使用。
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的安装相对还是比较简单,并且在安装分布式的时候,通过简单的配置就可以快速扩展多个ES节点,能够快速的进行扩容,非常灵活,方便!
集群:是由一个或者多个ES节点组成的集合。 每一个集群都有一个唯一的名字,默认是ElasticSearch。每个节点也都有唯一名字。
索引: 含有相同属性的文档集合,比如产品的索引,用户的索引等
类型:索引可以定义一个或多个类型,文档必须属于一个类型(一般会定义有相同字段的文档为一个类型)
文档:文档是可以被所有的基本数据单位,是整个ES中最小的存储单位
索引在Es中是通过一个名字来识别的,而且它的名字必须是 英文字母小写,且不能有中划线。
都是通过这个名字来进行增删查改。
ES | 数据库 |
---|---|
索引 | 实例database |
类型 | 表-table |
文档(字段) | 一行记录 |
举例:图书索引 —> 文学类型、技能类型…---->每一本书可以看成一个文档
分片: 每个索引都有多个分片,每个分片是一个Lucene索引
分片的好处:假如数据量很大的话,就会造成硬盘的压力很大,同时搜索速度也会出现瓶颈,将索引分成多个分片,从而分摊压力提供效率。
备份:拷贝一份分片就完成了分片的备份,提供可用性
ES默认在创建索引的时候,会创建5个分片,1个备份。这个默认的数量是可以修改的。
number_of_shards: 每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。
number_of_replicas:每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。
注意:索引的分片只能在创建索引的时候指定,而不能再后期进行修改。备份是可以动态修改的。
ES是以Restful API的风格来命名的。
API 的基本格式
http://:/<索引>/<类型>/<文档id>
常用HTTP动词
GET/PUT/POST/DELETE
问题:如何确定一个索引是非结构化还是结构的化的呢?
结合Head插件,索引–>索引信息,查看JSON数据中mappings,如果数据为空,则说明是非结构化索引。
使用Head插件进行索引的创建,在tab标签上,选择索引
,点 新建索引
,输入索引的名称,注意名称的命名规则。(我自己验证,中文、有中划线的索引名字都可以创建成功!但是字母必须是小写!)
创建成功每个框框,都是Es索引的分片,粗线框是主分片 和 细线框 是分片的备份。
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":{}
}
}
结果:
{
"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
}
文档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插入不同的两个地方是:
(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插件查看数据:
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
}
}
}
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
查询功能是使用过操作比较频繁的。
首先进行数据初始化,创建一个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"
}
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 Reference 6
特定字段查询 所指特定值
在查询过程中,除了判断文档是否满足查询条件外,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
}
}
}
}
{
"query":{
"bool":{
"filter":{
"term":{
"word_count":5000
}
}
}
}
}
以一定的逻辑组合子条件查询,结合了Query和Filter Context
固定分数查询
布尔查询
1、创建Spring boot web项目,spring boot 使用2.0.4版本, elasticsearch为5.6.10
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.4.RELEASEversion>
<relativePath/>
parent>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
<dependency>
<groupId>org.elasticsearch.plugingroupId>
<artifactId>transport-netty3-clientartifactId>
<version>5.6.10version>
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
}
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);
}
}
如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到,谢谢!
如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!
欢迎访问我的csdn博客,我们一同成长!
不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!
博客首页 : http://blog.csdn.net/u010648555
© 每天都在变得更好的阿飞