02 Elasticsearch的API使用

一、前言

1、RESTful & JSON

REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。 Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。

在服务器端,应用程序状态和功能可以分为各种资源。资源是一个有趣的概念实体,它向客户端公开。资源的例子有:应用程序对象、数据库记录、算法等等。每个资源都使用 URI(Universal Resource Identifier) 得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、 PUT、 POST 和DELETE。

在 REST 样式的 Web 服务中,每个资源都有一个地址。资源本身都是方法调用的目
标,方法列表对所有资源都是一样的。这些方法都是标准方法,包括 HTTP GET、 POST、PUT、 DELETE,还可能包括 HEAD 和 OPTIONS。简单的理解就是,如果想要访问互联网上的资源,就必须向资源所在的服务器发出请求,请求体中必须包含资源的网络路径, 以及对资源进行的操作(增删改查)。

REST 样式的 Web 服务若有返回结果,大多数以JSON字符串形式返回。

2、Postman 客户端工具

如果直接通过浏览器向 Elasticsearch 服务器发请求,那么需要在发送的请求中包含
HTTP 标准的方法,而 HTTP 的大部分特性且仅支持 GET 和 POST 方法。所以为了能方便地进行客户端的访问,可以使用 Postman 软件Postman 是一款强大的网页调试工具,提供功能强大的 Web API 和 HTTP 请求调试。

软件功能强大,界面简洁明晰、操作方便快捷,设计得很人性化。 Postman 中文版能够发送任何类型的 HTTP 请求 (GET, HEAD, POST, PUT…),不仅能够表单提交,且可以附带任意类型请求体。

Postman下载页面:https://www.postman.com/downloads/

3、倒排索引

正排索引(传统)

idcontent
1001my name is zhang san
1002my name is li si

倒排索引

keywordid
name1001, 1002
zhang1001

Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。 为了方便大家理解,我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比

在这里插入图片描述

ES 里的 Index 可以看做一个库,而 Types 相当于表, Documents 则相当于表的行。这里 Types 的概念已经被逐渐弱化, Elasticsearch 6.X 中,一个 index 下已经只能包含一个type, Elasticsearch 7.X 中, Type 的概念已经被删除了。

二、HTTP API

1、索引

(1) 创建

在 Postman 中,向 ES 服务器发 PUT 请求 : http://127.0.0.1:9200/shopping

结果:

{
    "acknowledged": true,//响应结果
    "shards_acknowledged": true,//分片结果
    "index": "shopping"//索引名称
}

后台日志:

[2021-04-08T13:57:06,954][INFO ][o.e.c.m.MetadataCreateIndexService] [DESKTOP-LNJQ0VF] [shopping] creating index, cause [api], templates [], shards [1]/[1], mappings []

如果重复发 PUT 请求添加索引,因 PUT 具有幂等性,所以会返回错误信息。

{
    "error": {
        "root_cause": [
            {
                "type": "resource_already_exists_exception",
                "reason": "index [shopping/8uAxOqFuQLm4VTneEWx4sQ] already exists",
                "index_uuid": "8uAxOqFuQLm4VTneEWx4sQ",
                "index": "shopping"
            }
        ],
        "type": "resource_already_exists_exception",
        "reason": "index [shopping/8uAxOqFuQLm4VTneEWx4sQ] already exists",
        "index_uuid": "8uAxOqFuQLm4VTneEWx4sQ",
        "index": "shopping"
    },
    "status": 400
}

POST 不是幂等的,如果改用 POST,会提示不能使用 POST 。

{
    "error": "Incorrect HTTP method for uri [/shopping] and method [POST], allowed: [GET, PUT, HEAD, DELETE]",
    "status": 405
}

(2) 查询

查询所有

在 Postman 中,向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/_cat/indices?v

这里请求路径中的_cat 表示查看的意思, indices 表示索引,所以整体含义就是查看当前 ES服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉,服务器响应结果如下 :

