赞
踩
在es中,索引和文档是REST接口操作的最基本资源,所以对索引和文档的管理也是我们必须要知道的。索引一般是以索引名称出现在REST请求操作的资源路径上,而文档是以文档ID为标识出现在资源路径上。映射类型_doc也可以认为是一种资源,但在es7中废除了映射类型,所以可以_doc也视为一种接口。
在前面的学习中我们已经知道,GET用来获取资源,PUT用来更新资源,DELETE用来删除资源。所以对索引,GET用来查看索引,PUT用来创建索引,DELETE用来删除索引,还有一个HEAD请求,用来检验索引是否存在。除此之外,对索引的管理还有:
GET /_cat/indices?v
也可以通过ElasticSearch提供的接口直接查询:http://192.168.0.101:9200/_cat/indices
关闭索引命令:
POST /open-soft/_close
除了删除索引,还可以选择关闭它们。如果关闭了一个索引,就无法通过Elasticsearch来读取和写人其中的数据,直到再次打开它。在现实世界中,最好永久地保存应用日志,以防要查看很久之前的信息。另一方面,在Elasticsearch中存放大量数据需要增加资源。对于这种使用案例,关闭旧的索引非常有意义。你可能并不需要那些数据,但是也不想删除它们。一旦索引被关闭,它在Elasticsearch内存中唯一的痕迹是其元数据,如名字以及分片的位置。如果有足够的磁盘空间,而且也不确定是否需要在那个数据中再次搜索,关闭索引要比删除索引更好。关闭它们会让你非常安心,永远可以重新打开被关闭的索引,然后在其中再次搜索。
重新打开
POST /open-soft/_open
通过settings参数配置索引,索引的所有配置项都以index开头。索引的管理分为静态设置和动态设置两种。
只能在索引创建时或在状态为closed index(闭合索引)上设置,主要配置索引主分片、压缩编码、路由等相关信息index.number_of_shards主分片数,默认为5;只能在创建索引时设置,不能修改index.shard.check_on_startup是否应在索引打开前检查分片是否损坏,当检查到分片损坏将禁止分片被打开。false:默认值;checksum:检查物理损坏;true:检查物理和逻辑损坏,这将消耗大量内存和CPU;fix:检查物理和逻辑损坏。
有损坏的分片将被集群自动删除,这可能导致数据丢失index.routing_partition_size自定义路由值可以转发的目的分片数。默认为1,只能在索引创建时设置。此值必须小于index.number_of_shards。
index.codec默认使用LZ4压缩方式存储数据,也可以设置为best_compression,它使用DEFLATE方式以牺牲字段存储性能为代价来获得更高的压缩比例。如:
PUT /test1/_settings
{
"index.number_of_shards":3,
"index.codec":"best_compression"
}
通过接口“_settings”进行,同时查询配置也通过这个接口进行,比如:
get _settings
get /open-soft/_settings
get /open-soft,test1/_settings
配置索引则通过:
PUT test1/_settings
{
"refresh_interval": "2s"
}
index.number_of_replicas:每个主分片的副本数,默认为1。
index.auto_expand_replicas:基于可用节点的数量自动分配副本数量,默认为false(即禁用此功能)。
index.refresh_interval:执行刷新操作的频率,默认为1s。可以设置为-1以禁用刷新。
index.max_result_window:用于索引搜索的from+size的最大值,默认为10000。
index.blocks.read_only:设置为true使索引和索引元数据为只读,false为允许写入和元数据更改。
index.blocks.read:设置为true可禁用对索引的读取操作。
index.blocks.write:设置为true可禁用对索引的写入操作。
index.blocks.metadata:设置为true可禁用索引元数据的读取和写入。
index.max_refresh_listeners:索引的每个分片上可用的最大刷新侦听器数。
index.max_docvalue_fields_search:一次查询最多包含开启doc_values字段的个数,默认为100。
index.max_script_fields:查询中允许的最大script_fields数量,默认为32。
index.max_terms_count:可以在terms查询中使用的术语的最大数量,默认为65536。
index.routing.allocation.enable:控制索引分片分配,All所有分片、primaries主分片、new_primaries新创建分片、none不分片。
index.routing.rebalance.enable:索引的分片重新平衡机制。all、primaries、replicas、none。
index.gc_deletes:文档删除后(删除后版本号)还可以存活的周期,默认为60s。
index.max_regex_length:用于正在表达式查询(regex query)正在表达式长度,默认为1000。
通过_mapping接口进行,在我们前面的章节中,已经展示过了。
GET /open-soft/_mapping
或者只看某个字段的属性:
GET /open-soft/_mapping/field/lang
{
"open-soft" : {
"mappings" : {
"lang" : {
"full_name" : "lang",
"mapping" : {
"lang" : {
"type" : "text"
}
}
}
}
}
}
修改映射,当然就是通过put或者post方法了。但是要注意,已经存在的映射只能添加字段或者字段的多类型。但是字段创建后就不能删除,大多数参数也不能修改,可以改的是ignore_above。所以设计索引时要做好规划,至少初始时的必要字段要规划好。
增加文档,我们在前面的章节已经知道了,比如:
PUT /open-soft/_doc/1
{
"name": "Apache Hadoop",
"lang": "Java",
"corp": "Apache",
"stars": 200
}
如果增加文档时,在Elasticsearch中如果有相同ID的文档存在,则更新此文档,比如执行:
PUT /open-soft/_doc/1
{
"name": "Apache Hadoop2",
"lang": "Java8",
"corp": "Apache",
"stars": 300
}
则会发现已有文档的内容被更新了。
当创建文档的时候,如果不指定ID,系统会自动创建ID。自动生成的ID是一个不会重复的随机数。使用GUID算法,可以保证在分布式环境下,不同节点同一时间创建的_id一定是不冲突的。比如:
POST /open-soft/_doc
{
"message": "Hello"
}
GET /open-soft/_doc/1
前面我们用put方法更新了已经存在的文档,但是可以看见他是整体更新文档,如果我们要更新文档中的某个字段怎么办?需要使用_update 接口。
POST /open-soft/_update/1/
{
"doc": {
"year": 2016
}
}
文档中存在year字段,更新year字段的值,如果不存在year字段,则会新增year字段,并将值设为2016。
update接口在文档不存在时提示错误,如果希望在文档不存在时创建文档,则可以在请求中添加upsert 参数或doc_as_upsert参数,例如:
POST /open-soft/_update/5
{
"doc": {
"year": "2020"
},
"upsert": {
"name": "hankin Framework",
"corp": "hankin "
}
}
或者:
POST /open-soft/_update/6
{
"doc": {
"year": "2020"
},
"doc_as_upsert": true
}
upsert参数定义了创建新文档使用的文档内容,而doc_as_upsert参数的含义是直接使用doc参数中的内容作为创建文档时使用的文档内容。
DELETE /open-soft/_doc/1
为了方便我们学习,我们导入kibana为我们提供的范例数据。目前为止,我们已经探索了如何将数据放入Elasticsearch, 现在来讨论下如何将数据从Elasticsearch中拿出来,那就是通过搜索。毕竟,如果不能搜索数据,那么将其放入搜索引擎的意义又何在呢?幸运的是,Elasticsearch提供了丰富的接口来搜索数据,涵盖了Lucene所有的搜索功能。因为Elasticsearch允许构建搜索请求的格式很灵活,请求的构建有无限的可能性。要了解哪些查询和过滤器的组合适用于你的数据,最佳的方式就是进行实验,因此不要害怕在项目的数据上尝试这些组合,这样才能弄清哪些更适合你的需求。
所有的REST搜索请求使用_search接口,既可以是GET请求,也可以是POST请求,也可以通过在搜索URL中指定索引来限制范围。
_search接口有两种请求方法,一种是基于URI的请求方式,另一种是基于请求体的方式,无论哪种,他们执行的语法都是基于DSL(ES为我们定义的查询语言,基于JSON的查询语言),只是形式上不同。我们会基于请求体的方式来学习。比如说:
GET kibana_sample_data_flights/_search
{
"query": {
"match_all": {}
}
}
或
GET kibana_sample_data_flights/_search
{
"query": {
"match_none": {}
}
}
当然上面的查询没什么太多的用处,因为他们分别代表匹配所有和全不匹配。所以我们经常要使用各种语法来进行查询,一旦选择了要搜索的索引,就需要配置搜索请求中最为重要的模块。这些模块涉及文档返回的数量,选择最佳的文档返回,以及配置不希望哪些文档出现在结果中等等。
■ query-这是搜索请求中最重要的组成部分,它配置了基于评分返回的最佳文档,也包括了你不希望返回哪些文档。
■ size-代表了返回文档的数量。
■ from-和size一起使用,from用于分页操作。需要注意的是,为了确定第2页的10项结果,Elasticsearch 必须要计算前20个结果。如果结果集合不断增加,获取某些靠后的翻页将会成为代价高昂的操作。
■ _source指定_ source字段如何返回,默认是返回完整的_ source 字段。通过配置_ source将过滤返回的字段。如果索引的文档很大,而且无须结果中的全部内容,就使用这个功能。请注意,如果想使用它,就不能在索引映射中关闭_ source字段。
■ sort默认的排序是基于文档的得分。如果并不关心得分,或者期望许多文档的得分相同,添加额外的 sort 将帮助你控制哪些文档被返回。
命名适宜的from和size字段,用于指定结果的开始点,以及每“页"结果的 数量。举个例子,如果发送的from值是7,size值是5,那么Elasticsearch将返回第8、9、10、11和12项结果(由于from参数是从0开始,指定7就是从第8项结果开始)。如果没有发送这两个参数,Elasticsearch 默认从第一项结果开始 (第0项结果),在回复中返回10项结果。
例如:
GET kibana_sample_data_flights/_search
{
"from": 100,
"size": 20,
"query": {
"term": {
"DestCountry": "CN"
}
}
}
但是注意,from与size的和不能超过index. max_result_window这个索引配置项设置的值。默认情况下这个配置项的值为10000,所以如果要查询10000条以后的文档,就必须要增加这个配置值。例如,要检索第10000条开始的200条数据,这个参数的值必须要大于10200,否则将会抛出类似“Result window is too
large'的异常。
由此可见,Elasticsearch在使用from和size处理分页问题时会将所有数据全部取出来,然后再截取用户指定范围的数据返回。所以在查询非常靠后的数据时,即使使用了from和size定义的分页机制依然有内存溢出的可能,而max_result_ window设置的10000条则是对Elastiesearch的一.种保护机制。
那么Elasticsearch为什么要这么设计呢?首先,在互联网时代的数据检索应该通过相似度算法,提高检索结果与用户期望的附和度,而不应该让用户在检索结果中自己挑选满意的数据。以互联网搜索为例,用户在浏览搜索结果时很少会看到第3页以后的内容。假如用户在翻到第10000条数据时还没有找到需要的结果,那么他对这个搜索引擎一定会非常失望。
元字段_source中存储了文档的原始数据。如果请求中没有指定_source,Elasticsearch默认返回整个_ source,或者如果_ source 没有存储,那么就只返回匹配文档的元数据:_ id、_type、_index 和_score。
例如:
GET kibana_sample_data_flights/_search
{
"query": {
"match_all": {}
},
"_source": [
"OriginCountry",
"DestCountry"
]
}
你不仅可以返回字段列表,还可以指定通配符。例如,如果想同时返回" DestCountry "和" DestWeather "字段,可以这样配置_ source: "Dest*"。也可以使用通配字符串的数组来指定多个通配符,例如_ source:[" Origin*", "* Weather "]。
GET kibana_sample_data_flights/_search
{
"query": {
"match_all": {}
},
"_source": [
"Origin*",
"*Weather"
]
}
不仅可以指定哪些字段需要返回,还可以指定哪些字段无须返回。比如:
GET kibana_sample_data_flights/_search
{
"_source": {
"includes": [
"*.lon",
"*.lat"
],
"excludes": "DestLocation.*"
}
}
大多搜索最后涉及的元素都是结果的排序( sort )。如果没有指定sort排序选项,Elasticsearch返回匹配的文档的时候,按照_ score 取值的降序来排列,这样最为相关的(得分最高的)文档就会排名在前。为了对字段进行升序或降序排列,指定映射的数组,而不是字段的数组。通过在sort中指定字段列表或者是字段映射,可以在任意数量的字段上进行排序。
例如:
GET kibana_sample_data_flights/_search
{
"from": 100,
"size": 20,
"query": {
"match_all": {}
},
"_source": [
"Origin*",
"*Weather"
],
"sort": [
{
"DistanceKilometers": "asc"
},
{
"FlightNum": "desc"
}
]
}
目前为止所进行的几乎所有搜索请求虽然有些条件限制,但是限制条件主要是在规定ES返回的查询结果中哪些字段返回,哪些字段不返回,对于哪些文档返回,哪些文档不返回,其实没有多少约束。真正如何更精确的找到我们需要的文档呢?这就需要我们需要使用带条件的搜索,主要包括两类,基于词项的搜索和基于全文的搜索。
2.1.1、term查询:
对词项做精确匹配,数值、日期等等,如:
GET kibana_sample_data_flights/_search
{
"query": {
"term": {
"dayOfWeek": 3
}
}
}
对于字符串而言,字符串的精确匹配是指字符的大小写,字符的数量和位置都是相同的,词条(term)查询使用字符的完全匹配方式进行文本搜索,词条查询不会分析(analyze)查询字符串,给定的字段必须完全匹配词条查询中指定的字符串。比如:
GET kibana_sample_data_flights/_search
{
"query": {
"term": {
"OriginCityName": "Frankfurt am Main"
}
}
}
但是如果我们执行:
GET kibana_sample_data_flights/_search
{
"query": {
"term": {
"OriginCityName": "Frankfurt"
}
}
}
结果却是因此可以把term查询理解为SQL语句中where条件的等于号。
terms查询可以把terms查询理解为SQL语句中where条件的in操作符:
GET kibana_sample_data_flights/_search
{
"query": {
"terms": {
"OriginCityName": [
"Frankfurt am Main",
"Cape Town"
]
}
}
}
Elasticsearch在terms查询中还支持跨索引查询,这类似于关系型数据库中的一对多或多对多关系。比如,用户与文章之间就是一对多关系,可以在用户索引中存储文章编号的数组以建立这种对应关系,而将文章的实际内容保存在文章索引中(当然也可以在文章中保存用户ID)。如果想将ID为1的用户发表的所有文章都找出来,在文章索引中查询时为:
POST /open-soft/_search
{
"query": {
"terms": {
"_id": {
"index": "open-soft",
"id": "1",
"path": "articles"
}
}
}
}
在上面的例子中,terms要匹配的字段是id,但匹配值则来自于另一个索引。
这里用到了index、id和path三个参数,它们分别代表要引用的索引、文档ID和字段路径。在上面的例子中,先会到open-soft索引中在找id为1的文档,然后取出articles字段的值与articles索引里的_id做对比,这样就将用户1的所有文章都取出来了。
2.2.1、range查询和exists查询
range查询和过滤器的含义是不言而喻的,它们查询介于一定范围之内的值,适用于数字、日期甚至是字符串。为了使用范围查询,需要指定某个字段的上界和下界值。例如:
GET kibana_sample_data_flights/_search
{
"query": {
"range": {
"FlightDelayMin": {
"gte": 100,
"lte": 200
}
}
}
}
可以查询出延误时间在100~200之间的航班。其中:
gte:大于等于(greater than and equal)
gt:大于(greater than)
lte:小于等于(less than and equal)
lt:大于(less than )
boost:相关性评分(后面的章节会讲到相关性评分)
exists:查询检索字段值不为空的的文档,无论其值是多少,在查询中通过field字段设置检查非空的字段名称,只能有一个。
2.1.3、prefix查询
prefix查询允许你根据给定的前缀来搜索词条,这里前缀在同样搜索之前是没有经过分析的。例如:
GET kibana_sample_data_flights/_search
{
"query": {
"prefix": {
"DestCountry": "C"
}
}
}
找到航班目的国家中所有以C开头的文档。
2.1.4、wildcard查询和regexp查询
wildcard查询就是通配符查询。使用字符串可以让Elasticsearch使用*通配符替代任何数量的字符(也可以不含)或者是使用?通配符替代单个字符。
例如,有5个单词:bacon,barn,ban和baboon、 bam,
“ba*n”的查询会匹配bacon、barn、ban和baboon这是因为‘*’号可以匹配任何字符序列,而查询“ba?n”只会匹配“barn",因为‘?’任何时候都需要匹配一个单独字符。也可以混合使用多个‘*’和‘?’字符来匹配更为复杂的通配模板,比如 f*f?x 就可以匹配 firefox。
GET kibana_sample_data_flights/_search
{
"query": {
"wildcard": {
"Dest": "*Marco*"
}
}
}
使用这种查询时,需要注意的是wildcard查询不像match等其他查询那样轻量级。查询词条中越早出现通配符( *或者? ),Elasticsearch就需要做更多的工作来进行匹配。例如,对于查询词条“h*”,Elasticsearch 必须匹配所有以“h”开头的词条。如果词条是“hi*”,Elasticsearch 只需搜索所有“hi" 开头的词条,这是“h”开头的词条集合的子集,规模更小。考虑到额外开支和性能问题,在实际生产环境中使用wildcard 查询之前,需要先考虑清楚,并且尽量不要让通配符出现在查询条件的第一位。
当然Elasticsearch也支持正则regexp查询,比如:
GET kibana_sample_data_flights/_search
{
"query":{"regexp":{
"字段名":"正则表达式"
}
}
}
词条(term)查询和全文(fulltext)查询最大的不同之处是:全文查询首先分析(Analyze)查询字符串,使用默认的分析器分解成一系列的分词,term1、term2、termN,然后从索引中搜索是否有文档包含这些分词中的一个或多个。
所以,在基于全文的检索里,ElasticSearch引擎会先分析(analyze)查询字符串,将其拆分成小写的分词,只要已分析的字段中包含词条的任意一个,或全部包含,就匹配查询条件,返回该文档;如果不包含任意一个分词,表示没有任何文档匹配查询条件。
这里就牵涉到了ES里很重要的概念,文本分析,当然对应非text类型字段来说,本身不存在文本数据词项提取的问题,所以没有文本分析的问题。
2.2.1、分析什么?
分析( analysis )是在文档被发送并加入倒排索引之前,Elasticsearch在其主体上进行的操作。在文档被加入索引之前,Elasticsearch让每个被分析字段经过一系列的处理步骤。
■字符过滤:使用字符过滤器转变字符。
■文本切分为分词:将文本切分为单个或多个分词。
■分词过滤:使用分词过滤器转变每个分词。
■分词索引:将这些分词存储到索引中。
比如有段话“I like ELK,it include Elasticsearch&LogStash&Kibana”,分析以后的分词为: i like elk it include elasticsearch logstash kibana。
2.2.2、字符过滤
Elasticsearch首先运行字符过滤器(char filter)。这些过滤器将特定的字符序列转变为其他的字符序列。这个可以用于将HTML从文本中剥离,或者是将任意数量的字符转化为其他字符(也许是将“I love u 2”这种缩写的短消息纠正为I love you too。在“I like ELK……”的例子里使用特定的过滤器将“&” 替换为“and”。
2.2.3、切分为分词
在应用了字符过滤器之后,文本需要被分割为可以操作的片段。底层的Lucene是不会对大块的字符串数据进行操作。相反,它处理的是被称为分词(token)的数据。分词是从文本片段生成的,可能会产生任意数量(甚至是0)的分词。例如,在英文中一个通用的分词是标准分词器,它根据空格、换行和破折号等其他字符,将文本分割为分词。在我们的例子里,这种行为表现为将字符串“I like ELK,it include Elasticsearch&LogStash&Kibana”分解为分词 I like ELK it include Elasticsearch and LogStash Kibana。
2.2.4、分词过滤器
一旦文本块被转换为分词,Elasticsearch将会对每个分词运用分词过滤器(token filter)。这些分词过滤器可以将一个分词作为输入,然后根据需要进行修改,添加或者是删除。最为有用的和常用的分词过滤器是小写分词过滤器,它将输入的分词变为小写,确保在搜索词条"nosql"的时候,可以发现关于"NoSq"的聚会。分词可以经过多于1个的分词过滤器,每个过滤器对分词进行不同的操作,将数据塑造为最佳的形式,便于之后的索引。在上面的例子,有3种分词过滤器:第1个将分词转为小写,第2个删除停用词(停止词)"and",第三个将词条"tools" 作为"technologies" 的同义词进行添加。
当分词经历了零个或者多个分词过滤器,它们将被发送到Lucene进行文档的索引。这些分词组成了第1章所讨论的倒排索引。
所有这些不同的部分,组成了一个分析器( analyzer ),它可以定义为零个或多个字符过滤器,1个分词器、零个或多个分词过滤器。Elasticsearch中提供了很多预定义的分析器。我们可以直接使用它们而无须构建自己的分析器。
GET /_analyze
POST /_analyze
GET /<index>/_analyze
POST /<index>/_analyze
可以使用_analyze API来测试analyzer如何解析我们的字符串的,在我们下面的学习中,我们会经常用到这个接口来测试。
因为文本分词会发生在两个地方:
创建索引:当索引文档字符类型为text时,在建立索引时将会对该字段进行分词;
搜索:当对一个text类型的字段进行全文检索时,会对用户输入的文本进行分词。
所以这两个地方都可以对分词进行配置。
ES将按照下面顺序来确定使用哪个分词器:
1)先判断字段是否有设置分词器,如果有,则使用字段属性上的分词器设置;
2)如果设置了analysis.analyzer.default,则使用该设置的分词器;
3)如果上面两个都未设置,则使用默认的standard分词器。
4.3.1、设置索引默认分词器:
PUT test
{
"settings": {
"analysis": {
"analyzer": {
"default": {
"type": "simple"
}
}
}
}
}
还可以为索引配置内置分词器,并修改内置的部分选项修改它的行为:
PUT test2
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"type": "standard",
"stopwords": [
"the", "a","an", "this", "is"
]
}
}
}
}
}
4.3.2、如何为字段指定内置分词器
PUT test3
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard",
"search_analyzer": "simple"
}
}
}
}
4.3.3、甚至还可以自定义分词器
我们综合来看看分词的设置,并且通过_analyzer接口来测试分词的效果:
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"std_english": {
"type": "standard",
"stopwords": "_english_"
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "standard",
"fields": {
"english": {
"type": "text",
"analyzer": "std_english"
}
}
}
}
}
}
我们首先,在索引my_index中配置了一个分析器std_english,std_english中使用了内置分析器 standard,并将standard的停止词模式改为英语模式_english_(缺省是没有的),对字段my_text配置为多数据类型,分别使用了两种分析器,standard和std_english。
POST /my_index/_analyze
{
"field": "my_text",
"text": "The old brown cow"
}
POST /my_index/_analyze
{
"field": "my_text.english",
"text": "The old brown cow"
}
通过上述运行我们可以看到,分析器std_english中的The被删除,而standard中的并没有。这是因为 my_text.english配置了单独的停止词。
文档搜索时使用的分析器有一点复杂,它依次从如下参数中如果查找文档分析器,如果都没有设置则使用standard分析器:
1)搜索时指定analyzer参数。
2)创建索引时指定字段的search_analyzer属性。
3)创建索引时字段指定的analyzer属性。
4)创建索引时setting里指定的analysis.analyzer.default_search。
5)如果都没有设置则使用standard分析器。
比如:搜索时指定analyzer查询参数
GET my_index/_search
{
"query": {
"match": {
"message": {
"query": "Quick foxes",
"analyzer": "stop"
}
}
}
}
指定字段的analyzer和seach_analyzer:
DELETE /my_index
PUT my_index2
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "whitespace",
"search_analyzer":"simple"
}
}
}
}
指定索引的默认搜索分词器:
DELETE /my_index
PUT my_index
{
"settings": {
"analysis": {
"analyzer": {
"default": {
"type": "simple"
},
"default_seach": {
"type": "whitespace"
}
}
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。