Elasticsearch学习笔记-基础入门

397 阅读6分钟

1、检查文档是否存在

如果只想检查一个文档是否存在--根本不想关心内容,那么用 HEAD 方法来代替 GET 方法。 HEAD 请求没有返回体,只返回一个 HTTP 请求报头:

#若文档不存在, Elasticsearch 将返回一个 404 Not Found 的状态码
$ curl -i -XHEAD http://localhost:9200/trade/share/1
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=UTF-8
Content-Length: 0

#如果文档存在, Elasticsearch 将返回一个 200 OK 的状态码
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 0

2、文档的部分更新

#1> update 请求最简单的一种形式是接收文档的一部分作为 doc 的参数,覆盖现有的字段,增加新的字段。 l例如,我们增加字段 views
$ curl -XPOST 'http://localhost:9200/trade/share/1/_update' -d '
{
   "doc" : {
      "views": 0
   }
}'
#响应
{"_index":"trade","_type":"share","_id":"1","_version":9}


#2> 使用脚本部分更新。脚本可以在 update API中用来改变 _source 的字段内容, 它在更新脚本中称为 ctx._source 。 例如,我们可以使用脚本来增加 views 的数量
$ curl -XPOST 'http://localhost:9200/trade/share/1/_update' -d '
{
   "script" : "ctx._source.views+=1"
}'
#响应
{"_index":"trade","_type":"share","_id":"1","_version":10}


#3> 使用脚本给 view 动态增加数量
$ curl -XPOST 'http://localhost:9200/trade/share/1/_update' -d '
{
   "script" : "ctx._source.views+=view_num",
   "params" : {
      "view_num" : 5
   }
}'
#响应
{"_index":"trade","_type":"share","_id":"1","_version":11}


#4> 通过设置 ctx.op 为 delete 来删除文档
$ curl -XPOST 'http://localhost:9200/trade/share/2/_update' -d '
{
   "script" : "ctx.op = ctx._source.views == count ? 'delete' : 'none'",
    "params" : {
        "count": 1
    }
}'
#响应
{"_index":"trade","_type":"share","_id":"2","_version":2}


#5> 使用 upsert 参数,指定如果文档不存在就会先创建它
#注:当文档存在时,执行脚本;当文档不存在时,upsert中的内容会插入到对应的文档中,而doc_as_upsert可以在文档不存在的时候,把doc中的内容插入到文档中。
$ curl -XPOST 'http://localhost:9200/trade/share/3/_update' -d '
{
   "script" : "ctx._source.views+=view_num",
   "params" : {
      "view_num" : 1
   },
   "upsert": {
       "views": 1
   }
}'
#响应
{"_index":"trade","_type":"share","_id":"3","_version":1}


#6> 设置参数 retry_on_conflict, 这个参数规定了失败之前 update 应该重试的次数,它的默认值为 0 。
$ curl -XPOST 'http://localhost:9200/trade/share/3/_update?retry_on_conflict=3' -d '
{
   "script" : "ctx._source.views+=view_num",
   "params" : {
      "view_num" : 1
   },
   "upsert": {
       "views": 1
   }
}'
#批量操作冲突重试
$ curl -XPOST 'http://localhost:9200/_bulk' -d '
{ "update": { "_index": "trade", "_type": "share", "_id": "3", "_retry_on_conflict" : 3} }
{ "doc" : {"views" : "2"} }
'

3、获取多个文档

如果需要从 Elasticsearch 检索很多文档,那么使用 multi-get 或者 mget API 来将这些检索请求放在一个请求中,可以避免单独处理每个请求花费的网络延时和开销,将比逐个文档请求更快地检索到全部文档。

1> mget API 要求有一个 docs 数组作为参数,每个元素包含需要检索文档的元数据, 包括 _index 、 _type 和 _id 。如果检索一个或者多个特定的字段,那么可以通过 _source 参数来指定这些字段的名字
$ curl -XGET 'http://localhost:9200/_mget' -d '
{
   "docs" : [
      {
         "_index" : "trade",
         "_type" :  "share",
         "_id" :    1
      },
      {
         "_index" : "trade",
         "_type" :  "share2",
         "_id" :    296,
         "_source": "brand_name"
      }
   ]
}'
#响应
{
    "docs": [
        {
            "_index": "trade",
            "_type": "share",
            "_id": "1",
            "_version": 11,
            "found": true,
            "_source": {
                "name": "share4",
                "age": "120",
                "id": "1",
                "views": 6
            }
        },
        {
            "_index": "trade",
            "_type": "share2",
            "_id": "296",
            "_version": 6,
            "found": true,
            "_source": {
                "brand_name": "VISEART"
            }
        }
    ]
}