health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   shopping J0WlEhh4R7aDrfIc3AkwWQ   1   1          0            0       208b           208b
表头含义
health当前服务器健康状态: green(集群完整) yellow(单点正常、集群不完整) red(单点不正常)
status索引打开、关闭状态
index索引名
uuid索引统一编号
pri主分片数量
rep副本数量
docs.count可用文档数量
docs.deleted文档删除状态(逻辑删除)
store.size主分片和副分片整体占空间大小
pri.store.size主分片占空间大小
查询单个

在 Postman 中,向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/shopping

{
    "shopping": {//索引名
        "aliases": {},//别名
        "mappings": {},//映射
        "settings": {//设置
            "index": {//设置 - 索引
                "creation_date": "1617861426847",//设置 - 索引 - 创建时间
                "number_of_shards": "1",//设置 - 索引 - 主分片数量
                "number_of_replicas": "1",//设置 - 索引 - 主分片数量
                "uuid": "J0WlEhh4R7aDrfIc3AkwWQ",//设置 - 索引 - 主分片数量
                "version": {//设置 - 索引 - 主分片数量
                    "created": "7080099"
                },
                "provided_name": "shopping"//设置 - 索引 - 主分片数量
            }
        }
    }
}

(3) 删除

在 Postman 中,向 ES 服务器发 DELETE 请求 : http://127.0.0.1:9200/shopping

{
    "acknowledged": true
}

2、文档操作

(1) 创建(Put & Post)

在 Postman 中,向 ES 服务器发 POST 请求 : http://127.0.0.1:9200/shopping/_doc,将以下数据添加到body中,选中 raw 的 json 格式。

注意,此处发送请求的方式必须为 POST,不能是 PUT,否则会发生错误 。

{
    "title":"小米手机",
    "category":"小米",
    "images":"http://www.gulixueyuan.com/xm.jpg",
    "price":3999.00
}

返回结果:

{
    "_index": "shopping",	//索引
    "_type": "_doc",	//类型-文档
    "_id": "ANQqsHgBaKNfVnMbhZYU",	//唯一标识,可以类比为 MySQL 中的主键,随机生成
    "_version": 1,	//版本
    "result": "created",	//结果,这里的 create 表示创建成功
    "_shards": {	//
        "total": 2,	//分片 - 总数
        "successful": 1,	//分片 - 总数
        "failed": 0	//分片 - 总数
    },
    "_seq_no": 0,
    "_primary_term": 1
}

上面的数据创建后,由于没有指定数据唯一性标识(ID),默认情况下, ES 服务器会随机生成一个。

如果想要自定义唯一性标识,需要在创建时指定: http://127.0.0.1:9200/shopping/_doc/1,请求体JSON内容为:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",	//<------------------自定义唯一性标识
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

此处需要注意:如果增加数据时明确数据主键,那么请求方式也可以为 PUT。

(2)查询

主键查询

主键查询文档时,需要指明文档的唯一性标识,类似于 MySQL 中数据的主键查询。

在 Postman 中,向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/shopping/_doc/1 。

返回结果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 1,
    "_seq_no": 1,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "title": "小米手机",
        "category": "小米",
        "images": "http://www.gulixueyuan.com/xm.jpg",
        "price": 3999
    }
}

查找不存在的内容,向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/shopping/_doc/1001。

返回结果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "found": false
}
全查询

查看索引下所有数据,向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/shopping/_search。

返回结果如下:

{
    "took": 133,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1,
                "_source": {
                    "title": "小米手机",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "1",
                "_score": 1,
                "_source": {
                    "title": "小米手机",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            }
        ]
    }
}

(3)全量修改

在 Postman 中,向 ES 服务器发 POST 请求 : http://127.0.0.1:9200/shopping/_doc/1 。会将原有的数据内容覆盖。

请求体JSON内容为:

{
    "title":"华为手机",
    "category":"华为",
    "images":"http://www.gulixueyuan.com/hw.jpg",
    "price":1999.00
}

修改成功后,服务器响应结果:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 2,
    "result": "updated",	//表示数据被更新
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 2,
    "_primary_term": 1
}

(4)局部修改

修改数据时,也可以只修改某一给条数据的局部信息

在 Postman 中,向 ES 服务器发 POST 请求 : http://127.0.0.1:9200/shopping/_update/1。

