gavanwanggw

导航

Elasticsearch 学习笔记


启动
./bin/elasticsearch

索引一条数据
curl  -XPUT 'localhost:9200/megacorp/employee/3' -d '
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}'

「索引」含义的区分
你可能已经注意到索引(index)这个词在Elasticsearch中有着不同的含义,所以有必要在此做一下区分:
1 索引(名词) 如上文所述。一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是 indices 或 indexes 。
2 索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里。以便它能够被检索或者查询。
这非常像SQL中的 INSERT keyword。区别是,假设文档已经存在。新的文档将覆盖旧的文档。


3 倒排索引 传统数据库为特定列添加一个索引,比如B-Tree索引来加速检索。
Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到同样目的。



---------------------------------------检索简单介绍--------------------------------------------------
检索文档

curl  -XGET 'localhost:9200/megacorp/employee/1'

简单搜索
curl  -XGET 'localhost:9200/megacorp/employee/_search'
curl  -XGET 'localhost:9200/megacorp/employee/_search?q=last_name:Smith'


使用DSL语句查询

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}'

复杂的搜索 过滤器(filter)

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
    "query" : {
        "filtered" : {
            "filter" : {
                "range" : {
                    "age" : { "gt" : 30 }
                }
            },
            "query" : {
                "match" : { "last_name" : "smith" }
            }
        }
    }
}'


全文搜索

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}'


这个样例非常好的解释了Elasticsearch怎样在各种文本字段中进行全文搜索。而且返回相关性最大的结果集

短语搜索

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}'


确切的匹配若干个单词或者短语(phrases)

高亮我们的搜索


curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    },
    "highlight": {
        "fields" : {
            "about" : {}
        }
    }
}'

---------------------------------------聚合(aggregations)--------------------------------------------------

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" }
    }
  }
}'

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
  "query": {
    "match": {
      "last_name": "smith"
    }
  },
  "aggs": {
    "all_interests": {
      "terms": {
        "field": "interests"
      }
    }
  }
}'

curl  -XGET 'localhost:9200/megacorp/employee/_search' -d '
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}'


分布式特性
Elasticsearch致力于隐藏分布式系统的复杂性。下面这些操作都是在底层自己主动完毕的:
1 将你的文档分区到不同的容器或者分片(shards)中,它们能够存在于一个或多个节点中。
2 将分片均匀的分配到各个节点,对索引和搜索做负载均衡。
3 冗余每个分片,防止硬件故障造成的数据丢失。


4 将集群中随意一个节点上的请求路由到对应数据所在的节点。
5 不管是添加节点,还是移除节点,分片都能够做到无缝的扩展和迁移。

--------------分布式集群------------


一个节点(node)就是一个Elasticsearch实例,而一个集群(cluster)由一个或多个节点组成,它们具有同样的cluster.name。它们协同工作,分享数据和负载。




做为用户,我们可以与集群中的不论什么节点通信,包含主节点。

每个节点都知道文档存在于哪个节点上。它们可以转发请求到对应的节点上。
我们訪问的节点负责收集各节点返回的数据,最后一起返回给client。这一切都由Elasticsearch处理。

------集群健康------

curl  -XGET 'localhost:9200/_cluster/health'

green 全部主要分片和复制分片都可用
yellow 全部主要分片可用,但不是全部复制分片都可用
red 不是全部的主要分片都可用

------加入索引------
一个分片(shard)是一个最小级别 "工作单元(worker unit)" ,它仅仅是保存了索引中全部数据的一部分。

我们的文档存储在分片中,而且在分片中被索引。可是我们的应用程序不会直接与它们通信。取而代之的是,直接与索引通信。

分片能够是主分片(primary shard)或者是复制分片(replica shard)
curl  -XPUT 'localhost:9200/blogs' -d '
{
   "settings" : {
      "number_of_shards" : 3,
      "number_of_replicas" : 1
   }
}'


在同一个节点上保存同样的数据副本是没有必要的。假设这个节点故障了,那全部的数据副本也会丢失。

Elasticsearch是一个分布式的文档(document)存储引擎。
它能够实时存储并检索复杂数据结构——序列化的JSON文档。


换言说。一旦文档被存储在Elasticsearch中,它就能够在集群的任一节点上被检索。



在Elasticsearch中,每个字段的数据都是默认被索引的。




3.1 索引
_index  类似关系数据库的数据库
_type   类似关系数据库的表
_id     类似关系数据库的主键

PUT /{index}/{type}/{id}
{
  "field": "value",
  ...
}


id能够自己主动生成

3.2 获取数据