2> 如果想检索的数据都在相同的 _index 中(甚至相同的 _type 中),则可以在 URL 中指定默认的 /_index 或者默认的 /_index/_type
$ curl -XGET 'http://localhost:9200/trade/_mget' -d '
{
   "docs" : [
      {
         "_type" :  "share",
         "_id" :    1
      },
      {
         "_type" :  "share2",
         "_id" :    296,
         "_source": "brand_name"
      }
   ]
}'


3> 如果所有文档的 _index 和 _type 都是相同的,可以只传一个 ids 数组,而不是整个 docs 数组
$ curl -XGET 'http://localhost:9200/trade/share/_mget' -d '
{
   "ids" : [ "1", "2" ]
}'
#响应
{
    "docs": [
        {
            "_index": "trade",
            "_type": "share",
            "_id": "1",
            "_version": 11,
            "found": true,
            "_source": {
                "name": "share4",
                "age": "120",
                "id": "1",
                "views": 6
            }
        },
        {
            "_index": "trade",
            "_type": "share",
            "_id": "2",
            "_version": 2,
            "found": true,
            "_source": {
                "id": "2",
                "name": "yuan",
                "age": "18",
                "views": 1
            }
        }
    ]
}

4、批量操作

与 mget 可以使我们一次取回多个文档同样的方式, bulk API 允许在单个步骤中进行多次 create 、 index 、 update 或 delete 请求。
{ action: { metadata }}\n
{ request body}\n
{ action: { metadata }}\n
{ request body}\n
...
1> 	每行一定要以换行符(\n)结尾,包括最后一行。这些行不能包含未转义的换行符,因为他们将会对解析造成干扰。
2> 	action/metadata 行指定 哪一个文档 做什么操作 
3> 	action 必须是以下选项之一
	create
	如果文档不存在,那么就创建它。
	index
	创建一个新文档或者替换一个现有的文档。
	update
	部分更新一个文档。
	delete
	删除一个文档。删除操作不需要 request body 行。
	
$ curl -XPOST 'http://localhost:9200/_bulk' -d '
{ "delete": { "_index": "trade", "_type": "share", "_id": "1" }} 
{ "create": { "_index": "trade", "_type": "share", "_id": "2" }}
{ "views":    "5" }
{ "index":  { "_index": "trade", "_type": "share" }}
{ "views":    "0" }
{ "update": { "_index": "trade", "_type": "share", "_id": "3", "_retry_on_conflict" : 3} }
{ "doc" : {"views" : "2"} }
'
1> 每个子请求都是独立执行,因此某个子请求的失败不会对其他子请求的成功与否造成影响。 如果其中任何子请求失败,最顶层的 error 标志被设置为 true ,并且在相应的请求报告出错误明细。
2> bulk 请求不是原子的, 不能用它来实现事务控制。每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求。

5、分页

Elasticsearch 接受 from 和 size 参数进行分页
size
显示应该返回的结果数量,默认是 10
from
显示应该跳过的初始结果数量,默认是 0

如果每页展示 20 条结果,可以用下面方式请求得到 1 到 3 页的结果
GET /_search?size=20&from=0
GET /_search?size=20&from=20
GET /_search?size=20&from=40

6、测试分析器

$ curl -XGET 'http://localhost:9200/_analyze' -d '
{
  "analyzer": "standard",
  "text": "Text to analyze"
}'
#响应
{
    "tokens": [
        {
            "token": "text",
            "start_offset": 0,
            "end_offset": 4,
            "type": "<ALPHANUM>",
            "position": 0
        },
        {
            "token": "to",
            "start_offset": 5,
            "end_offset": 7,
            "type": "<ALPHANUM>",
            "position": 1
        },
        {
            "token": "analyze",
            "start_offset": 8,
            "end_offset": 15,
            "type": "<ALPHANUM>",
            "position": 2
        }
    ]
}
token 是实际存储到索引中的词条。 position 指明词条在原始文本中出现的位置。 start_offset 和 end_offset 指明字符在原始字符串中的位置。

7、映射

Elasticsearch 支持如下简单域类型:
字符串: string
整数: byte, short, integer, long
浮点数: float, double
布尔型: boolean
日期: date

