- 一个索引可以理解成一个关系型数据库
- 一种type就像一类表,比如user表,order表。
注意
es 5.x中一个index可以有多种type
es6.x中一个index只能有一种type
es7.x以后已经移除type这个概念
mapping定义了每个字段的类型等信息。相当于关系型中的表结构
一个document相当于关系型数据库中的一行记录
相当于关系型数据库表的字段
集群由一个或多个节点组成,一个集群有一个默认名称“elasticsearch”
集群的节点,一台机器或者一个进程
副本是分片的副本。分片有主分片(primary Shard)和副本分片(replica Shard)之分
一个index数据在物理上被分布在多个主分片中,每个主分片只存放部分数据
每个主分片可以有多个副本,叫副本分片,是主分片的复制
像上面基础概念所讲的,映射mapping就是mysql中的表结构。在mysql定义表结构时,我们使用以下的sql语句。首先,创建一个主键自增的表。因为es中的_id元数据也是默认采用自动生成的。
CREATE TABLE `testone` (
`id` int(50) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`descs` varchar(255) DEFAULT NULL,
`birthday` datetime DEFAULT NULL,
`address` varchar(500) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PUT http://localhost:9200/testone
{
"mappings": {
"_doc": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"descs": {
"type": "text"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
GET http://localhost:9200/testone/_doc/_mapping
在定义完文档结构后,可以添加字段,改变文档结构。无论是es这种非结构数据存储还是关系型存储的mysql,都是不建议减少字段的。
ALTER TABLE testone add address VARCHAR(255)
PUT http://localhost:9200/testone/_mapping/_doc
{
"properties": {
"address": {
"type": "text"
}
}
}
es不能修改字段类型的,如果非要如此操作,可以使用重新索引,将testone的数据索引到testwo,testone的数据可以传送到testwo中。
ALTER TABLE testone MODIFY address VARCHAR(500)
PUT http://localhost:9200/testwo
{
"mappings": {
"_doc": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "text"
},
"age": {
"type": "keyword"
},
"descs": {
"type": "text"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
POST http://localhost:9200/_reindex
{
"source": {
"index": "testone"
},
"dest": {
"index": "testwo"
}
}
重新索引后可以将之前的索引删除
创建文档在mysql就是新增一条记录。
insert into testone(name,age,descs,birthday,address)
VALUES ('lyc','25','palyer','2010-02-20 20:00:00','beijing')
在es中新增一条记录时,id可以是es默认的自增模式,也是可以自己指定id值去创建。
POST http://localhost:9200/testone/_doc
{
"name": "lyc",
"age": "25",
"descs": "palyer",
"birthday": "2010-02-20 20:00:00",
"address": "beijing"
}
POST http://localhost:9200/testone/_doc/1
{
"name": "test",
"age": "25",
"descs": "palyer",
"birthday": "2010-02-20 20:00:00",
"address": "beijing"
}
若是put请求,必须指定id,post请求也可以添加自定义id
文档在Elasticsearch中是不可变的——我们不能修改他们。如果需要更新已存在的文档,可以使用上文所讲的重建索引(reindex) 或者替换掉它。
更新文档时,在Elasticsearch内部,会将之前的文档标记旧文档并删除,然后添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。这个API 似乎 允许你修改文档的局部,但事实上Elasticsearch遵循与之前所说完全相同的过程,这个过程如下:
- 从旧文档中检索JSON
- 修改它
- 删除旧文档
- 索引新文档
唯一的不同是update API完成这一过程只需要一个客户端请求既可,不再需要get和index请求了
空查询就是不加查询参数的查询,将表或索引中的记录都查询出来。
在mysql中如果我们不指定任何查询参数,将会查询出所有表记录。而在es中为了效率问题,默认只会查询出前10条记录
select * from testone
GET http://localhost:9200/testone/_doc/_search
{}
hits
响应中最重要的部分是hits
,它包含了total
字段来表示匹配到的文档总数,hits
数组还包含了匹配到的前10条数据。
hits
数组中的每个结果都包含_index
、_type
和文档的_id
字段,被加入到_source
字段中这意味着在搜索结果中我们将可以直接使用全部文档。这不像其他搜索引擎只返回文档ID,需要你单独去获取文档。
每个节点都有一个_score
字段,这是相关性得分(relevance score),它衡量了文档与查询的匹配程度。默认的,返回的结果中关联性最大的文档排在首位;这意味着,它是按照_score
降序排列的。这种情况下,我们没有指定任何查询,所以所有文档的相关性是一样的,因此所有结果的_score
都是取得一个中间值1
max_score
指的是所有文档匹配查询中_score
的最大值。
took
took
告诉我们整个搜索请求花费的毫秒数。
shards
_shards
节点告诉我们参与查询的分片数(total
字段),有多少是成功的(successful
字段),有多少的是失败的(failed
字段)。通常我们不希望分片失败,不过这个有可能发生。如果我们遭受一些重大的故障导致主分片和复制分片都故障,那这个分片的数据将无法响应给搜索请求。这种情况下,Elasticsearch将报告分片failed
,但仍将继续返回剩余分片上的结果。
timeout
time_out
值告诉我们查询超时与否。一般的,搜索请求不会超时。如果响应速度比完整的结果更重要,你可以定义timeout
参数为10
或者10ms
(10毫秒),或者1s
(1秒)
GET http://localhost:9200/testone/_doc?timeout=10ms
Elasticsearch将返回在请求超时前收集到的结果。
超时不是一个断路器(circuit breaker)
在mysql中多表查询需要用到关联,则关联查询时需要两个表有逻辑关联,如果是两个不相干,互相独立不依赖的表是无法做关联查询的。
而在es中可以多索引多类别的查询
GET http://localhost:9200/testone,ca_bm_blog_message/_doc,_doc/_search
翻译一下,查询testone和ca_bm_blog_message索引下的testone的_doc类别和ca_bm_blog_message的_doc类型。格式上为
select * from testone limit 0,10
select * from testone limit 5,20
- size: 结果数,默认10
- from: 跳过开始的结果数,默认0
testone/_search?size=10
testone/_search?size=20&from=5
应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住一个搜索请求常常涉及多个分片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。
在集群系统中深度分页
为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。
现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个!
search API有两种表单:一种是“简易版”的查询字符串(query string)将所有参数通过查询字符串定义,另一种版本使用JSON完整的表示请求体(request body),这种富搜索语言叫做结构化查询语句(DSL)
select * from testone where name = 'zst'
GET http://localhost:9200/testone/_search?q=%2Bname%3Azst
注意 参照URL编码表
code | decode |
---|---|
%2B | + |
%3A | : |
%2D | - |
… | … |
decode之后是这样:
GET http://localhost:9200/testone/_search?q=+name:zst
"+“前缀表示语句匹配条件必须被满足。类似的”-"前缀表示条件必须不被满足。所有条件如果没有+或-表示是可选的——匹配越多,相关的文档就越多。
在不指定查询字段匹配时,如
GET http://localhost:9200/testone/_search?q=palyer
es会把文档中所有字段组合生成一个新的字段_all,将字段文本都合成这一个新字段(此处只是类比)
然后所有出符合条件的文档记录。
"name": "zst",
"age": "25",
"desc": "palyer",
"birthday": "2000-02-20 20:00:00",
"address": "beijing"
类似于,_all : “zst 25 palyer 2000-02-20 20:00:00 beijing”
当查询参数过多,查询条件组合过多,在简易查询中就不易读了
GET http://localhost:9200/testone/_search?q=+name:(test zst) +birthday:>2014-09-10
-address:shanghai
翻译成sql就是
select * from testone where name in ('test','zst') and birthday > '2020-01-10 ' and address<>'shanghai'
此时就需要使用JSON完整的表示请求体(request body)
未完待续。。。下节将字段类型和分词对查询的影响