请求体JSON内容为:

{
	"doc": {
		"title":"小米手机",
		"category":"小米"
	}
}

返回结果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 3,
    "result": "updated",	//表示数据被更新
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 3,
    "_primary_term": 1
}

在 Postman 中,向 ES 服务器发 GET请求 : http://127.0.0.1:9200/shopping/_doc/1,查看修改内容:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 3,
    "_seq_no": 3,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "title": "小米手机",
        "category": "小米",
        "images": "http://www.gulixueyuan.com/hw.jpg",
        "price": 1999
    }
}

(5)删除

删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。

在 Postman 中,向 ES 服务器发 DELETE 请求 : http://127.0.0.1:9200/shopping/_doc/1

返回结果:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 4,
    "result": "deleted",	//删除成功
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 4,
    "_primary_term": 1
}

查看是否删除成功,发 GET 请求 : http://127.0.0.1:9200/shopping/_doc/1

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "found": false
}

3、文档查询

(1)URL带参查询

查找 category小米 的文档,发送 GET 请求 : http://127.0.0.1:9200/shopping/_search?q=category:小米

(2)根据指定字段查询

查找 category小米 的文档,发送 GET请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
	"query":{
		"match":{
			"category":"小米"
		}
	}
}

(3)查询所有

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
	"query":{
		"match_all":{}
	}
}

(4)查询返回指定字段

发送 GET请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
	"query":{
		"match_all":{}
	},
	"_source":["title"] //只返回title字段
}

(5)分页查询

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
	"query":{
		"match_all":{}
	},
	"from":0,	//表示查询开始的下标
	"size":2	//表示查询的个数
}

(6)查询结果排序

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
	"query":{
		"match_all":{}
	},
	"sort":{
		"price":{
			"order":"desc" //根据价格进行倒序排序
		}
	}
}

(7)and 查询

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
    "query":{
        "bool":{
            "must":[ //表示and
                {
                    "match":{
                        "category": "小米"
                    }
                },                
                {
                    "match":{
                        "price": 1999.00
                    }
                }
                
            ]
        }
    }
}

(8)or 查询

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
    "query":{
        "bool":{
            "should":[ //表示or
                {
                    "match":{
                        "category": "小米"
                    }
                },                
                {
                    "match":{
                        "category": "华为"
                    }
                }
                
            ]
        }
    }
}

(9)范围查询

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "category": "小米"
                    }
                },
                {
                    "match": {
                        "category": "华为"
                    }
                }
            ],
            "filter": {
                "range": {
                    "price": {
                        "gt": 3000
                    }
                }
            }
        }
    }
}

(10)模糊查询

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search。

  • “category”:“米” 会查询出所有小米手机
  • “category”:“华” 会查询出所有华为手机
  • “category”:“华米” 会查询出所有小米与华为手机

附带JSON体如下:

{
	"query":{
		"match":{
			"category":"华米"
		}
	}
}

(11)匹配短语

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search。只能使用字段的子集,才能查询。

{
	"query":{
		"match_phrase":{
			"category":"米"
		}
	}
}

(12)高亮查询

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search。

{
    "query": {
        "match_phrase": {
            "category": "华为"
        }
    },
    "highlight": {
        "fields": {
            "category": {}
        }
    }
}

结果:

{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 3.3479528,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "1003",
                "_score": 3.3479528,
                "_source": {
                    "title": "华为手机",
                    "category": "华为",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 5999.00
                },
                "highlight": {
                    "category": [
                        "<em>华</em><em>为</em>"
                    ]
                }
            }
        ]
    }
}

(13)聚合查询-分组

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search。

{
    "aggs": { //聚合操作
        "price_group": { //名称,随意起名
            "terms": { //分组
                "field": "price" //分组字段
            }
        }
    },
    "size":0 //不显示原始数据
}

结果:

{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "price_group": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": 3999.0,
                    "doc_count": 4
                },
                {
                    "key": 1999.0,
                    "doc_count": 1
                },
                {
                    "key": 5999.0,
                    "doc_count": 1
                }
            ]
        }
    }
}

(14)聚合查询-均值