注:如果通过引号( "123" )索引一个数字,它会被映射为 string 类型,而不是 long 。但是,如果这个域已经映射为 long,那么 Elasticsearch 会尝试将这个字符串转化为 long ,如果无法转化,则抛出一个异常。

Elasticsearch还支持复杂域类型:null 值,数组,和对象

查看映射
$ curl -XGET 'http://localhost:9200/trade/share/_mapping'
{
    "trade": {
        "mappings": {
            "share": {
                "properties": {
                    "age": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string"
                    },
                    "name": {
                        "type": "string"
                    },
                    "origin": {
                        "type": "long"
                    },
                    "views": {
                        "type": "long"
                    }
                }
            }
        }
    }
}


域最重要的属性是 type 。对于不是 string 的域,一般只需要设置 type。string 类型域默认会被认为包含全文。string 域映射的两个最重要属性是 index 和 analyzer

1> index 属性控制怎样索引字符串。它可以是下面三个值
analyzed
	首先分析字符串,然后索引它。换句话说,以全文索引这个域。
not_analyzed
  	索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
no
	不索引这个域。这个域不会被搜索到。
string 域 index 属性默认是 analyzed 。如果我们想映射这个字段为一个精确值,我们需要设置它为 not_analyzed

2> analyzer
对于 analyzed 字符串域,用 analyzer 属性指定在搜索和索引时使用的分析器。

8、请求体查询

1> 对于一个查询请求,Elasticsearch的search API同时支持 GET 和 POST 请求

2> 验证查询
使用 validate-query API 可以用来验证查询是否合法
$ curl -XGET 'http://localhost:9200/trade/share/_validate/query' -d '
{
   "query": {
      "name" : {
         "match" : "女士 手表"
      }
   }
}'
#响应
{
    "valid": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    }
}

可以将 explain 参数 加到查询字符串中显示查询不合法的原因
$ curl -XGET 'http://localhost:9200/trade/share/_validate/query?explain' -d '
{
   "query": {
      "name" : {
         "match" : "女士 手表"
      }
   }
}'
#响应
{
    "valid": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    },
    "explanations": [
        {
            "index": "trade",
            "valid": false,
            "error": "org.elasticsearch.index.query.QueryParsingException: [trade] No query registered for [name]"
        }
    ]
}

对于合法查询,使用 explain 参数将返回可读的描述
$ curl -XGET 'http://localhost:9200/trade/share/_validate/query?explain' -d '
{
   "query": {
      "match" : {
         "name" : "女士 手表"
      }
   }
}'
#响应
{
    "valid": true,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    },
    "explanations": [
        {
            "index": "trade",
            "valid": true,
            "explanation": "filtered(name:女士 name:手表)->cache(+_type:share +org.elasticsearch.index.search.nested.NonNestedDocsFilter@ed9142ed)"
        }
    ]
}

9、索引管理

1> 创建索引
$ PUT /index_one
{
    "settings": { ... any settings ... },
    "mappings": {
        "type_one": { ... any mappings ... },
        "type_two": { ... any mappings ... },
        ...
    }
}
可以通过在 config/elasticsearch.yml 的每个节点下添加下面的配置禁止自动创建索引
action.auto_create_index: false