curl  -XGET 'localhost:9200/megacorp/employee/1?pretty'

pretty參数 :在随意的查询字符串中添加pretty參数,类似于上面的样例。

会让Elasticsearch美化输出(pretty-print)JSON响应以便更加easy阅读。


通常,GET请求将返回文档的所有,存储在_source參数中。
请求个别字段能够使用_source參数,多个字段能够使用逗号分隔

curl  -XGET 'localhost:9200/megacorp/employee/1?

pretty&_source=first_name,last_name'



仅仅想得到_source字段
curl  -XGET 'localhost:9200/megacorp/employee/1/_source'

检查文档是否存在
curl -i -XHEAD 'localhost:9200/megacorp/employee/1'


这仅仅表示你在查询的那一刻文档不存在

--------创建一个新文档
curl  -XPUT 'localhost:9200/megacorp/employee/3?

op_type=create' -d ' { "first_name" : "Douglas", "last_name" : "Fir", "age" : 35, "about": "I like to build cabinets", "interests": [ "forestry" ] }'


curl  -XPUT 'localhost:9200/megacorp/employee/4/_create'  -d '
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}'


删除文档
curl  -XDELETE 'localhost:9200/megacorp/employee/4'


即使文档不存在——"found"的值是false——_version依然添加了。
这是内部记录的一部分。它确保在多节点间不同操作能够有正确的顺序。



并发控制-处理冲突
乐观并发控制
_version保证全部改动都被正确排序
利用_version的这一长处确保数据不会由于改动冲突而丢失。


我们能够指定文档的version来做想要的更改。
假设那个版本不是如今的,我们的请求就失败了。



检索多个文档
_mget

批量操作
_bulk

-------分布式-----
当我们发送请求,最好的做法是循环通过全部节点请求。这样能够平衡负载。


新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完毕才干拷贝到相关的复制分片上。




routing
replication
consistency

timeout


批量格式
Elasticsearch则是从网络缓冲区中一行一行的直接读取数据。它使用换行符识别和解析action/metadata行,以决定哪些分片来处理这个请求。
这些行请求直接转发到相应的分片上。

这些没有冗余复制,没有多余的数据结构。整个请求过程使用最小的内存在进行。



--------搜索------------
每一个文档里的字段都会被索引并被查询。
在简单查询时,Elasticsearch能够使用全部的索引。以很快的速度返回结果。这让你永远不必考虑传统数据库的一些东西。


映射(Mapping) 数据在每一个字段中的解释说明
分析(Analysis) 全文是怎样处理的能够被搜索的
领域特定语言查询(Query DSL) Elasticsearch使用的灵活的、强大的查询语言


空搜索


多索引和多类别


curl  -XGET 'localhost:9200/megacorp/employee/_search?

q=+first_name:Jane+last_name:Smith' curl -XGET 'localhost:9200/megacorp/employee/_search?

q=Smith'




*** _all字段
Elasticsearch把全部字符串字段值连接起来放在一个大字符串中,它被索引为一个特殊的字段_all

复杂的查询
使用_all的复杂查询
_all field
name字段包括"mary"或"john"
date晚于2014-09-10
_all字段包括"aggregations"或"geo"
+name:(mary john) +date:>2014-09-10 +(aggregations geo)


**查询字符串搜索同意随意用户在索引中不论什么一个字段上执行潜在的慢查询语句,可能暴露私有信息甚至使你的集群瘫痪。**

------映射和分析------

映射(mapping)机制用于进行字段类型确认,将每一个字段匹配为一种确定的数据类型(string, number, booleans, date等)。


分析(analysis)机制用于进行全文文本(Full Text)的分词,以建立供搜索用的反向索引。

index 參数控制字符串以何种方式被索引
{
	"tag": {
		"type": "string",
		"index": "not_analyzed"   
	}
}


analyzed       首先分析这个字符串。然后索引。

换言之。以全文形式索引此字段。
not_analyzed   索引这个字段,使之能够被搜索,可是索引内容和指定值一样。不分析此字段。
no             不索引这个字段。这个字段不能为搜索到。

对于analyzed类型的字段


确切值(Exact values) vs. 全文文本(Full text)




**
分析(analysis)是分析器(analyzer)完毕的。
一个分析器(analyzer)仅仅是一个包装用于将三个功能放到一个包里
1 字符过滤器  首先字符串经过字符过滤器(character filter)。它们的工作是在表征化(断词)前处理字符串。字符过滤器可以去除HTML标记。或者转换 "&" 为 "and" 。


