ELK(五)ElasticSearch核心讲解

文档

在ElasticSearch中,文档以JSON格式进行存储,可以是复杂的结构

{
        "_index": "haoke",
        "_type": "user",
        "_id": "xrIe7XEBNovl8aBHQJyE",
        "_score": 1,
        "_source": {
          "id": 1002,
          "name": "李四",
          "age": 40,
          "sex": "男"
          "card":{
			"card_number":"123456789"
			}
        }
}

其中,card是一个复杂对象,嵌套的Card对象

元数据

一个文档不只有数据,它还包含了元数据(metadata) – 关于文档的信息.三个必须的元数据节点是

节点说明
_index文档存储的地方(索引)
_type文档代表的对象的类(文档类型)
_id文档的唯一标识(主键id)

_index

索引(index)类似于关系型数据库里的数据库–它是我们存储和索引相关联数据的地方

提示
事实上,我们的数据被存储在分片(shards)中,索引只是一个把一个或多个分片分组在一起的逻辑空间,然而,这只是一些内部细节,我们的程序完全不用关心分片,对于我们的程序而言,文档存储在索引(index)中,剩下的细节由ElasticSearch关心即可

_type

在应用中,我们使用对象表示一些"事物",例如一个用户,一篇博客,一个评论,或者一封邮件.每个对象都属于一个类(class),这个类定义了属性或与对象关联的数据 user类的对象可能包含姓名,性别,年龄和email地址
在关系型数据库中,我们经常将相同类的对象存储在一个表里,因为他们有着相同的结构 ,同理,在ElasticSearch中,我们使用相同类型(type) 的文档表示相同的"事物",因为他们的数据结构也是相同的
每个类型(type) 都有自己的映射(mapping) 或者结构定义,就像传统数据库表中的列一样,所有类型下的文档都被存储在同一索引下,但是类型的映射(mapping)会告诉ElasticSearch不同的文档如何被索引
_type的名字可以是大写或小写,不能包含下划线或逗号,我们将使用blog作为类型名(表示博客类型)

_id

id仅仅是一个字符串,它与_index 和_type组合时,就可以在ElasticSearch中唯一标识一个文档,当创建一个文档,你可以自定义_id,也可以让ElasticSearch帮你自动生成(32位长度)

查询响应

查询结果美化

请求后接?pretty
例如:http://175.24.75.131:9200/haoke/user/xrIe7XEBNovl8aBHQJyE?pretty

指定响应字段

GET /haoke/user/1005?_soure=id,name
# 就会返回指定的字段了

不返回元数据

GET /haoke/user/1005/_source
# 注意这个_source与上面的_source不同,这个_source在/后面,而不是作为参数传递
如果这时候还是想就指定某几个字段
GET /haoke/user/1005/_source?_source=id,name

判断文档是否存在

如果我们只需要判断文档是否存在,而不是查询文档内容,可以这样

HEAD /haoke/user/{id}
# 这样通过返回的状态码200代表有数据 404代表没有数据 就可以看出是否存在了

这只表示查询的那一刻文档不存在,但不表示几毫秒后依旧不存在,另一个进程在这期间可能创建新文档

批量操作

有些情况下可以通过批量操作以减少网络请求,如:批量查询,批量插入数据

  1. 批量查询
POST /haoke/user/_mget
{
	"ids":["xrIe7XEBNovl8aBHQJyE","yLIe7XEBNovl8aBHoZwk"] 
}

如果某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据

  1. _bulk操作 (批量插入,修改,删除)
    在ElasticSearch中,支持批量的插入,修改,删除操作,都是通过_bulk的api完成的
    请求格式:
{ action:{metadata}}\n #注意这里有回车换行,这个代表要执行什么动作
{ request body}\n
{ action:{metadata}}\n
{request body}\n

批量插入数据