2> 删除索引
#删除单个索引
$ DELETE /index_one
#删除多个
$ DELETE /index_one,index_two
$ DELETE /index_*
#删除所有索引
$ DELETE /_all
$ DELETE /*
如果要避免意外的大量删除, 可以在你的 elasticsearch.yml 做如下配置
action.destructive_requires_name: true
这个设置使删除只限于特定名称指向的数据, 而不允许通过指定 _all 或通配符来删除指定索引库。

3> 索引设置
$ PUT /index_one
{
    "settings": {
        "number_of_shards" :   1,
        "number_of_replicas" : 0
    }
}
number_of_shards
每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。
number_of_replicas
每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。

#用 update-index-settings API 动态修改副本数
$ PUT /index_one/_settings
{
    "number_of_replicas": 1
}

4> 自定义分析器
一个分析器就是在一个包里面组合了三种函数的一个包装器,三种函数按照顺序被执行
a、字符过滤器(Character filters)
字符过滤器用来整理一个尚未被分词的字符串。例如,如果我们的文本是HTML格式的,它会包含像 <p> 或者 <div> 这样的HTML标签,这些标签是我们不想索引的。可以使用html清除字符过滤器来移除掉所有的HTML标签。一个分析器可能有0个或者多个字符过滤器。
b、分词器(Tokenizer)
一个分析器必须有一个唯一的分词器。 分词器把字符串分解成单个词条或者词汇单元。 
c、词单元过滤器
经过分词,作为结果的词单元流会按照指定的顺序通过指定的词单元过滤器。词单元过滤器可以修改、添加或者移除词单元。

#创建自定义分析器
$ PUT /test
{
    "settings": {
        "analysis": {
            "char_filter": { ... custom character filters ... }, #自定义的字符过滤器
            "tokenizer":   { ...    custom tokenizers     ... }, #自定义的分词器
            "filter":      { ...   custom token filters   ... }, #自定义的token过滤器
            "analyzer":    { ...    custom analyzers      ... } #自定义的分析器,可以将上面的char_filter、tokenizer、filter用不同的组合拼起来,形成不同的分析器
        }
    }
}
#例
$ curl -XPUT 'http://localhost:9200/trade' -d '
{
    "settings": {
        "analysis": {
            "filter": {
                "share_length_filter": { #自定义的oken过滤器名称
                    "type": "length", #类型
                    "min": 1, 
                    "max": 3
            }},
            "analyzer": {
                "share_analyzer": { #自定义的分析器名称
                    "type": "custom", #类型
                    "tokenizer": "standard", #分词器
                    "filter": [ "lowercase", "share_length_filter" ] #使用自定义的token过滤器share_length_filter
            }}
        }
    }
}'
#修改分析器
如果直接修改,会出现如下报错
{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"Can't update non dynamic settings...
不支持动态设置,indecis处于开启状态,需要先关闭,再进行设置,设置完成后在打开。这种通过API设置的方式不需要重启Elsatisearch。
$ curl -XPOST 'localhost:9200/trade/_close'
$ curl -XPUT 'http://localhost:9200/trade/_settings' -d '
{
    "settings": {
        "analysis": {
            "filter": {
                "share_length_filter": {
                    "type": "length",
                    "min": 1, 
                    "max": 3
                },
                "share_shingle_filter": {
                    "type": "shingle",
                    "output_unigrams": "false"
                },
                "share_stopwords_filter": {
                    "type": "stop",
                    #"stopwords": ["this", "a"], #直接配置停用词
                    "stopwords_path": "stopwords/english.txt" #停用词还可以使用一行一个单词的格式保存在文件中。此文件必须在集群的所有节点上,并且通过 stopwords_path 参数设置路径,该路径相对于 Elasticsearch 的 config 目录。
                }
            },
            "analyzer": {
                "share_analyzer": {
                    "type": "custom",
                    "tokenizer": "standard",
                    "filter": [ "lowercase", "share_length_filter", "share_shingle_filter" ]
            }}
        }
    }
}'
$ curl -XPOST 'localhost:9200/trade/_open'

#索引被创建以后,使用 analyze API 来测试新的分析器:
$ curl -XGET 'http://localhost:9200/trade/_analyze' -d '
{
  "analyzer": "share_analyzer",
  "text" : "This is a test"
}'

#将分析器应用到指定的字段上
$ curl -XPUT 'http://localhost:9200/trade/share/_mapping' -d '
{
    "properties": {
        "title": {
            "type":      "string",
            "analyzer":  "share_analyzer"
        }
    }
}'

5> 动态映射
当 Elasticsearch 遇到文档中以前未遇到的字段,它用 dynamic mapping 来确定字段的数据类型并自动把新的字段添加到类型映射。
如果期望遇到新字段就会抛出异常,这样能及时发现问题,可以用 dynamic 配置来控制这种行为,选项如下:
true
	动态添加新的字段—​缺省
false
	忽略新的字段
strict
	如果遇到新字段抛出异常	
配置参数 dynamic 可以用在根 object 或任何 object 类型的字段上。可以将 dynamic 的默认值设置为 strict , 而只在指定的内部对象中开启它, 例如:
$ curl -XPUT 'http://localhost:9200/trade' -d '
{
    "mappings": {
        "share": {
            "dynamic": "strict",  #如果遇到新字段,对象 share 就会抛出异常。
            "properties": {
                "title": {"type": "string"},
                "stash": { #而内部对象 stash 遇到新字段就会动态创建新字段。
                    "type": "object",
                    "dynamic": true 
                }
            }
        }
    }
}'

6> 自定义动态映射
#日期检测
当 Elasticsearch 遇到一个新的字符串字段时,它会检测这个字段是否包含一个可识别的日期,比如 2014-01-01 。如果它像日期,这个字段就会被作为 date 类型添加。否则,它会被作为 string 类型添加。
日期检测可以通过在根对象上设置 date_detection 为 false 来关闭:
$ PUT /trade
{
    "mappings": {
        "share": {
            "date_detection": false
        }
    }
}

#动态模板
使用 dynamic_templates ,可以完全控制新检测生成字段的映射。模板按照顺序来检测;第一个匹配的模板会被启用。
$ PUT /trade
{
    "mappings": {
        "share": {
            "dynamic_templates": [
                {
                    "es": { #模板名称
                        "match": "*_es", #匹配字段名以 _es 结尾的字段
                        "match_mapping_type": "string", #匹配映射的类型,如string,long等
                        "mapping": { #类型映射定义
                            "type": "string", #最终转换成的类型
                            "analyzer": "spanish"
                        }
                    }
                },
                {
                    "en": {
                        "match": "*",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "string",
                            "analyzer": "english"
                        }
                    }
                }
            ]
        }
    }
}
match 参数只匹配字段名称, path_match 参数匹配字段在对象上的完整路径,所以 address.*.name 将匹配这样的字段:{"address":{"city":{"name":"New York"}}} 
unmatch 和 path_unmatch将被用于未被匹配的字段。

7> 缺省映射(_default_)
在设置 _default_ 映射之后创建的所有类型都将应用这些缺省的设置,除非类型在自己的映射中明确覆盖这些设置。
例如 _default_ 映射为所有的类型禁用 _all 字段, 而只在 blog 类型启用:
$ PUT /trade
{
    "mappings": {
        "_default_": {
            "_all": {
                "enabled": false
            }
        },
        "blog": {
            "_all": {
                "enabled": true
            }
        }
    }
}

8> 重新索引
为了有效的重新索引所有在旧的索引中的文档,用 scroll 从旧的索引检索批量文档 , 然后用 bulk API 把文档推送到新的索引中。
从Elasticsearch v2.3.0开始, Reindex API 被引入。它能够对文档重建索引而不需要任何插件或外部工具。
#为防止数据重叠,可以按日期或者时间这样的字段作为过滤条件把大的重建索引分成小的任务:
$ GET /old_index/_search?scroll=1m
{
    "query": {
        "range": {
            "date": {
                "gte":  "2014-01-01",
                "lt":   "2014-02-01"
            }
        }
    },
    "sort": ["_doc"],
    "size":  1000
}

9> 索引别名
索引别名就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。
有两种方式管理别名: _alias 用于单个操作, _aliases 用于执行多个原子级操作。
$ PUT /index_v1  #创建索引 index_v1
$ PUT /index_v1/_alias/trade #将别名 trade 指向 index_v1
$ GET /*/_alias/my_index #检测别名指向哪一个索引
$ GET /my_index_v1/_alias/* #哪些别名指向这个索引

一个别名可以指向多个索引,所以我们在添加别名到新索引的同时必须从旧的索引中删除它。这个操作需要原子化,这意味着我们需要使用 _aliases 操作:
$ POST /_aliases
{
    "actions": [
        { "remove": { "index": "index_v1", "alias": "trade" }},
        { "add":    { "index": "index_v2", "alias": "trade" }}
    ]
}

10、近实时搜索

默认情况下每个分片会每秒自动刷新一次。可以用 refresh API 执行一次手动刷新
$ POST /_refresh #刷新(Refresh)所有的索引
$ POST /trade/_refresh #只刷新(Refresh) trade 索引

可以通过设置 refresh_interval ,降低每个索引的刷新频率
$ PUT /trade
{
  "settings": {
    "refresh_interval": "30s"  #每30秒刷新 trade 索引
  }
}

refresh_interval 可以在既存索引上进行动态更新。 在生产环境中,在建立一个大的新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来
#关闭自动刷新
$ PUT /trade/_settings
{ "refresh_interval": -1 } 

#每秒自动刷新
$ PUT /trade/_settings
{ "refresh_interval": "1s" }
refresh_interval 需要一个持续时间值, 例如 1s (1 秒) 或 2m (2 分钟)。 一个绝对值 1 表示的是1毫秒