2 分词器      被表征化(断词)为独立的词。一个简单的分词器(tokenizer)能够依据空格或逗号将单词分开(这个在中文中不适用)。
3 表征过滤    最后,每一个词都通过全部表征过滤(token filters)。
             它能够改动词(比如将 "Quick" 转为小写)。去掉词(比如停用词像 "a" 、 "and"``"the" 等等)。或者添加词(比如同义词像 "jump" 和 "leap" )


Elasticsearch提供非常多开箱即用的字符过滤器,分词器和表征过滤器。这些能够组合来创建自己定义的分析器以应对不同的需求。


内建的分析器
1 标准分析器
2 简单分析器
3 空格分析器
4 语言分析器

測试分析器
curl  -XGET 'localhost:9200/_analyze?

analyzer=standard'

指定分析器

自己主动探測字段的情况:
当Elasticsearch在你的文档中探測到一个新的字符串字段,它将自己主动设置它为全文 string 字段并用 standard 分析器分析。




所以须要人工映射字段

当你索引一个包括新字段的文档(一个之前没有的字段)

Elasticsearch将使用动态映射推測字段类型,这类型来自于JSON的基本数据类型,使用下面规则:
Boolean: true or false			"boolean"
Whole number: 123			"long"
Floating point: 123.45			"double"
String, valid date: "2014-09-15"	"date"
String: "foo bar"			"string"

添加新的映射字段
PUT /gb/_mapping/tweet
{
	"properties" : {
		"tag" : {
		"type" : "string",
		"index": "not_analyzed"
		}
	}
}


你能够在第一次创建索引的时候指定映射的类型。

此外,你也能够晚些时候为新类型加入映射
你能够向已有映射中添加字段,但你不能改动它。
假设一个字段在映射中已经存在。这可能意味着那个字段的数据已经被索引。
假设你改变了字段映射,那已经被索引的数据将错误而且不能被正确的搜索到。
****我们能够更新一个映射来添加一个新字段,可是不能把已有字段的类型那个从 analyzed 改到 not_analyzed 。

(改动了之后数据不会依照原有想法被查询到)

查看映射

curl  -XGET 'localhost:9200/megacorp/_mapping/employee/'

** 复合核心字段类型


1 数组  :对于数组不须要特殊的映射。不论什么一个字段能够包括零个、一个或多个值,相同对于全文字段将被分析并产生多个词。


        数组中全部值必须为同一类型。索引是没有顺序的,在搜索阶段你不能指定"第一个值"或者"最后一个值"。




2 空字段  Lucene没法存放 null 值,所以一个 null 值的字段被觉得是空字段。
        这四个字段将被识别为空字段而不被索引:
"empty_string": "",
"null_value": null,
"empty_array": [],
"array_with_null_value": [ null ]

3 内部对象 - 怎样被索引 - 扁平化文件


{
    "tweet": "Elasticsearch is very flexible",
    "user": {
        "id": "@johnsmith",
        "gender": "male",
        "age": 26,
        "name": {
            "full": "John Smith",
            "first": "John",
            "last": "Smith"
        }
    }
}

索引的时候,一个 Lucene 文件包括一个键-值相应的扁平表单。
"user.name.full": [john, smith],
"user.name.first": [john],
"user.name.last": [smith]

********查询与过滤


** 结构化查询 DSL
1. 查询字句
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    }
}

2. 合并多个字句  : 以合并简单的子句为一个复杂的查询语句
{
	"bool": {
	   "must": { "match": { "tweet": "elasticsearch" }},
	   "must_not": { "match": { "name": "mary" }}, 
	   "should": { "match": { "tweet": "full text" }}
	}
}

** 查询与过滤
过滤语句会询问每一个文档的字段值是否包括着特定值
        查询语句会询问每一个文档的字段值与特定值的匹配程度怎样
重点:      
做精确匹配搜索时,你最好用过滤语句。由于过滤语句能够缓存数据

** 过滤
1 term 过滤
term 主要用于精确匹配哪些值。比方数字,日期。布尔值或 not_analyzed 的字符串

	{ "term": { "age": 26 }}

精准查找使用过滤器,过滤器的重要性在于它们很的快。


过滤器顺序
假如条件 A 匹配 1000 万个文档,而 B 仅仅匹配 100 个文档。那么须要将 B 放在 A 前面。


term 过滤器本身并不能起作用,为了使用 term 过滤器,我们须要将它包括在一个过滤查询语句中:
{
    "query": {
        "filtered": {  -- filtered 查询同一时候接受接受 query 与 filter
            "query": {
                "match_all": { }  -- match_all 用来匹配全部文档
            },
            "filter": {
                "term": {  -- term 过滤器
                    "price": 20
                }
            }
        }
    }
}