发送 GET 请求 : http://127.0.0.1:9200/shopping/_search。

{
    "aggs": { //聚合操作
        "price_avg": { //名称,随意起名
            "avg": { //平均值
                "field": "price" //分组字段
            }
        }
    },
    "size":0
}

结果:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "price_avg": {
            "value": 3999.0
        }
    }
}

(15)滚动查询

参考:es分组后分页_解决 Elastic Search 的深分页问题
https://blog.csdn.net/weixin_32573931/article/details/112418874

  • 注意要在URL中的 search 后加上 scroll=1m,不能写在 request body 中,其中 1m 表示这个游标要保持开启 1 分钟
  • 可以指定 size 大小,就是每次回传几条数据,当回传到没有数据时,仍会返回 200 成功,只是 hits 里的 hits 会是空 list
  • 在初始化时除了回传 _scroll_id,也会回传前 100 笔(假设 size = 100)的数据
  • request body 和一般搜索一样,因此可以说在初始化的过程中,除了加上 scroll 设置游标开启时间之外,其他的都跟一般的搜寻没有两样(要设置查询条件,也会回传前 size 笔的数据)
a、初始化 - 请求

GET http://192.168.1.121:32000/shopping/_search?scroll=1m

b、初始化 - 返回结果
{
    "_scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAB8WSktlS1p6SHNSSXlfUjhDWURWQjd1dw==",
    "took": 181,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 4,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "1013",
                "_score": 1.0,
                "_source": {
                    "title": "小米手机",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 5999.00
                }
            }
        ]
    }
}
c、遍历数据 - 请求

使用初始化返回的 _scroll_id 来进行再次请求,每一次请求都会继续返回初始化中未读完数据,并且会返回一个 _scroll_id这个 _scroll_id 可能会改变,因此每一次请求应该带上上一次请求返回的 _scroll_id,且每次发送 scroll 请求时,都要在请求参数带上 scroll=1m,重新刷新这个 scroll 的开启时间,以防不小心超时导致数据取得不完整

注意:返回结果中的字段是 _scroll_id,但是请求中的字段是 scroll_id两者拼写上有不同

GET http://192.168.1.121:32000/_search/scroll?scroll=1m

{
    "_scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAIWR3lwVDJFN2xUQ0NEQWpZMmlVbU1yZw==",
    "took": 1,
    "timed_out": false,
    "terminated_early": true,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": []
    }
}
d、遍历数据 - 返回结果

如果没有数据了,就会回传空的 hits,可以用这个判断是否遍历完成了数据

{
    "_scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAIWR3lwVDJFN2xUQ0NEQWpZMmlVbU1yZw==",
    "took": 1,
    "timed_out": false,
    "terminated_early": true,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": []
    }
}
e、优化 scroll 查询

在一般场景下,scroll 通常用来取得需要排序过后的大笔数据,但是有时候数据之间的排序性对我们而言是没有关系的,只要所有数据都能取出来就好,这时能够对 scroll 进行优化

请求初始化
使用 _doc 去 sort 得出来的结果,这个执行的效率最快,但是数据就不会有排序,适合用在只想取得所有数据的场景

GET http://192.168.1.121:32000/shopping/_search?scroll=1m
{
    "query": {
        "match_all" : {}
    },
    "sort": ["_doc"]
}
f、清除 scroll

虽然我们在设置开启 scroll 时,设置了一个 scroll 的存活时间,但是如果能够在使用完顺手关闭,可以提早释放资源,降低 ES 的负担

DELETE _search/scroll
{
    "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAIWR3lwVDJFN2xUQ0NEQWpZMmlVbU1yZw=="
}

4、映射关系

接下来就需要建索引库(index)中的映射了,类似于数据库(database)中的表结构(table)。

创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)。

(1)创建一个索引

PUT http://127.0.0.1:9200/user

(2)创建映射

# PUT http://127.0.0.1:9200/user/_mapping

{
    "properties": {
        "name":{
        	"type": "text",
        	"index": true
        },
        "sex":{
        	"type": "keyword",
        	"index": true
        },
        "tel":{
        	"type": "keyword",
        	"index": false
        }
    }
}