POST /haoke/user/_bulk
{"create":{"_index":"haoke","_type":"user","_id":2001}}
{"id":2001,"name":"name1","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2002}}
{"id":2002,"name":"name2","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2003}}
{"id":2003,"name":"name3","age": 20,"sex": "男"}

# 注意每一行都需要\n 最下面也有一个\n

批量删除

POST /haoke/user/_bulk
{"delete":{"_index":"haoke","_type":"user","_id":2001}}
{"delete":{"_index":"haoke","_type":"user","_id":2002}}
{"delete":{"_index":"haoke","_type":"user","_id":2003}}

# 注意最下面的回车

由于delete没有请求体,所以action的下一行直接就是下一个action

新创建的索引后最开始添加数据不指定_id的情况下需要把create换成index
注意 用create的话 必须指定_id

一次性请求多少性能最高?

  • 整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其他请求可用的内存就越小,有一个最佳的bulk请求大小,超过这个大小,性能不再提升而且可能降低
  • 最佳大小,不是一个固定的数字,完全取决于硬件,文档的大小和复杂度以及索引和搜索的负载
  • 幸运的是,这个最佳点还是容易找到的: 试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了.开始的数量可以在1000-5000个文档之间,如果你的文档非常大,可以使用较小的批次
  • 通常着眼于你请求批次的物理大小是非常有用的 一千个1kb文档和一千个1Mb的文档大不相同,一个好的批次最好保持在5-15M大小之间

分页

和sql使用limit关键字返回只有一页的结果一样,ElasticSearch接收from和size参数

size: 结果数,默认10
from: 跳过开始的结果数,默认0

如果每页显示5个结果,页码从1到3,请求如下

GET /_search?size=5 #第一页
GET /_search?size=5&from=5 #第二页
GET /_search?size=5&from=10 #第三页

应该当心分页太深或者一次请求太多结果,结果在返回前会被排序,但是记住一个搜索请求常常涉及多个分片,每个分片生成自己排好序的结果,他们接着需要集中起来排序以确保整体排序正确

在集群系统中深度分页会产生一些问题
为了理解为什么深度分页是有问题的,假设在一个有5个主分片的索引中搜索,当我们请求结果的第一页(结果从1到10)时,每个分片产生自己最顶端10个结果然后返回他们给请求节点,它再排序这所有的50个结果以选出顶端的10个结果
假设我们请求第1000页–结果10001到10010.工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果,然后请求节点排序这50050个结果并丢弃50040个
在分布式系统中,排序结果的花费随着分页的深入二成倍增长,这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因

映射

前面我们创建的索引以及插入的数据,都是由ElasticSearch进行自动判断类型的,有些时候我们是需要进行明确字段类型的,否则,自动判断的类型和实际需求是不相符的
自动判断的规则如下:

JSON typeField type
Boolean:true or false“boolean”
wholenumber:123“long”
Floating point: 123.45“double”
String,valid date:“2014-09-15”“date”
String:“foo bar”“string”

ElasticSearch中支持的类型如下

类型表示的数据类型
Stringstring,text,keyword
wholenumberbyte.short,integer,long
Floating pointfloat,double
Booleanboolean
Datedate
  • string类型在ElasticSearch旧版本中使用较多,从ElasticSearch 5.x 开始不再支持string,由text和keyword来兴代替
  • text类型,当一个字段是要被全文检索的(要进行分词),比如产品描述,商品名等,应该使用text类型,设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项.text类型的字段不用于排序,很少用于聚合
  • keyword类型适用于索引结构化的字段(不需要进行分词),比如email地址 ,ip地址,状态码等,如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章),排序\聚合 keyword类型的字段只能通过精确值搜索到

创建明确类型的索引:

PUT /itcast # 索引名
{
    #指定分片及副本数
	"settings":{
		"index":{
			"number_of_shards":"2", 
			"number_of_replicas":"0" 
		}
	},
	#创建索引时指定映射类型
	"mappings":{
		# 文档类型
		"person":{
			# 文档中的字段名及明确类型
			"properties":{
				# type:text 为字符串类型且需要分词
				"name":{
					"type":"text"
				},
				"age":{
					"type":"integer"
				},
				# type:keyword 为字符串类型且不需要分词
				"mail":{
					"type":"keyword"
				},
				"hobby":{
					"type":"text"
				}
			}
		}
	}
}

查看映射:

GET /itcast/_mapping

插入数据

{"index":{"_index":"itcast","_type":"person"}}
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球,乒乓球,足球"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球,乒乓球,足球,篮球"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球,篮球,游泳,听音乐"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步,游泳"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐,看电影"}

# 注意 这里最后一行需要有回车
# 注意 新创建的索引 第一次添加数据不指定_id需要用index操作

测试搜索:

# 使用DSL搜索
POST /itcast/person/_search
{
	"query":{
		"match":{
			"hobby":"音乐"
		}
	}
}

结构化查询

  1. term查询
    term 主要用于精确匹配哪些值,比如数字,日期,布尔或not_analyzed的字符串(未经分析的文本数据类型)
 
 用法:
 {"term":{"age":26}}
 {"term":{"date":"2014-09-01"}}
 {"term":{"public":true}}
 {"term":{"tag":"full_text"}}
示例 DSL查询
POST /itcast/person/_search
{
		"query":{
				"term":{
					"age":20
				}
		}
}

  1. terms查询
    terms与term有点类似,但terms允许指定多个匹配条件,如果某个字段指定了多个值,那么文档需要一起去做匹配
用法:
{
	"terms":{
		"tag":["search","full_text","nosql"]
	}
}

示例:
POST /itcast/person/_search
{
		"query":{
				"terms":{
					"age":[20,21,22]
				}
		}
}
  1. range查询
    range过滤允许我们按照指定范围查找一批数据:
用法:
{
	"range":{
		"age":{
			"gte":20,
			"lt":30
		}
	}
}
范围操作符包括
gt 大于
gte 大于等于
lt 小于
lte 小于等于

示例:
POST /itcast/person/_search
{
		"query":{
				"range":{
					"age":{
						"gte":20,
						"lt":30
					}
				}
		}
}
  1. exists查询
    exists查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的IS_NULL条件
用法:
{
	"exists":{
		"field": "title" # 字段title不为空的数据
	}
}

示例:
POST /itcast/person/_search
{
		"query":{
				"exists":{
				# 查询age字段不为空的文档
					"field":"age"
				}
		}
}
  1. match查询
    match查询是一个标准查询,不管需要全文查询还是精切查询基本上都要用它(可以查需要分词的查询也可以做结构化查询 text keyword都能查)
    如果使用match查询一个全文本字段,他会在真正查询之前用分析器先分析match一下查询字符
用法:
{
	"match":{
		"tweet": "About Search" 
	}
}
{"match":{"age": 26}}
{"match":{"date": "2014-09-01"}}
{"match":{"public": true}}
{"match":{"tag": "full_text"}}

如果用match下制定了一个确切值,在遇到数字,日期,布尔值或者not_analyzed 的字符串时,它将为你搜索你给定的值

示例:
POST /itcast/person/_search
{
		"query":{
				"match":{
				# 全文搜索的(非结构化的)
					"name":"张三"
				}
		}
}
{
		"query":{
				"match":{
				#结构化的
					"age":20
				}
		}
}
  1. 布尔查询 bool查询
    bool查询可以用来合并多个条件查询结果的布尔逻辑值,它包含以下操作符
    must: 多个查询条件的完全匹配 相当于and
    must_not 多个查询条件的相反匹配 相当于not
    should 至少有一个查询条件匹配,相当于or
    这些参数可以分别集成一个查询条件或者一个查询条件的数组
用法:
{
	"bool":{
		"must": {"term":{"folder":"inbox"}},
		"must_not":{"term":{"tag":"spam"}},
		"should":[
			{"term":{"starred":true}},
			{"term":{"unread":true}}
		]
	}
}

举例:
POST /itcast/person/_search
{
	"query":{
		"bool":{
			# 必须包含足球(一个match全文查询)
			"must":{
				"match":{
					"hobby":"足球"
				}
			},
			# 必须不包含音乐(一个match全文查询)
			"must_not":{
				"match":{
					"hobby":"音乐"
				}	
			}
		}
	}
}

过滤查询

前面讲过结构化查询,ElasticSearch也支持过滤查询,如term,range,match等
示例: 查询年龄为20岁的用户

POST /itcast/person/_search
{
	"query":{
		"bool":{
			"filter":{
				"term":{
					"age":20
				}
			}
		}
	}
}

查询和过滤的对比

  • 一条过滤语句会询问每个文档的字段值是否包含着特定值
  • 查询语句会询问每个文档的字段值与特定值的匹配程度如何
    • 一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分_score,并且按照相关性对匹配到的文档进行排序.这种评分方式非常适用于一个没有完全配置结构的全文本搜索
  • 一个简单的文档列表,快速匹配运算并存入内存是十分方便的,每个文档仅需要1个字节,这些缓存的过滤结果集与后续请求的结合使用时非常高效的
  • 查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比过滤语句更耗时,并且查询结果也不可缓存

建议:
做精确匹配搜索时,最好使用过滤语句,因为过滤语句可以缓存数据
做全文搜索时,可以使用查询语句

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值