term过滤字符串的时候,字段须要为 not_analyzed

2 terms 过滤
{
    "terms": {
        "tag": [ "search", "full_text", "nosql" ]
    }
}



-- 完整的。也要放在 filtered 查询中
{
    "query": {
        "filtered": {
            "filter": {
                "terms": {
                    "price": [ 20, 30 ]
                }
            }
        }
    }
}

3 range 过滤
{
    "range": {
        "age": {
            "gte": 20,
            "lt": 30
        }
    }
}


支持日期数学操作
{
    "range": {
        "timestamp": {
            "gt": "now-1h"   -- 找出全部时间戳大于当前时间减 1 小时的文档
        }
    }
}


"2014-01-01 00:00:00||+1M" : 2014 年 1 月 1 号加一个月

4 exists 和 missing 过滤


{
    "exists": {
        "field": "title"
    }
}


仅仅是针对已经查出一批数据来,可是想区分出某个字段是否存在的时候使用


5 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 }}
	  ]
	}
}

** 查询


1 match_all 查询


2 match 查询


match 查询一个全文本字段
{
    "match": {
        "tweet": "About Search"
    }
}


match 下指定了一个确切值

{ "match": { "age": 26 }}


3 multi_match 查询 

{
    "multi_match": {
        "query": "full text search",
        "fields": [ "title", "body" ]
    }
}


同一时候搜索title 和 body 这2个字段


4 bool 查询   合并多个查询子句


{
    "bool": {
        "must": {
            "match": {  "title": "how to make millions" }
        },
        "must_not": {
            "match": { "tag": "spam"}
        },
        "should": [
            { "match": {"tag": "starred" } },
            { "range": { "date": {"gte": "2014-01-01"} } }
        ]
    }
}

** 查询与过滤条件的合并


1 带过滤的查询语句 , filtered 来同一时候包括 "query" 和 "filter" 子句
{
    "filtered": {
        "query": {
            "match": { "email": "business opportunity" }
        },
        "filter": {
            "term": { "folder": "inbox" }
        }
    }
}


{
    "filtered": {
        "query": { "match_all": {}}, -- 查询语句能够省略 相当于 match all
        "filter": {
            "term": { "folder": "inbox" }
        }
    }
}

-------------------------------排序--------------------------------


GET /_search
{
    "query": {
        "filtered": {
            "filter": {
                "term": {"user_id": 1 }
            }
        }
    },
    "sort": {
        "date": { "order": "desc" }
    }
}


_score


多级排序
"sort": [
   { "date":   { "order": "desc" }},
   { "_score": { "order": "desc" }}
]



被分析器(analyser)处理过的字符称为 analyzed field (译者注:即已被分词并排序的字段。全部写入ES中的字段默认圴会被analyzed),
analyzed 字符串字段同一时候也是多值字段,在这些字段上排序往往得不到你想要的值。
比方你分析一个字符"fine old art",它终于会得到三个值。


比如我们想要依照第一个词首字母排序,假设第一个单词同样的话,再用第二个词的首字母排序,以此类推,
可惜ElasticSearch在进行排序时是得不到这些信息的。




**********
相关性
ElasticSearch的相似度算法被定义为 TF/IDF。即检索词频率/反向文档频率


检索词频率
检索词在该字段出现的频率。出现频率越高。相关性也越高。


反向文档频率
检索词在索引中出现的频率,频率越高。相关性越低。


检索词出如今多数文档中会比出如今少数文档中的权重更低

------------------------索引管理---------------------------

1 创建索引 
简单的通过加入一个文档的方式创建了一个索引。这个索引使用默认设置,新的属性通过动态映射加入到分类中。
如今我们须要对这个过程有很多其它的控制:我们须要确保索引被创建在适当数量的分片上,在索引数据之前设置好分析器和类型映射。

禁止自己主动创建索引:
 - config/elasticsearch.yml
 - action.auto_create_index : false


2 删除索引

3 索引设置
number_of_shards
定义一个索引的主分片个数。默认值是 5 。

这个配置在索引创建后不能改动。


number_of_replicas
每一个主分片的复制分片个数,默认是 1 。这个配置能够随时在活跃的索引上改动。

4 配置分析器

standard 分析器 : 大部分西方语系
whitespace :
simple :
english :

"name": {
   "type": "string",
   "analyzer": "whitespace"
}

不同类型的文档能够被加到同一个索引里

*********动态映射


dynamic  true   自己主动加入字段(默认)
         false  忽略字段
         strict  当遇到未知字段时抛出异常