(3)查询映射

GET http://127.0.0.1:9200/user/_mapping

结果

{
    "user": {
        "mappings": {
            "properties": {
                "name": {
                    "type": "text"
                },
                "sex": {
                    "type": "keyword"
                },
                "tel": {
                    "type": "keyword",
                    "index": false
                }
            }
        }
    }
}

(4)添加数据

#PUT http://127.0.0.1:9200/user/_create/1001
{
	"name":"小米",
	"sex":"男的",
	"tel":"1111"
}

结果

{
    "_index": "user",
    "_type": "_doc",
    "_id": "1001",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

(5)查找name含有“小”数据

#GET http://127.0.0.1:9200/user/_search
{
	"query":{
		"match":{
			"name":"小"
		}
	}
}

结果:

{
    "took": 495,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "user",
                "_type": "_doc",
                "_id": "1001",
                "_score": 0.2876821,
                "_source": {
                    "name": "小米",
                    "sex": "男的",
                    "tel": "1111"
                }
            }
        ]
    }
}

(6)查找sex含有“男”数据

#GET http://127.0.0.1:9200/user/_search
{
	"query":{
		"match":{
			"sex":"男"
		}
	}
}

结果:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }
}

找不想要的结果,只因创建映射时"sex"的类型为"keyword"。

"sex"只能完全为”男的“,才能得出原数据。

#GET http://127.0.0.1:9200/user/_search
{
	"query":{
		"match":{
			"sex":"男的"
		}
	}
}

结果

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "user",
                "_type": "_doc",
                "_id": "1001",
                "_score": 0.2876821,
                "_source": {
                    "name": "小米",
                    "sex": "男的",
                    "tel": "1111"
                }
            }
        ]
    }
}

(7)查询电话

报错只因创建映射时"tel"的"index"为false。

# GET http://127.0.0.1:9200/user/_search
{
	"query":{
		"match":{
			"tel":"11"
		}
	}
}
{
    "error": {
        "root_cause": [
            {
                "type": "query_shard_exception",
                "reason": "failed to create query: Cannot search on field [tel] since it is not indexed.",
                "index_uuid": "ivLnMfQKROS7Skb2MTFOew",
                "index": "user"
            }
        ],
        "type": "search_phase_execution_exception",
        "reason": "all shards failed",
        "phase": "query",
        "grouped": true,
        "failed_shards": [
            {
                "shard": 0,
                "index": "user",
                "node": "4P7dIRfXSbezE5JTiuylew",
                "reason": {
                    "type": "query_shard_exception",
                    "reason": "failed to create query: Cannot search on field [tel] since it is not indexed.",
                    "index_uuid": "ivLnMfQKROS7Skb2MTFOew",
                    "index": "user",
                    "caused_by": {
                        "type": "illegal_argument_exception",
                        "reason": "Cannot search on field [tel] since it is not indexed."
                    }
                }
            }
        ]
    },
    "status": 400
}

三、JAVA API

1、环境准备

新建Maven工程。

添加依赖:

<dependencies>
    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>7.8.0</version>
    </dependency>

    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-client</artifactId>
        <version>7.8.0</version>
    </dependency>

    <!-- elasticsearch 的客户端 -->
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.8.0</version>
    </dependency>

    <!-- elasticsearch 依赖 2.x 的 log4j -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.8.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.9</version>
    </dependency>
    <!-- junit 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

2、HelloElasticsearch

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import java.io.IOException;

public class HelloElasticsearch {
    public static void main(String[] args) throws IOException {
        // 创建客户端对象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.1.115", 9200, "http")));
        //		...
        System.out.println(client);

        // 关闭客户端连接
        client.close();
    }
}

3、索引

(1)创建

package index;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;

import java.io.IOException;

/**
 * 创建Index
 */
public class CreateIndex {
    public static void main(String[] args) throws IOException {
        // 创建客户端对象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.1.115", 9200, "http")));

        // 创建索引 - 请求对象
        CreateIndexRequest request = new CreateIndexRequest("user3");
        // 发送请求,获取响应
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        boolean acknowledged = response.isAcknowledged();
        // 响应状态
        System.out.println("操作状态 = " + acknowledged);

        // 关闭客户端连接
        client.close();
    }
}

