Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
但是,Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
不过,Elasticsearch不仅仅是Lucene和全文搜索,我们还能这样去描述它:
- 分布式的实时文件存储,每个字段都被索引并可被搜索
- 分布式的实时分析搜索引擎
- 可以扩展到上百台服务器,处理PB级结构化或非结构化数据
而且,所有的这些功能被集成到一个服务里面,你的应用可以通过简单的RESTful API、各种语言的客户端甚至命令行与之交互。
基于HTTP协议,以JSON为数据交互格式的RESTful API
其他所有程序语言都可以使用RESTful API,通过9200端口的与Elasticsearch进行通信,你可以使用你喜欢的WEB客户端,事实上,如你所见,你甚至可以通过curl命令与Elasticsearch通信。向ElasticSearch发出的请求的组成部分与其它普通的HTTP请求是一样的:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
- VERB:HTTP方法:GET、POST、DELETE、PUT、HEAD
- PROTOCOL:HTTP或者HTTPS协议
- HOST:主机名
- PORT:ElasticSearch服务的端口,默认为9200
- PATH:API路径,PATH可以包含多个组件,例如_cluster/stats或者_nodes/stats/jvm
- QUERY_STRING:一些可选的请求参数,例如
?pretty
参数将使请求返回的更加美观易读的JSON数据 - BODY:一个JSON格式的请求主体
举个栗子:
curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query":{
"match_all":{}
}
}
'
面向文档
ElasticSearch是面向文档的,这意味着它可以存储整个对象或文档。然而它不仅仅是存储,还会索引每个文档的内容使之可以被搜索。在ElasticSearch中。在ElasticSearch中,你可以对文档进行索引、搜索、排序、过滤。这种理解的方式与以往完全不同,这也是ElasticSearch能够执行复杂的全文搜索的原因之一。ElasticSearch使用JSON作为文档序列化格式,JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。它简洁、简单且容易阅读。
索引
在ElasticSearch中存储数据的行为就叫做索引(indexing),不过在索引之前,我们需要明确数据存储在哪里。在ElasticSearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中。ElasticSearch集群中可以存在多个索引(indices),每一个缩影可以包含多个类型(types),每一个类型可以包含多个文档(documents),每个文档可以包含多个字段(fields)。接下来可以看一下ElasticSearch对于与普通关系型数据库:
Relational DB --> Databases --> Tables --> Rows --> Columns
ElasticSearch --> indices --> types --> documents --> fields
可以看出,ElasticSearch中索引(indices)
类似于关系型数据库中的数据库,类型(types)
类似于关系型数据库的表,文档(documents)
类似于关系型数据库的行,字段(fields)
类似于关系型数据库中的列
此时我们有个需求,往3年级2班插入一个学生的信息,我们可以通过一个命令来完成操作
curl -XPUT 'http://localhost:9200/grade3/class2/1' -d '
{
"name":"张三",
"age":18,
"email":"xxx@163.com"
}
'
将会返回如下数据:
{
"_index" : "grade3",
"_type" : "class2",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : true
}
此时就完成了一个简单的插入操作,将张三
的基本信息插入到ElasticSearch中的grade3
索引,class2
类型中,并设Id为1。PUT请求的响应体包括{"created": true}
,这证实了文档已经创建了。如果我们插入ID已经存在的文档,我们仍旧会得到一个JSON响应体,但是created
将会是false
,表示未创建,将会将原来的文档数据覆盖。
此时我们有个需求,如果ID存在,我们就不进行插入操作,不将其覆盖,我们可以通过两种方式进行操作:
- 使用op_type
查询,如:curl -XPUT "http://localhost:9200/grade3/class2/1?op_type=create"
- 在URL末端拼接_create
,如:curl -XPUT "http://localhost:9200/grade3/class2/1/_create"
如果文档存在,将会出现409错误
那么我们该如何取出这个学生的信息呢,我们也可以通过一个命令来完成:
curl -XGET 'http://localhost:9200/grade3/class2/1'
将会获得如下数据:
{
"_index": "grade3",
"_type": "class2",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name":"张三",
"age":18,
"email":"xxx@163.com"
}
}
GET 请求的响应体包括{"found": true}
,这证实了文档已经被找到。 如果我们请求一个不存在的文档,我们仍旧会得到一个 JSON 响应体,但是found
将会是false
。
上面的插入方式,为开发者自己为数据设定一个ID值,当我们不需要主动设置ID值时,可以让ElasticSearch自动为你设置一个唯一个ID值,此时应该使用POST
请求:
curl -XPOST 'http://localhost:9200/grade3/class2' -d '
{
"name":"张三",
"age":18,
"email":"xxx@163.com"
}
'
此时将会返回如下数据:
{
"_index" : "grade3",
"_type" : "class2",
"_id" : "AV8pasLoj7fgiH8Scljp",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : true
}
此时表明数据插入成功,并且自动为你分配一个ID:”AV8pasLoj7fgiH8Scljp”
索引命名:必须小写,不能以下划线开头,不能包含逗号。类型命名:可以是大写或小写,不能以下划线或句号开头,不能包含逗号,长度限制为256个字符
轻量搜索
一个GET
是相当简单的,可以直接获得指定的文档。现在稍微尝试一下高级点的功能,比如一个简单的搜索:
- 搜索全部数据
curl -XGET 'http://localhost:9200/grade3/class2/_search'
- 获取全部数据的数量
curl -XGET 'http://localhost:9200/grade3/class2/_count'
- 过滤输出字段,如下值输出name与addr数据,如果什么数据都不需要,直接
curl -XGET 'http://localhost:9200/grade3/class2/1?_source
curl -XGET 'http://localhost:9200/grade3/class2/1?_source=name,addr'
- 查询名字为
张三
的数据
curl -XGET 'http://localhost:9200/grade3/class2/_search?q=name:张三'
值得注意的是,使用上面方法搜索中文时,如搜索name:”张三”,将匹配name中包含‘张’或者‘三’的数据,搜索英文时,如搜索name:”python developer”,将匹配name中包含‘python’或者‘developer’的数据,但英文只有一个单词的话,不会拆分成字符来搜索
根据查询表达式搜索
Query-string 搜索通过命令非常方便地进行临时性的即席搜索 ,但它有自身的局限性(参见 轻量 搜索 )。Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。
例如,查询name
为张三
的数据
curl -XGET 'http://localhost:9200/grade3/class2/_search' -d '
{
"query":{
"match":{
"name":"张三"
}
}
}
'
将会获得如下数据:
{
"took": 27,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 6,
"max_score": 2.0519085,
"hits": [
{
"_index": "my_index",
"_type": "test_type",
"_id": "2",
"_score": 2.0519085,
"_source": {
"name": "andoird",
"addr": "深几把圳"
}
},
{
"_index": "my_index",
"_type": "test_type",
"_id": "4",
"_score": 0.79423964,
"_source": {
"name": "广东",
"addr": "深南大道"
}
}
...
]
}
}
观察如上数据,我们可以发现有一个_score
字段,即每个文档跟查询的匹配程度,ElasticSearch默认是按照_score
排序的
值得注意的是,使用上面方法搜索中文时,如搜索name:”张三”,将匹配name中包含‘张’或者‘三’的数据,搜索英文时,如搜索name:”python developer”,将匹配name中包含‘python’或者‘developer’的数据,但英文只有一个单词的话,不会拆分成字符来搜索
短语搜索
找出一个属性中的独立单词是没有问题的,但有时候想要精确匹配一系列单词或者短语 。比如,我想搜索name包含张三
的数据,即同时包含张和三,并且紧挨着的所有数据,很显然上面match
无法实现该需求,此时需要用match_phrase
curl -XGET 'http://localhost:9200/grade3/class2/_search' -d '
{
"query":{
"match_phrase":{
"name":"张三"
}
}
}
'
删除文档
删除文档的语法和我们所知道的规则相同,只是使用DELETE
方法:
# 删除grade3索引,class2类型,id为1的数据
curl -XDELETE "http://localhost:9200/grade3/class2/1"
将会获得如下数据:
{
"found" : true,
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 3
}
DELETE
请求体包括{"found": true}
,这证实了文档已经被找到并且被删除。 如果我们请求一个不存在的文档,我们仍旧会得到一个 JSON 响应体,但是found
将会是false
。
返回数据分析
在上面的轻量搜索
中知道了如何简单的搜索出数据,可是ElasticSearch通过你的搜索请求之后返回一大堆数据,是否都明白所有字段的意思呢?接下来先用一个空搜索获取到所有数据:
curl -XGET 'http://localhost:9200/grade3/class2/_search'
返回数据如下:
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 15,
"successful" : 15,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [
{
"_index" : "grade3",
"_type" : "class2",
"_id" : "AV3p-1XqqNRvUsZQhddU",
"_score" : 1.0,
"_source" : {
"name":"张三",
"age":18,
"email":"xxx@163.com"
}
},
{
"_index" : "grade3",
"_type" : "class2",
"_id" : "123",
"_score" : 1.0,
"_source" : {
"name":"李四",
"age":20,
"email":"xxx@163.com"
}
}
]
}
}
- hits:返回结果中,最重要的就是hits,它包含匹配到的文档的数量
total
,和包裹匹配到的所有数据的数组hits
,以及max_score
为查询锁匹配文档的_score
的最大值 - took:为整个搜索请求一共消耗了多少毫秒
- shards:告诉我们参与查询的分片总数
total
,和成功了的分片数量successful
,以及失败了的分片数量failed
- timeout:是否超时,默认情况下,ElasticSearch不会超时。开发者也可以自己设置请求超时时间,如:
curl -XGET 'http://localhost:9200/grade3/class2/_search?timeout=10ms'