es数据
重点
- 默认的,一个文档中的每一个属性都是被索引的(有一个倒排索引)和可搜索的,没有倒排索引的属性时不能被检索的。
- 一个搜索默认返回10条结果
- 短语搜索:精确匹配一系列的单词或者短语
GET /megacorp/employee/_search
{
"query": {
"match_phrase": {
"about": "rock climbing"
}
}
}
- 高亮检索,并不能够解决我们的这个显示高亮的数据的想法,高亮显示还是有点用的
- aggs类似于groupby
# https://www.jishux.com/p/ba02e27d28cd9c3b
# 使用聚合需要设置的属性为文本属性
PUT /ecommerce/_mapping/product
{
"properties": {
"tags": {
"type": "text",
"fielddata": true
}
}
}
原理
- 索引实际上是指向一个或者多个物理分片的逻辑命名空间
- 一个分片是一个底层的工作单元,保存了全部数据当中的一部分,一个分片是一个Lucene的实例,以及它本身就是一个完整的搜索引擎
- 一个分片可以是主分片或者副本分片,索引内的任一文档都归属一个主分片,所以主分片的数目决定着索引能够保存的最大数据量
- 技术上来说,一个主分片主打能够存储Integer.MAX_VALUE - 128个文档
- 副本分片只是主分片的拷贝
- 在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改
- 索引在默认情况下会分配5个主分片
- 主分片数目决定了这个索引能够存储的最大数据量,但是读操作–搜索和返回数据可以同时被主分片或者副本分片处理,所以当你拥有越多的副本分片时,也将拥有越高的吞吐量
# 动态改变副本分片的数目
PUT /blogs/_settings
{
"number_of_replicas" : 2
}
丢失了一个节点之后
- 如果是主节点,选举出一个新的主节点,丢失节点的主分片对应的搜索也是不能用的,但是幸运的是,副本分片将会被提升为主分片,这时为yellow状态,需要生成指定数目的副本,变为green
_type
- 允许在索引当中进行逻辑分区,不同的types的文档可能有不同的字段,但是最好能够非常相似
乐观并发控制
- Elasticsearch是分布式的。当文档创建、更新或删除的时候,新的文档必须复制到集群当中的其他节点。Elasticsearch也是异步和并发的,这意味着这些复制请求被并行发送,并且到达目的地时也许顺序是乱的。需要一种方法确保文档的旧版本不会覆盖新的版本.
- index,get,delete请求时每个文档都有一个_version
- 请求可以带上version,如果现在es单重存储的不是请求的版本,会报错,接下来交给程序去处理(乐观并发控制)
搜索
相关概念
- 映射(Mapping): 描述数据在每个字段内如何存储
- 分析(Analysis): 全文是如何处理使之可以被搜索的
- 领域特定查询语言(Query DSL):
分页相关
- 在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是web搜索引擎对任何查询都不要返回超过1000个结果的原因
映射和分析
精确值 vs 全文
分析与分析器
- 字符过滤器:在分词前整理字符串,一个字符过滤器可以用来去掉HTML,或者将&转化为 and
- 分期器:字符串被分词器分为单个的词条
- token过滤器:词条按顺序通过每个token过滤器。这个过程可能会改变词条(例如,小写化Quick),删除词条a, and, the等无用词
什么时候使用分析器
- 索引一个文档,它的全文域被分析成词条用来创建倒排索引。但是我们检索的时候,需要将查询字符串通过相同的分析过程。
- 查询精确值时,不会分析查询字符串,而是搜索你指定的精确值
- 当Elasticsearch在你的文档中检测到一个新的字符串域,它会自动设置其为一个全文字符串域,使用标准分析器对他进行分析
- 如果你想使用一个不同的分析器,必须手动执行这些域的映射
- 当你索引一个包含新域的文档,之前未曾出现,Elasticsearch会使用动态映射,通过JSON当中基本数据类型,尝试猜测域类型。
- 类型的映射
- 域最重要的属性是type,string类型域被认为包含全文
# 测试分析器
GET /_analyze
{
"analyzer": "standard",
"text": "Text to analyze"
}
复杂核心域类型
查询与过滤
- 过滤情况时:不评分或者过滤查询,即这个查询只是简单的问一个问题”这篇文章是否匹配“,yes or no
- 查询情况时:评分查询
处理人类语言
聚合
mapping
PUT cars/_mapping/transactions
{
"properties": {
"make": {
"type": "text",
"fielddata": true
}
}
}
聚合的写法
GET /cars/transactions/_search
{
"size": 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"agg_price": {
"avg": {
"field": "price"
}
},
"make": {
"terms": {
"field": "make"
},
"aggs": {
"min_price": {"min": {"field": "price"}},
"max_price": {"max": {"field": "price"}}
}
}
}
}
}
}
# 更复杂的一个聚合
GET cars/transactions/_search
{
"size": 0,
"aggs": {
"sales": {
"date_histogram": {
"field": "sold",
"interval": "quarter",
"format": "yyyy-MM-dd",
"min_doc_count": 0,
"extended_bounds": {
"min": "2014-01-01",
"max": "2014-12-31"
}
},
"aggs": {
"per_make_sum": {
"terms": {
"field": "make"
},
"aggs": {
"per_make_sum": {
"terms": {
"field": "make"
},
"aggs": {
"sum_price": {
"sum": {
"field": "price"
}
}
}
}
}
}
}
}
}
}
实际当中的一些操作
# 根据id查询数据
GET zhuvideo/video/_search
{
"query": {
"ids": {
"values": ["d98f6049c4eadcd023b029ff07a4174e", "205a27426ef2f3ae8a028ab2c69f6b9c"]
}
}
}
数据建模
父子文档
- 创建的时机
- 创建索引时
- 在子文档type创建之前更新父文档的mapping
- 在执行单文档的请求时,需要指定父文档的ID,因为在索引一个子文档的时候指定了父文档的ID,那么不在使用子文档的ID去索引了,单文档操作只会向存储改文档的分片发送请求
- 执行搜索请求的时候是不需要指定父文档的ID的。因为搜索请求时想索引的所有分片发起请求
- 慎用父子文档,查询速度非常慢
嵌套文档
部署之后
{
"words_en": [
{
"starttime": "00:00:00",
"endtime": "00:12:00",
"words": "hello world! hello",
"words_ori": "hello word"
}
]
}