(2)查询

package index;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;

import java.io.IOException;

/**
 * 查询索引
 */
public class SearchIndex {
    public static void main(String[] args) throws IOException {
        // 创建客户端对象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.1.115", 9200, "http")));
        // 查询索引 - 请求对象
        GetIndexRequest request = new GetIndexRequest("user3");
        // 发送请求,获取响应
        GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);

        System.out.println("aliases:" + response.getAliases());
        System.out.println("mappings:" + response.getMappings());
        System.out.println("settings:" + response.getSettings());

        client.close();
    }
}

(3)删除

package index;

import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import java.io.IOException;

/**
 * 删除索引
 */
public class DeleteIndex {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.1.115", 9200, "http")));
        // 删除索引 - 请求对象
        DeleteIndexRequest request = new DeleteIndexRequest("user3");
        // 发送请求,获取响应
        AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
        // 操作结果
        System.out.println("操作结果 : " + response.isAcknowledged());
        client.close();
    }
}

4、文档操作

在进行文档操作前,需要先添加以下两个类,ES任务接口和ES任务执行器。

ES任务接口

package base;

import org.elasticsearch.client.RestHighLevelClient;

/**
 * ES任务接口
 */
public interface ESTask {
    void doing(RestHighLevelClient client) throws Exception;
}

ES任务执行器

package base;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