{
    "mappings": {
        "my_type": {
            "dynamic": "strict", -- 当遇到未知字段时。 my_type 对象将会抛出异常
             "properties": {
                "title": {
                    "type": "string"
                },
                "stash": {
                    "type": "object",
                    "dynamic": true -- stash 对象会自己主动创建字段
                }
            }
        }
    }
}


自己定义动态映射


动态模板 ****
    使用 dynamic_templates ,你能够全然控制新字段的映射,你设置能够通过字段名或数据类型应用一个全然不同的映射。



样例


{
    "mappings": {
        "my_type": {
            "dynamic_templates": [
                {
                    "es": {  -- 名称
                        "match": "*_es",   -- 字段名以 _es 结尾须要使用 spanish 分析器
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "string",
                            "analyzer": "spanish"
                        }
                    }
                },
                {
                    "en": {
                        "match": "*",  -- 全部其它字段使用 english 分析器。 匹配全部字符串类型字段
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "string",
                            "analyzer": "english"
                        }
                    }
                }
            ]
        }
    }
}


match_mapping_type  :同意你限制模板仅仅能使用在特定的类型上 (比如 strong 和 long )

match       :仅仅匹配字段名, 
path_match  :匹配字段在一个对象中的完整路径

_default_ 默认映射


{
    "mappings": {
        "_default_": {
            "_all": { "enabled": false }  -- 映射对全部类型禁用 _all 字段
        },
        "blog": {
            "_all": { "enabled": true }    -- 仅仅在 blog 字段上开启 _all 字段
        }
    }
}



_default_ 映射也是定义索引级别的动态模板的好地方

重建索引


能够给索引加入新的类型,或给类型加入新的字段。可是你不能加入新的分析器或改动已有字段
可是能够通过重建索引来完毕

---重点查询中的聚合
统计每种兴趣下职员的平均年龄


GET /megacorp/employee/_search


{
    "aggs": {
        "all_interests": {
            "terms": {
                "field": "interests"
            },
            "aggs": {
                "avg_age": {
                    "avg": {
                        "field": "age"
                    }
                }
            }
        }
    }
}


基于短语(Term-based)的查询
全文(Full-text)检索

----------了解部分--------------------


文档到分片


shard = hash(routing) % number_of_primary_shards


routing 通常是 _id 字段


主分片的数量仅仅能在创建索引时定义且不能改动,假设主分片的数量在未
来改变了,全部先前的路由值就失效了。文档也就永远找不到了。


主分片  复制分片


master 请求节点


文档可以从主分片或随意一个复制分片被检索。

-------------分布式搜索-----了解就可以-
查询阶段  
取回阶段 



管理命令

查看索引
curl 192.168.1.201:9200/_cat/indices?v

删除索引


curl  -XDELETE '192.168.1.201:9200/razor_cd'


curl  -XDELETE '192.168.1.201:9200/razor_event'


curl  -XDELETE '192.168.1.201:9200/razor_usinglog'


curl  -XDELETE '192.168.1.201:9200/razor_app'


curl  -XDELETE '192.168.1.201:9200/razor_tag'

curl  -XGET '192.168.1.201:9200/razor_cd/clientdata/_search?pretty=true'


curl  -XGET '192.168.1.201:9200/razor_app/app_info/_search?pretty=true'


curl  -XGET '192.168.1.201:9200/razor_tag/app_tag/_search?pretty=true'


curl  -XGET '192.168.1.201:9200/razor_event/event/_search?

pretty=true' curl -XGET '192.168.1.201:9200/razor_usinglog/usinglog/_search?

pretty=true'


curl  -XGET '192.168.1.201:9200/razor_app/app_info/_search?

pretty=true' -d ' { "query": { "range": { "localtime": { "gte": "2016-03-28 00:00:00", "lt": "2016-03-28 10:08:00" } } } }'



curl  -XGET '192.168.1.201:9200/razor_app/app_info/_search?pretty=true' -d '
{
    "query": {
        "match": {
            "user_id": "0c5ead1ca4bda378f0784440eda92700"
        }
    }
}'

curl  -XGET '192.168.1.201:9200/razor_cd/clientdata/_search?pretty=true' -d '
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "user_id": "a8013699209"
          }
        },
        {
          "match": {
            "product_id": "10028147"
          }
        }
      ]
    }
  },
  "sort": {
    "localtime": {
      "order": "desc"
    }
  },
  "from": 0,
  "size": 100
}
'





posted on 2017-07-25 14:56  gavanwanggw  阅读(170)  评论(0编辑  收藏  举报