【ElasticSearch】使用学习
【ElasticSearch】使用学习和许可证
===================================================
0、许可证
1、安装
2、基本概念
3、常用操作
4、
5、
6、更新 fielddata
7、翻页检索
8、字符类型 使用 text 或 keyword 类型设置
===================================================
0、许可证
查看有效期
curl -XGET http://localhost:9200/_xpack/license?pretty
注册许可证
更新许可证
curl -XPUT -u elastic 'http://localhost:9200/_xpack/license?acknowledge=true' -H "Content-Type: application/json" -d @license.json
1、安装
2、基本概念
基本请求格式
一个 Elasticsearch 请求和任何 HTTP 请求一样由若干相同的部件组成:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
VERB
适当的 HTTP 方法 或 谓词 : GET、 POST、 PUT、 HEAD 或者 DELETE。
PROTOCOL
http 或者 https(如果你在 Elasticsearch 前面有一个 https 代理)
HOST
Elasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点。
PORT
运行 Elasticsearch HTTP 服务的端口号,默认是 9200 。
PATH
API 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如:_cluster/stats 和 _nodes/stats/jvm 。
QUERY_STRING
任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读)
BODY
一个 JSON 格式的请求体 (如果请求需要的话)
文档
Elasticsearch 使用 JavaScript Object Notation(或者 JSON)作为文档的序列化格式。JSON 序列化为大多数编程语言所支持,并且已经成为 NoSQL 领域的标准格式。 它简单、简洁、易于阅读。
一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性
全文搜索 match
GET /myindex/mytype/_search { "query": { "match": { "about": "rock climbing" } } }
Elasticsearch 默认按照相关性得分排序,即每个文档跟查询的匹配程度
短语搜索 match_phrase
找出一个属性中的独立单词是没有问题的,但有时候想要精确匹配一系列单词或者_短语_ 。
比如, 我们想执行这样一个查询,仅匹配同时包含 “rock” 和 “climbing” ,并且 二者以短语 “rock climbing” 的形式紧挨着的雇员记录。
GET /myindex/mytype/_search { "query": { "match_phrase": { "about": "rock climbing" } } }
高亮搜索 highlight
GET /myindex/mytype/_search { "query": { "match_phrase": { "about": "rock climbing" } }, "highlight": { "fields": { "about": {} } } }
返回结果多了一个叫做 highlight 的部分
这个部分包含了 about 属性匹配的文本片段,并以 HTML 标签 <em></em> 封装:
分析聚合 aggregations
Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。
聚合与 SQL 中的 GROUP BY 类似但更强大。
GET /myindex/mytype/_search { "aggs": { "all_interests": { "terms": { "field": "interests" } } } }
报fielddata错误时修改
PUT myindex/_mapping/mytype { "properties": { "interests": { "type": "text", "fielddata": true } } }
姓 Smith 的员工中最受欢迎的兴趣爱好
GET /myindex/mytype/_search { "query": { "match": { "last_name": "smith" } }, "aggs": { "all_interests": { "terms": { "field": "interests" } } } }
查询特定兴趣爱好员工的平均年龄
GET /myindex/mytype/_search { "aggs": { "all_interests": { "terms": { "field": "interests" }, "aggs": { "avg_age": { "avg": { "field": "age" } } } } } }
两层聚合
{ "aggs": { "all_interests": { "terms": { "field": "interests" }, "aggs": { "xingshi": { "terms": { "field": "last_name", "order": { "avg_age": "asc" } }, "aggs": { "avg_age": { "avg": { "field": "age" } } } } } } } }
集群健康
GET /_cluster/health { "cluster_name": "elasticsearch", "status": "green", "timed_out": false, "number_of_nodes": 1, "number_of_data_nodes": 1, "active_primary_shards": 0, "active_shards": 0, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 0 }
status 字段指示着当前集群在总体上是否工作正常。它的三种颜色含义如下:
green
所有的主分片和副本分片都正常运行。
yellow
所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red
有主分片没能正常运行。
PUT /myindex { "settings" : { "number_of_shards" : 3, "number_of_replicas" : 1 } }
动态调整副本分片数
PUT /myindex/_settings { "number_of_replicas" : 1 }
处理冲突
在数据库领域中,有两种方法通常被用来确保并发更新时变更不会丢失:
悲观并发控制
这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。
乐观并发控制
Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。
文档的部分更新
我们也介绍过文档是不可变的:他们不能被修改,只能被替换。 update API 必须遵循同样的规则。 从外部来看,我们在一个文档的某个位置进行部分更新。然而在内部, update API 简单使用与之前描述相同的 检索-修改-重建索引 的处理过程。 区别在于这个过程发生在分片内部,这样就避免了多次请求的网络开销。通过减少检索和重建索引步骤之间的时间,我们也减少了其他进程的变更带来冲突的可能性。
POST /myindex/mytype/1/_update { "doc" : { "tags" : [ "testing" ], "views": 0 } }
doc是关键字
更新和冲突
这可以通过设置参数 retry_on_conflict 来自动完成, 这个参数规定了失败之前 update 应该重试的次数,它的默认值为 0
取回多个文档
mget API 要求有一个 docs 数组作为参数,每个元素包含需要检索文档的元数据, 包括 _index 、 _type 和 _id 。如果你想检索一个或者多个特定的字段,那么你可以通过 _source 参数来指定这些字段的名字:
POST /_mget { "docs": [ { "_index": "myindex", "_type": "mytype", "_id": 3 }, { "_index": "myindex", "_type": "mytype", "_id": 2, "_source": "age" }, { "_index": "myindex", "_type": "mytype", "_id": 1, "_source": [ "first_name", "last_name" ] } ] }
该响应体也包含一个 docs 数组, 对于每一个在请求中指定的文档,这个数组中都包含有一个对应的响应,且顺序与请求中的顺序相同。
如果想检索的数据都在相同的 _index 中(甚至相同的 _type 中),则可以在 URL 中指定默认的 /_index 或者默认的 /_index/_type 。
POST /myindex/mytype/_mget { "docs": [ { "_id": 2 }, { "_type": "mytype2", "_id": 1 } ] }
index和type都相同只传ID
POST /myindex/mytype/_mget { "ids": ["1","2","3"] }
代价较小的批量操作
bulk 与其他的请求体格式稍有不同,如下所示
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
每行一定要以换行符(\n)结尾, 包括最后一行
action 必须是以下选项之一:
create
如果文档不存在,那么就创建它
index
创建一个新文档或者替换一个现有的文档
update
部分更新一个文档
delete
删除一个文档,没有请求体
request body 行由文档的 _source 本身组成—文档包含的字段和值。它是 index 和 create 操作所必需的,这是有道理的:你必须提供文档以索引
{"delete":{"_index": "myindex", "_type": "mytype", "_id": "1"}}
{"create":{"_index": "myindex", "_type": "mytype", "_id": "4"}}
{"title":"My first blog post"}
{"update":{"_index": "myindex", "_type": "mytype", "_id": "123", "_retry_on_conflict" : 3} }
{"doc":{"title":"My updated blog post"} }
{"index":{"_index": "website", "_type": "blog"}}
{"title":"My second blog post"}
映射(Mapping)描述数据在每个字段内如何存储
分析(Analysis)全文是如何处理使之可以被搜索的
领域特定查询语言(Query DSL)Elasticsearch 中强大灵活的查询语言
hits
返回结果中最重要的部分是 hits ,它包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档。
在 hits 数组中每个结果包含文档的 _index 、 _type 、 _id ,加上 _source 字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的ID,需要你单独去获取文档。
每个结果还有一个 _score ,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照 _score 降序排列的。在这个例子中,我们没有指定任何查询,故所有的文档具有相同的相关性,因此对所有的结果而言 1 是中性的 _score 。
max_score 值是与查询所匹配文档的 _score 的最大值。
took 值告诉我们执行整个搜索请求耗费了多少毫秒。
_shards 部分告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒):
多索引多类型
/_search
在所有的索引中搜索所有的类型
/gb/_search
在 gb 索引中搜索所有的类型
/gb,us/_search
在 gb 和 us 索引中搜索所有的文档
/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型
/gb/user/_search
在 gb 索引中搜索 user 类型
/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型
/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型
分页
size显示应该返回的结果数量,默认是 10
from显示应该跳过的初始结果数量,默认是 0
在分布式系统中深度分页
理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。
现在假设我们请求第 1000 页—结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
查看分词结果
info/info/100001/_termvectors?fields=tree_id_position
POST _analyze
{
"analyzer": "whitespace",
"text": "The quick brown fox."
}
查看索引分词结果
info/_analyze
{
"analyzer": "comma",
"text": "1,b,c"
}
//静态设置
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 方式以牺牲字段存储性能为代价来获得更高的压缩比例。
//动态设置
index.number_of_replicas //每个主分片的副本数。默认为 1。
index.auto_expand_replicas //基于可用节点的数量自动分配副本数量,默认为 false(即禁用此功能)
index.refresh_interval //执行刷新操作的频率,这使得索引的最近更改可以被搜索。默认为 1s。可以设置为 -1 以禁用刷新。
index.max_result_window //用于索引搜索的 from+size 的最大值。默认为 10000
index.max_rescore_window // 在搜索此索引中 rescore 的 window_size 的最大值
index.blocks.read_only //设置为 true 使索引和索引元数据为只读,false 为允许写入和元数据更改。
index.blocks.read // 设置为 true 可禁用对索引的读取操作
index.blocks.write //设置为 true 可禁用对索引的写入操作。
index.blocks.metadata // 设置为 true 可禁用索引元数据的读取和写入。
index.max_refresh_listeners //索引的每个分片上可用的最大刷新侦听器数
查询
查询领域特定语言(query domain-specific language) 或者 Query DSL 来写查询语句。
GET myindex/mytype/_search { "match": { "last_name": "Smith" } } 完整的查询 GET myindex/mytype/_search { "query": { "match": { "last_name": "Smith" } } }
合并查询语句
叶子语句(Leaf clauses)像 match 被用于将查询字符串和一个字段(或者多个字段)对比
复合(Compound) 语句 主要用于 合并其它查询语句一个 bool 语句 允许在你需要的时候组合其它语句,无论是 must 匹配、 must_not 匹配还是 should 匹配,同时它可以包含不评分的过滤器(filters):
{ "bool": { "must": { "match": { "email": "admin@126.com" } }, "should": [ { "match": { "starred": true } }, { "bool": { "must": { "match": { "folder": "inbox" } }, "must_not": { "match": { "spam": true } } } } ], "minimum_should_match": 1 } }
match 查询
{ "match": { "tweet": "About Search" } }
multi_match 查询
{ "multi_match": { "query": "full text search", "fields": [ "title", "body" ] } }
range 查询
gt大于,gte大于等于,lt小于,lte小于等于
{ "range": { "age": { "gte": 20, "lt": 30 } } }
term 查询
被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串:
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
terms 查询
和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件
{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}
exists 查询和 missing 查询
被用于查找那些指定字段中有值 (exists) 或无值 (missing) 的文档。这与SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本质上具有共性
{ "exists": { "field":"title" } }
3、常用操作
索引
a. 添加索引
PUT /myindex 返回 { "acknowledged": true, "shards_acknowledged": true, "index": "myindex" }
指定setting
PUT /myindex { "settings": { "number_of_shards": 1, "number_of_replicas": 1, "analysis": { "analyzer": { "comma": { "type": "pattern", "pattern": "," } } } } }
指定setting 和 mapping
{ "settings": { "number_of_shards": 1, "numbeer_of_replicas": 1 }, "mapping": { "mytype": { "properties": { "id": { "type": "string" }, "name": { "name": "string" } } } } }
b. 修改索引
GET /myindex/_settings?pretty { "myindex": { "settings": { "number_of_replicas": 1, "index": { "analysis": { "analyzer": { "comma": { "type": "pattern", "pattern": "," } } } } } } }
GET /myindex/mytype/_mapping?pretty { "myindex": { "mappings": { "mytype": { "properties": { "name": { "type": "string" }, "age": { "type": "long" } } } } } }
c. 查询索引
GET /myindex 返回 { "myindex": { "aliases": {}, "mappings": {}, "settings": { "index": { "creation_date": "1583914355792", "number_of_shards": "5", "number_of_replicas": "1", "uuid": "Aeld8sxwTCu5__gLHTvBwQ", "version": { "created": "6020499" }, "provided_name": "myindex" } } } }
d. 删除索引
DELETE /myindex
返回 { "acknowledged": true }
搜索
GET /myindex/mytype/_search
默认返回10条,查询表达式 , 它支持构建更加复杂和健壮的查询。领域特定语言 (DSL), 使用 JSON 构造了一个请求。
姓氏为 Smith 年龄大于 30 的员工
查询 match,过滤器 filter
POST /myindex/mytype/_search { "query": { "bool": { "must": { "match": { "last_name": "smith" } }, "filter": { "range": { "age": { "gt": 30 } } } } } }
文档
最顶层或者根对象指定了唯一ID被序列化成JSON并存储到Elasticsearch中。
a. 索引文档(创建文档)
在Elasticsearch中_index、_type和_id的组合唯一标识一个文档
自定义ID
PUT /myindex/mytype/id { ... }
自动生成ID
把put换成post就可以,去掉指定ID
POST /myindex/mytype
{ ... }
创建一个完全新的文档,而不是覆盖现有的文档
如果已经有自己的_id,那么我们必须告诉Elasticsearch,只有在相同的_index、_type和_id不存在时才接受我们的索引请求。有两种方式:
第一种方法使用op_type查询-字符串参数
PUT /myindex/mytype/id?op_type=create { ... }
第二种方法是在URL末端使用/_create
PUT /myindex/mytype/id/_create { ... }
如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码
另一方面,如果具有相同的_index、_type和_id的文档已经存在,Elasticsearch将会返回409Conflict响应码
b. 查询文档
GET /myindex/mytype/{id}?pretty
返回部分文档,单个字段能用_source参数请求得到,多个字段也能使用逗号分隔的列表来指定。
GET /myindex/mytype/id?_source=age,first_name
带请求体
POST /myindex/mytype/_search { "_source": [ "age", "first_name" ] }
返回_source部分
GET /myindex/mytype/id/_source
c. 更新文档
在Elasticsearch中文档是不可改变的,不能修改它们。相反,如果想要更新现有的文档,需要重建索引或者进行替换
PUT /myindex/mytype/id { "name": "alice", }
d. 删除文档
DELETE /myindex/mytype/id
成功"result": "deleted"、 失败"result": "not_found"
统计文档件数
curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } } '
更新 fielddata
PUT my_index/_mapping/my_type { "properties": { "id": { "type": "text", "fielddata": true } } }
7、翻页检索
A、from, size
获取实时数据,但是随着翻页深度增加效率会越来越差,window默认是10000
可通过,设置max_result_window临时解决
PUT movies/_settings { "index" : { "max_result_window" : 20000 } }
B、Search After
可用于实时翻页,不能指定页码
POST my_index/my_type/_search { "size": 1, "query": { "match_all": {} }, "sort": [ { "status": "desc" }, { "id": "desc" } ] }
返回的sort放在search_after中
"sort": [2,"1235472336950333441"]
POST my_index/my_type/_search { "size": 1, "query": { "match_all": {} }, "search_after": [ 2, "1235472336950333441" ], "sort": [ { "status": "desc" }, { "id": "desc" } ] }
C、Scroll
非实时获取文档,处理大数据量,类似数据库的光标。指定scroll参数
POST /my_index/_search?scroll=1m { "size": 1, "query": { "match": { "title": "文档" } } }
返回结果中有"_scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAACYWaFh0R3k5Y3ZTYU9JSEZqNGV6ek14UQ==",拿到_scroll_id之后,用下面的方式往下翻
POST /_search/scroll { "scroll" : "1m", "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAADgWaFh0R3k5Y3ZTYU9JSEZqNGV6ek14UQ==" }
查询当前有多少scroll
默认情况下,打开的滚动的最大数量为500.可以使用search.max_open_scroll_context群集设置更新此限制
GET /_nodes/stats/indices/search
删除指定scroll_id
DELETE /_search/scroll { "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" }
删除全部
DELETE /_search/scroll/_all
8、字符类型 使用 text 或 keyword 类型设置
参考:https://blog.csdn.net/kakaluoteyy/article/details/80324553
es从2.X版本一下子跳到了5.X版本,将string类型变为了过期类型,取而代之的是text和keyword数据类型,一直到现在最新的6以上版本
按照官方文档的阐述
text类型的数据被用来索引长文本,例如电子邮件主体部分或者一款产品的介绍,这些文本会被分析,在建立索引文档之前会被分词器进行分词,转化为词组。经过分词机制之后es允许检索到该文本切分而成的词语,但是text类型的数据不能用来过滤、排序和聚合等操作。
keyword类型的数据可以满足电子邮箱地址、主机名、状态码、邮政编码和标签等数据的要求,不进行分词,常常被用来过滤、排序和聚合。
综上,可以发现text类型在存储数据的时候会默认进行分词,并生成索引。而keyword存储数据的时候,不会分词建立索引,显然,这样划分数据更加节省内存。
2.X版本设置分词使用"index":"not_analyzed"配置是否可以被用来搜索
5以上的版本中,“index”参数用来配置该字段是否可以被用来搜索,true可以通过搜索该字段检索到文档,false为否,配置分词器,用analyzer参数