public class ES {
    public static void doing(ESTask task) {
        // 创建客户端对象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.1.115", 9200, "http")));
        try {
            task.doing(client);
            // 关闭客户端连接
            client.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(1)添加

package doc;

import base.ESTask;
import com.fasterxml.jackson.databind.ObjectMapper;
import base.ES;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;
import po.User;

/**
 * 添加文档信息
 */
public class InsertDoc {
    private static ESTask insertTask = client -> {
        // 新增文档 - 请求对象
        IndexRequest request = new IndexRequest();
        // 设置索引及唯一性标识
        request.index("user").id("1001");

        // 创建数据对象
        User user = new User();
        user.setName("zhangsan");
        user.setAge(30);
        user.setSex("男");

        ObjectMapper objectMapper = new ObjectMapper();
        String productJson = objectMapper.writeValueAsString(user);
        System.out.println(productJson);
        // 添加文档数据,数据格式为 JSON 格式
        request.source(productJson, XContentType.JSON);
        // 客户端发送请求,获取响应对象
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        System.out.println("_index:" + response.getIndex());
        System.out.println("_id:" + response.getId());
        System.out.println("_result:" + response.getResult());
    };

    public static void main(String[] args) {
        //执行
        ES.doing(insertTask);
    }
}

(2)获取

package doc;

import base.ES;
import base.ESTask;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.RequestOptions;

/**
 * 获取文档信息
 */
public class GetDoc {
    static ESTask getDocTask = client -> {
        //1.创建请求对象
        GetRequest request = new GetRequest().index("user").id("1001");
        //2.客户端发送请求,获取响应对象
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        System.out.println("_index:" + response.getIndex());
        System.out.println("_type:" + response.getType());
        System.out.println("_id:" + response.getId());
        System.out.println("source:" + response.getSourceAsString());
    };

    public static void main(String[] args) {
        ES.doing(getDocTask);
    }
}

(3)更新

package doc;

import base.ES;
import base.ESTask;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

/**
 * 更新文档信息
 */
public class UpdateDoc {
    static ESTask updateDocTask = client -> {
        // 修改文档 - 请求对象
        UpdateRequest request = new UpdateRequest();
        // 配置修改参数
        request.index("user").id("1001");
        // 设置请求体,对数据进行修改
        request.doc(XContentType.JSON, "sex", "女");
        // 客户端发送请求,获取响应对象
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
        System.out.println("_index:" + response.getIndex());
        System.out.println("_id:" + response.getId());
        System.out.println("_result:" + response.getResult());
    };

    public static void main(String[] args) {
        ES.doing(updateDocTask);
    }
}

(4)删除

package doc;

import base.ES;
import base.ESTask;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.client.RequestOptions;

/**
 * 删除文档信息
 */
public class DeleteDoc {
    static ESTask deleteDocTask = client -> {
        //创建请求对象
        DeleteRequest request = new DeleteRequest().index("user").id("1001");
        //客户端发送请求,获取响应对象
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        //打印信息
        System.out.println(response.toString());
    };

    public static void main(String[] args) {
        ES.doing(deleteDocTask);
    }
}

5、文档查询

(1)查询任务

package doc;

import base.ESTask;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;

import java.util.Map;

/**
 * 查询任务
 */
public class QueryDocTask {
    /**
     * 简单查询
     */
    static ESTask simpleQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 条件查询
     */
    static ESTask conditionQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.termQuery("age", "30"));
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 分页查询
     */
    static ESTask pageQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        // 当前页其实索引(第一条数据的顺序号), from
        sourceBuilder.from(0);
        // 每页显示多少条 size
        sourceBuilder.size(2);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 查询排序
     */
    static ESTask sortQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        sourceBuilder.sort("age", SortOrder.ASC);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 查询过滤
     */
    static ESTask fetchQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());

        String[] includes = {};
        String[] excludes = {"age"};
        sourceBuilder.fetchSource(includes, excludes);

        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 组合查询
     */
    static ESTask composeQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 必须包含
        boolQueryBuilder.must(QueryBuilders.matchQuery("age", "30"));
        // 一定不含
        boolQueryBuilder.mustNot(QueryBuilders.matchQuery("name", "zhangsan"));
        // 可能包含
        boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "女"));

        sourceBuilder.query(boolQueryBuilder);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 范围查询
     */
    static ESTask rangeQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
        //大于等于30
        rangeQuery.gte("30");
        //小于50
        rangeQuery.lt("50");
        sourceBuilder.query(rangeQuery);

        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        printResponse(response);
    };

    /**
     * 模糊查询
     */
    static ESTask fuzzyQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //Fuzziness.ONE 表示可以相差一位字符
        sourceBuilder.query(QueryBuilders.fuzzyQuery("name", "wangwu").fuzziness(Fuzziness.ONE));
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //输出结果
        printResponse(response);
    };

    /**
     * 高亮查询
     */
    static ESTask highlightQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("name", "zhangsan");
        sourceBuilder.query(termsQueryBuilder);

        //构建高亮字段
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red'>");//设置标签前缀
        highlightBuilder.postTags("</font>");//设置标签后缀
        highlightBuilder.field("name");//设置高亮字段
        //设置高亮构建对象
        sourceBuilder.highlighter(highlightBuilder);

        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //输出结果
        printResponse(response);
    };

    /**
     * 最大值查询
     */
    static ESTask maxQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.aggregation(AggregationBuilders.max("maxAge").field("age"));
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //输出结果
        //printResponse(response);
        System.out.println(response);
    };

    /**
     * 分组查询
     */
    static ESTask groupQueryDoc = client -> {
        SearchRequest request = new SearchRequest();
        request.indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.aggregation(AggregationBuilders.terms("age_group").field("age"));
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //输出结果
        //printResponse(response);
        System.out.println(response);
    };

    /**
     * 打印结果
     *
     * @param response
     */
    private static void printResponse(SearchResponse response) {
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //输出每条查询的结果信息
            System.out.println(hit.getSourceAsString());
            //打印高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields.size() > 0)
                System.out.println(highlightFields);
        }
        System.out.println("<<========");
    }
}

(2)进行查询

package doc;

import base.ES;

/**
 * 文档查询
 */
public class QueryDoc {
    public static void main(String[] args) {
        //简单查询
        ES.doing(QueryDocTask.simpleQueryDoc);
        //条件查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //分页查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //排序
        ES.doing(QueryDocTask.conditionQueryDoc);
        //过滤查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //组合查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //范围查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //模糊查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //高亮查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //最大值查询
        ES.doing(QueryDocTask.conditionQueryDoc);
        //分组查询
        ES.doing(QueryDocTask.conditionQueryDoc);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值