带入
在学习初期的时候,可以把es的各种概念对比数据库来看。
-
索引库(indices)---------------------------------Database 数据库
-
(type)----------------------------------Table 数据表
-
(Document)--------------------------Row 行
-
(Field)---------------------Columns 列
概念 | 说明 |
---|---|
索引库(indices) | indices是index的复数,代表许多的索引, |
类型(type) | 类型是模拟mysql中的table概念,一个索引库下可以有不同类型的索引,类似数据库中的表概念。数据库表中有表结构,也就是表中每个字段的约束信息;索引库的类型中对应表结构的叫做映射(mapping),用来定义每个字段的约束。 |
文档(document) | 存入索引库原始的数据。比如每一条商品信息,就是一个文档 |
字段(field) | 文档中的属性 |
映射配置(mappings) | 字段的数据类型、属性、是否索引、是否存储等特性 |
为啥要把数据库称为索引呢?我们应当有这样的意识,数据即为索引,索引也即为数据。
1. 索引库操作
1.1 增加索引库
1.2 查看索引库
1.3 删除索引库
DELETE 索引库名。这个不再做演示。
2. 映射操作
2.1 创建字段映射
这里依旧采取PUT方式
PUT /索引库名/类型名称
{
"properties": {
"字段名": {
"type": "类型",
"index": true,
"store": true,
"analyzer": "分词器"
}
}
}
-
类型名称:就是前面讲的type的概念,类似于数据库中的表 字段名:任意填写,也可以不写,es会为我们指定默认的。下面指定许多属性,例如:
-
type:类型,可以是text、long、short、date、integer、object等
-
index:是否索引,默认为true
-
store:是否存储,默认为false
-
analyzer:分词器
下面我们来创建一个映射
POST test1/people
{
"properties":{
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"desc":{
"type": "text"
}
}
}
ok
也可以直接在创建索引库的时候指定相关的文档属性,不必太拘泥于形式
PUT /database1
{
"settings": {},
"mappings": {
"properties": {
"title": {
"type": "keyword"
},
"factory": {
"type": "text",
"analyzer": "ik_max_word"
},
"price":{
"type": "long"
}
}
}
}
这样创建出的东西也是没问题的。
2.2 查看字段映射
我们直接使用elasticsearch-head查看即可。
或者用kibana查看也可以。
3. 操作文档
3.1 新增文档
3.1.1 随机id
通过POST请求,可以向一个已经存在的索引库中添加文档数据。
POST /索引库名/类型名
{
"key":"value"
...
}
使用elasticsearch-head查看下test1索引库的情况
Ok,已经存进来了。没有任何问题。
另外,需要注意的是,在响应结果中有个_id字段,这个就是这条文档数据的唯一标示,以后的增删改查都依赖这个id作为唯一标示。
可以看到id的值为:yZs6En0BPM71Aju4rWTq,这里我们新增时没有指定id,所以是ES帮我们随机生成的id。
我们利用kibana也可以查看刚才存入的文档,此处的请求方式当然还是get。后面熟练了以后,我们就不再利用elasticsearch-head查看数据了,直接都用kibana.
-
_source:源文档信息,所有的数据都在里面。
-
_id:这条文档的唯一标示
3.1.2 新增文档并自定义id
POST /索引库名/类型/id值
{
...
}
显示成功了。此时我们再查询下:
3.2. 修改文档
注意es并无专门的修改,覆盖即是修改,我们修改下id为1的李四的记录。
已经显示修改成功了。
如果我们要修改的目标id不存在,那么这个修改就会变成新增,如下:
有一个细节需要注意下,如果你不是全字段修改的话,没有指明的字段会被抹除掉,如下:
我们再来查询下小武的相关信息
果然desc相关的信息都没有了,其实这样是很不舒服的,因为大家的习惯上,做修改只想指明要修改的字段,不修改的字段一般希望它保持不变,es也为我们提供了解决方案。先把小武的信息改回去,如下:
然后在id后面加个_update,就可以实现只修改目标字段,其余保持不动了。
再次查看数据,目的已经达到。不知道大家有没有注意到,es的返回结果中version在不断发生改变,每修改一次数据version就会发生一次改变,这里大家能不能想到cas呢,要想到,同时有时间的话大家不妨回忆下cas的知识!
3.3. 删除文档
删除文档的语法为:DELETE /索引库名/类型名/id值
不再演示了,也比较简单
3.4 查看文档
3.4.1 基本查询
GET /索引库名/_search
{
"query":{
"查询类型":{
"查询条件":"查询条件值"
}
}
}
这里的query代表一个查询对象,里面可以有不同的查询属性
查询类型:
例如:match_all, match,term , range 等等
查询条件:查询条件会根据类型的不同,写法也有差异,后面详细讲解
3.4.2 查询所有(match_all)
- query:代表查询对象
- match_all:代表查询所有
3.4.3 匹配查询(match)
加入一些测试的数据,便于测试:
直接查询”好心人”出现四个结果,因为好心人、人、好都是分词的结果,均被检索。多个词条之间是or的关系,所以有1个都会被检索。
这里需要注意的一点是,尽管有四个记录都被检索出来了。但是他们的分值是不一样的,分值越大,表示匹配度是越高的!
如果我们想精确的匹配,”好心人”这个词条,那么可以像下面这么做,包含所有词条才会被检索到。
3.4.4 词条匹配(term)
term 查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些未分词的字符串。
GET /test1/people/_search
{
"query":{
"term": {
"age": {
"value": "9"
}
}
}
}
这里我们要特别注意的意见事情是,match的匹配是会按照词条查询的。想要做到整个字段的全匹配查询需要依靠term。term加keyword才可以精确查询,term加text不可以,依旧会被分词。那么在text能不能精确匹配呢,可以的,例如使用match并且指定操作为operator就可以了,这样就要求查询出的结果必须匹配到分词后的所有词条!
3.4.5 布尔组合(bool)
bool把各种其它查询通过must(与)、must_not(非)、should(或)的方式进行组合。
GET /database1/_search
{
"query":{
"bool":{
"must": { "match": { "title": "苹果手机" }},
"must_not": { "match": { "factory": "apple" }}
}
}
}
和预期一致。
3.4.6 范围查询(range)
range 查询找出那些落在指定区间内的数字或者时间
示例
GET database1/_search
{
"query": {
"range": {
"price": {
"gte": 2000,
"lte": 5000
}
}
}
}
范围查询非常简单,就不一一演示了。规则给大家列在下面。
range查询允许以下字符:
操作符 | 说明 |
---|---|
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
3.4.7 模糊查询(fuzzy)
我们刚才所说的匹配查询或者词条查询和这里要说的模糊查询都不是一个概念!
fuzzy 查询是 term 查询的模糊等价。它允许用户搜索词条与实际词条的拼写出现偏差,但是默认偏差的编辑距离不得超过2。我感觉一般这个查询不用于汉语,因为汉语的拼写错误要怎么定义呢?
GET database1/_search
{
"query": {
"fuzzy": {
"factory": "appl"
}
}
}
Ok的,查询”appl”发现依旧可以有结果的。还可以通过fuzziness来指定允许的编辑距离。
3.5 结果过滤
默认情况下,elasticsearch在搜索的结果中,会把文档中保存在_source的所有字段都返回。
如果我们只想获取其中的部分字段,我们可以添加_source的过滤,例如查询手机的时候只选择title和price显示。
GET database1/_doc/_search
{
"query": {
"match": {
"factory": "苹果"
}
}
,
"_source": ["title","price"]
}
Source中可以指定includes和excludes来进行字段的指明和排除。
GET database1/_doc/_search
{
"query": {
"match": {
"factory": "苹果"
}
}
,
"_source": {
"includes": ["price","factory"],
"excludes": "factory"
}
}
3.6 过滤(filter)
条件查询中进行过滤
所有的查询都会影响到文档的评分及排名。如果我们需要在查询结果中进行过滤,并且不希望过滤条件影响评分,那么就不要把过滤条件作为查询条件来用。而是使用filter方式:
GET database1/_search
{
"query":{
"bool":{
"must":{ "match": { "factory": "苹果" }},
"filter":{
"range":{"price":{"gt":2000,"lt":8000}}
}
}
}
}
无查询条件,直接过滤
如果一次查询只有过滤,没有查询条件,不希望进行评分,我们可以使用constant_score取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。
3.7 排序
3.7.1 单字段排序
sort 可以让我们按照不同的字段进行排序,并且通过order指定排序的方式。
3.7.2 多字段排序
假定我们想要使用desc进行匹配查询,并且匹配的结果首先按照age降序排序,然后按照相关性得分升序排序。
结果没问题,排在前面的排序字段将优先被考虑。
3.8 聚合
聚合可以让我们极其方便的实现对数据的统计、分析。例如:
- 什么品牌的手机最受欢迎?
- 这些手机的平均价格、最高价格、最低价格?
- 这些手机每月的销售情况如何?
实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果。
3.8.1 基本概念
Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫桶
,一个叫度量
:
桶(bucket)
桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个桶
,例如我们根据国籍对人划分,可以得到中国桶
、英国桶
,日本桶
……或者我们按照年龄段对人进行划分:010,1020,2030,3040等。桶这个叫法非常的形象。
Elasticsearch中提供的划分桶的方式有很多:
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似,需要知道分组的间隔(interval)
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
- ……
综上所述,我们发现bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量。
度量(metrics)
分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
- ……
为了测试聚合,我们先批量导入一些数据
创建索引:
POST /car
{
"mappings": {
"orders": {
"properties": {
"color": {
"type": "keyword"
},
"make": {
"type": "keyword"
}
}
}
}
}
注意:在ES中,需要进行聚合、排序、过滤的字段其处理方式比较特殊,因此不能被分词,必须使用keyword
或数值类型
。这里我们将color和make这两个文字类型的字段设置为keyword类型,这个类型不会被分词,将来就可以参与聚合
导入数据,这里是采用批处理的API,大家直接复制到kibana运行即可:
POST /car/orders/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "红", "make" : "本田", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "绿", "make" : "福特", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "蓝", "make" : "丰田", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "绿", "make" : "丰田", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "红", "make" : "宝马", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "蓝", "make" : "福特", "sold" : "2014-02-12" }
3.8.2 聚合为桶
首先,我们按照 汽车的颜色color来
划分桶
,按照颜色分桶,最好是使用TermAggregation类型,按照颜色的名称来分桶。
GET /car/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color.keyword"
}
}
}
}
-
size: 查询条数,这里设置为0,因为我们不关心搜索到的数据,只关心聚合结果,提高效率
-
aggs:声明这是一个聚合查询,是aggregations的缩写
-
popular_colors:给这次聚合起一个名字,可任意指定。
-
terms:聚合的类型,这里选择terms,是根据词条内容(这里是颜色)划分
- field:划分桶时依赖的字段
-
-
结果:
-
hits:查询结果为空,因为我们设置了size为0
-
aggregations:聚合的结果
-
popular_colors:我们定义的聚合名称
-
buckets:查找到的桶,每个不同的color字段值都会形成一个桶
- key:这个桶对应的color字段的值
- doc_count:这个桶中的文档数量
通过聚合的结果我们发现,目前红色的小车比较畅销!
3.8.3 桶内度量
前面的例子告诉我们每个桶里面的文档数量,这很有用。 但通常,我们的应用需要提供更复杂的文档度量。 例如,每种颜色汽车的平均价格是多少?
因此,我们需要告诉Elasticsearch使用哪个字段
,使用何种度量方式
进行运算,这些信息要嵌套在桶
内,度量
的运算会基于桶
内的文档进行
现在,我们为刚刚的聚合结果添加 求价格平均值的度量:
GET /car/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color.keyword"
},
"aggs":{
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
- aggs:我们在上一个aggs(popular_colors)中添加新的aggs。可见度量也是一个聚合
- avg_price:聚合的名称
- avg:度量的类型,这里是求平均值
- field:度量运算的字段
结果
可以看到每个桶中都有自己的avg_price
字段,这是度量聚合的结果。
3.8.4 桶内套桶
刚刚的案例中,我们在桶内嵌套度量运算。事实上桶不仅可以嵌套运算, 还可以再嵌套其它桶。也就是说在每个分组中,再分更多组。
比如:我们想统计每种颜色的汽车中,分别属于哪个制造商,按照make
字段再进行分桶
GET /car/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color.keyword"
},
"aggs":{
"avg_price": {
"avg": {
"field": "price"
}
},
"maker":{
"terms":{
"field":"make.keyword"
}
}
}
}
}
}
结果
-
原来的color桶和avg计算我们不变
-
maker:在嵌套的aggs下新添一个桶,叫做maker
-
terms:桶的划分类型依然是词条
-
filed:这里根据make字段进行划分
-
我们可以看到,新的聚合
maker
被嵌套在原来每一个color
的桶中。 -
每个颜色下面都根据
make
字段进行了分组 -
我们能读取到的信息:
-
红色车共有4辆
-
红色车的平均售价是 $32,500 美元。
-
其中3辆是 Honda 本田制造,1辆是 BMW 宝马制造。
暂时就聊这么多。其实还有很多的知识没涉及到,里我们暂时不展开了,精力要从这里转移到整合springboot了!
-