elasticsearch 学习整理
索引和值:增删查改
In [2]: from elasticsearch import Elasticsearch
In [8]: es = Elasticsearch([{'host':'192.168.3.215','port':9215}])
# 创建索引
In [11]: res = es.indices.create(index='wl_test')
In [12]: print(res)
{'acknowledged': True, 'shards_acknowledged': True, 'index': 'wl_test'}
# 重复创建索引,报错 400
RequestError: RequestError(400, 'resource_already_exists_exception', 'index [wl_test/CEwdWyvfSzO4oQVpafZpsg] already exists')
# ignore 参数,忽略 400 不会报错
In [15]: es.indices.create(index='wl_test',ignore=[400])
Out[15]:
{'error': {'root_cause': [{'type': 'resource_already_exists_exception',
'reason': 'index [wl_test/CEwdWyvfSzO4oQVpafZpsg] already exists',
'index_uuid': 'CEwdWyvfSzO4oQVpafZpsg',
'index': 'wl_test'}],
'type': 'resource_already_exists_exception',
'reason': 'index [wl_test/CEwdWyvfSzO4oQVpafZpsg] already exists',
'index_uuid': 'CEwdWyvfSzO4oQVpafZpsg',
'index': 'wl_test'},
'status': 400}
# 删除索引
In [16]: es.indices.delete(index='wl_test')
Out[16]: {'acknowledged': True}
# 重复删除索引,报错 404
NotFoundError: NotFoundError(404, 'index_not_found_exception', 'no such index [wl_test]', wl_test, index_or_alias)
# ignore 参数,忽略 404 不会报错
In [18]: es.indices.delete(index='wl_test',ignore=[404])
Out[18]:
{'error': {'root_cause': [{'type': 'index_not_found_exception',
'reason': 'no such index [wl_test]',
'index_uuid': '_na_',
'resource.type': 'index_or_alias',
'resource.id': 'wl_test',
'index': 'wl_test'}],
'type': 'index_not_found_exception',
'reason': 'no such index [wl_test]',
'index_uuid': '_na_',
'resource.type': 'index_or_alias',
'resource.id': 'wl_test',
'index': 'wl_test'},
'status': 404}
# 我们需要善用 ignore 参数,把一些意外情况排除,这样可以保证程序的正常执行而不会中断。
# 插入数据
IIn [20]: data = {'title':'测试','q':"what's your name?"}
In [21]: res = es.create(index= 'wl_test',doc_type='data',id = 1,body=data)
DeprecationWarning: The 'body' parameter is deprecated for the 'create' API and will be removed in a future version. Instead use the 'document' parameter. See https://github.com/elastic/elasticsearch-py/issues/1698 for more information
d:\python36\lib\site-packages\elasticsearch\connection\base.py:209: ElasticsearchWarning: [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
warnings.warn(message, category=ElasticsearchWarning)
In [25]: res = es.create(index= 'wl_test',doc_type='data',id = 2,document=data)
ElasticsearchWarning: [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).warnings.warn(message, category=ElasticsearchWarning)
In [26]: res
Out[26]:
{'_index': 'wl_test',
'_type': 'data',
'_id': '2',
'_version': 1,
'result': 'created',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 1,
'_primary_term': 1}
# create 方法插入数据,创建文档必须有 id
In [27]: res = es.create(index= 'wl_test',doc_type='data',document=data)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-27-761089aab424> in <module>
----> 1 res = es.create(index= 'wl_test',doc_type='data',document=data)
d:\python36\lib\site-packages\elasticsearch\client\utils.py in _wrapped(*args, **kwargs)
299 if p in kwargs:
300 params[p] = kwargs.pop(p)
--> 301 return func(*args, params=params, headers=headers, **kwargs)
302
303 return _wrapped
TypeError: create() missing 1 required positional argument: 'id'
# index 方法,插入数据,不用指定 id,随机id;create 方法 是对 index 方法的封装。
In [28]: es.index(index='wl_test',doc_type='data',body= data)
Out[28]:
{'_index': 'wl_test',
'_type': 'data',
'_id': 'JouMRX0Bd_s0QCOkwdWF',
'_version': 1,
'result': 'created',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 2,
'_primary_term': 1}
# update 更新数据
# 注意 body 参数,我们需要添加 doc 或者 script 变量来指定修改的内容
In [45]: data={'doc':{'title':'aaa','q':'bbb'}}
In [46]: es.update(index='wl_test',doc_type='data',body=data,id=1)
# _version 字段 版本号,每次更新都 +1
Out[46]:
{'_index': 'wl_test',
'_type': 'data',
'_id': '1',
'_version': 2,
'result': 'updated',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 3,
'_primary_term': 1}
# updat 不存在的 id
NotFoundError: NotFoundError(404, 'document_missing_exception', '[data][10]: document missing')
# index 更新,如果 id 存在则创建
In [48]: es.index(index='wl_test',doc_type='data',body=data,id = 10)
Out[48]:
{'_index': 'wl_test',
'_type': 'data',
'_id': '10',
'_version': 1,
'result': 'created',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 4,
'_primary_term': 1}
In [49]: es.index(index='wl_test',doc_type='data',body=data,id = 10)
Out[49]:
{'_index': 'wl_test',
'_type': 'data',
'_id': '10',
'_version': 2,
'result': 'updated',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 5,
'_primary_term': 1}
# delete 删除数据, result 字段为 deleted,代表删除成功,_version 变成了 3,又增加了 1。
In [50]: es.delete(index='wl_test',doc_type='data',id=10)
Out[50]:
{'_index': 'wl_test',
'_type': 'data',
'_id': '10',
'_version': 3,
'result': 'deleted',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 6,
'_primary_term': 1}
# 重新创建,_version +1
In [51]: es.index(index='wl_test',doc_type='data',id=10,document=data)
Out[51]:
{'_index': 'wl_test',
'_type': 'data',
'_id': '10',
'_version': 4,
'result': 'created',
'_shards': {'total': 2, 'successful': 2, 'failed': 0},
'_seq_no': 7,
'_primary_term': 1}
# 查询数据:对于中文需要安装中文分词插件
# elasticsearch-analysis-ik,GitHub 链接为:https://github.com/medcl/elasticsearch-analysis-ik,这里我们使用 Elasticsearch 的另一个命令行工具 elasticsearch-plugin 来安装,这里安装的版本是 6.2.4,请确保和 Elasticsearch 的版本对应起来,命令如下:
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.3.0/elasticsearch-analysis-ik-7.3.0.zip
# 查看是否中文分词器安装成功
[root@b4de731914a2 elasticsearch]# /usr/share/elasticsearch/bin/elasticsearch-plugin list
analysis-ik
# 安装之后重新启动 Elasticsearch 就可以了,它会自动加载安装好的插件。
# 首先我们新建一个索引并指定需要分词的字段
In [73]: mapping
Out[73]:
{'properties': {'title': {'type': 'text',
'analyzer': 'ik_max_word',
'search_analyzer': 'ik_max_word'}}}
es.indices.create(index='news', ignore=400)
# 更新 mapping
result = es.indices.put_mapping(index='news', doc_type='politics', body=mapping)
# 插入数据
datas = [
{
'title': '美国留给伊拉克的是个烂摊子吗',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2011-12-16'
},
{
'title': '公安部:各地校车将享最高路权',
'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
'date': '2011-12-16'
},
{
'title': '中韩渔警冲突调查:韩警平均每天扣1艘中国渔船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2011-12-17'
},
{
'title': '中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2011-12-18'
}
]
for data in datas:
es.index(index='news', doc_type='politics', body=data)
查询:
In [74]: es.search(index='wl_test',doc_type='data')
d:\python36\lib\site-packages\elasticsearch\connection\base.py:209: ElasticsearchWarning: [types removal] Specifying types in search requests is deprecated.
warnings.warn(message, category=ElasticsearchWarning)
Out[74]:
{'took': 54,
'timed_out': False,
'_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0},
'hits': {'total': {'value': 4, 'relation': 'eq'},
'max_score': 1.0,
'hits': [{'_index': 'wl_test',
'_type': 'data',
'_id': 'wVk5Rn0BM5UnHl8RH9ZL',
'_score': 1.0,
'_source': {'title': '美国留给伊拉克的是个烂摊子吗',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2011-12-16'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'wlk5Rn0BM5UnHl8RINYy',
'_score': 1.0,
'_source': {'title': '公安部:各地校车将享最高路权',
'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
'date': '2011-12-16'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'w1k5Rn0BM5UnHl8RINZK',
'_score': 1.0,
'_source': {'title': '中韩渔警冲突调查:韩警平均每天扣1艘中国渔船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2011-12-17'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'xFk5Rn0BM5UnHl8RINZe',
'_score': 1.0,
'_source': {'title': '中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2011-12-18'}}]}}
In [75]: es.search(index='wl_test')
Out[75]:
{'took': 7,
'timed_out': False,
'_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0},
'hits': {'total': {'value': 4, 'relation': 'eq'}, # 结果数量
'max_score': 1.0, # 最大匹配分数
'hits': [{'_index': 'wl_test',
'_type': 'data',
'_id': 'wVk5Rn0BM5UnHl8RH9ZL',
'_score': 1.0,
'_source': {'title': '美国留给伊拉克的是个烂摊子吗',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2011-12-16'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'wlk5Rn0BM5UnHl8RINYy',
'_score': 1.0,
'_source': {'title': '公安部:各地校车将享最高路权',
'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
'date': '2011-12-16'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'w1k5Rn0BM5UnHl8RINZK',
'_score': 1.0,
'_source': {'title': '中韩渔警冲突调查:韩警平均每天扣1艘中国渔船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2011-12-17'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'xFk5Rn0BM5UnHl8RINZe',
'_score': 1.0,
'_source': {'title': '中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2011-12-18'}}]}}
In [76]: dsl = { # 通过 DSL 查询
...: 'query':{
...: 'match':{
...: 'title':'中国 领事馆'
...: }
...: }
...: }
In [77]: es.search(index='wl_test',doc_type='data',body=dsl)
D:\python36\Scripts\ipython:1: DeprecationWarning: The 'body' parameter is deprecated for the 'search' API and will be removed in a future version. Instead use API parameters directly. See https://github.com/elastic/elasticsearch-py/issues/1698 for more information
d:\python36\lib\site-packages\elasticsearch\connection\base.py:209: ElasticsearchWarning: [types removal] Specifying types in search requests is deprecated.
warnings.warn(message, category=ElasticsearchWarning)
Out[77]:
{'took': 30,
'timed_out': False,
'_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0},
'hits': {'total': {'value': 2, 'relation': 'eq'},
'max_score': 3.9923213,
'hits': [{'_index': 'wl_test',
'_type': 'data',
'_id': 'xFk5Rn0BM5UnHl8RINZe',
'_score': 3.9923213,
'_source': {'title': '中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2011-12-18'}},
{'_index': 'wl_test',
'_type': 'data',
'_id': 'w1k5Rn0BM5UnHl8RINZK',
'_score': 0.642793,
'_source': {'title': '中韩渔警冲突调查:韩警平均每天扣1艘中国渔船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2011-12-17'}}]}}
# 检索时会对对应的字段全文检索,结果还会按照检索关键词的相关性进行排序,这就是一个基本的搜索引擎雏形。
mapping 类型
属性名字 说明
text 用于全文索引,该类型的字段将通过分词器进行分词,最终用于构建索引
keyword 不分词,只能搜索该字段的完整的值,只用于 filtering
long 有符号64-bit integer:-2^63 ~ 2^63 - 1
integer 有符号32-bit integer,-2^31 ~ 2^31 - 1
short 有符号16-bit integer,-32768 ~ 32767
byte 有符号8-bit integer,-128 ~ 127
double 64-bit IEEE 754 浮点数
float 32-bit IEEE 754 浮点数
half_float 16-bit IEEE 754 浮点数
boolean true,false
date https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html
binary 该类型的字段把值当做经过 base64 编码的字符串,默认不存储,且不可搜索
字符串类型
在 ES 7.x 有两种字符串类型:text 和 keyword,在 ES 5.x 之后 string 类型已经不再支持了。
text :类型适用于需要被全文检索的字段,例如新闻正文、邮件内容等比较长的文字,text 类型会被 Lucene 分词器(Analyzer)处理为一个个词项,并使用 Lucene 倒排索引存储,text 字段不能被用于排序,如果需要使用该类型的字段只需要在定义映射时指定 JSON 中对应字段的 type 为 text。
keyword:不会被分词,适合简短、结构化字符串,例如主机名、姓名、商品名称等,可以用于过滤、排序、聚合检索,也可以用于精确查询。
各种查询:
# 查询所有
body = {
"query":{
"match_all":{}
}
}
# 等于查询
# term查询:查询 preview 字段里有某个关键词的文档
# terms 查询:查询某个字段里有多个关键词的文档
# term: 查询 xx = “xx”
body = {
"query":{
"term":{
"name":"python"
}
}
}
# terms: 查询 xx = “xx” 或 xx = “yy”
body = {
"query":{
"terms":{
"name":[
"ios","android"
]
}
}
}
# 包含查询
# match 查询可以接受文字,数字日期等数据类型
# match 和term的区别是,match 查询的时候,elasticsearch会根据你给定的字段提供合适的分析器,而term查询时不会有分析器分析的过程
# match: 匹配name包含"python"关键字的数据
body = {
"query":{
"match":{
"name":"python"
}
}
}
# multi_match: 在name和addr里匹配包含深圳关键字的数据
body = {
"query":{
"multi_match":{
"query":"深圳",
"fields":["name", "addr"]
}
}
}
# 通过match_phrase查询
# 短语查询,slop定义的关键词之间隔多个未知的单词
# 查询elasticsearch,distributed 之间间隔2个单词的文档
{
"query"{
"match_phrase":{
"preview":{
"query":"Elasticsearch,distributed",
"slop":2
}
}
}
# ids 查询id
# 搜索出id为1或2的所有数据
body = {
"query":{
"ids":{
"type":"type_name",
"values":[
"1","2"
]
}
}
}
# 复合查询 bool 查询:bool有3类查询关系,must(都满足),should(其中一个满足),must_not(都不满足)
# 获取name="python"并且age=18的所有数据
body = {
"query":{
"bool":{
"must":[
{
"term":{
"name":"python"
}
},
{
"term":{
"age":18
}
}
]
}
}
}
# 切片式查询
# 从第2条数据开始,获取4条数据,索引 从 0 开始
body = {
"query":{
"match_all":{}
},
"from":2, # 从第二条数据开始
"size":4, # 获取4条数据
}
# 范围查询
# 查询18<=age<=30的所有数据
body = {
"query":{
"range":{
"age":{
"gte":18, # >=18
"lte":30 # <=30
}
}
}
}
# 前缀查询
# 查询前缀为"赵"的所有数据
body = {
"query":{
"prefix":{
"name":"p"
}
}
}
# 查询name以id为后缀的所有数据
body = {
"query":{
"wildcard":{
"name":"*id"
}
}
}
# 排序
body = {
"query":{
"match_all":{}
}
"sort":{
"age":{ # 根据age字段升序排序
"order":"asc" # asc升序,desc降序
}
}
}
# 多字段排序,注意顺序!写在前面的优先排序
body = {
"query":{
"match_all":{}
}
"sort":[{
"age":{ # 先根据age字段升序排序
"order":"asc" # asc升序,desc降序
}
},{
"name":{ # 后根据name字段升序排序
"order":"asc" # asc升序,desc降序
}
}],
}
# filter_path, 响应过滤
# 只要 _id 数据
In [147]: es.search(index='wl_test',doc_type='data',filter_path=('hits.hits._id'))
d:\python36\lib\site-packages\elasticsearch\connection\base.py:209: ElasticsearchWarning: [types removal] Specifying types in search requests is deprecated.
warnings.warn(message, category=ElasticsearchWarning)
Out[147]:
{'hits': {'hits': [{'_id': 'wVk5Rn0BM5UnHl8RH9ZL'},
{'_id': 'wlk5Rn0BM5UnHl8RINYy'},
{'_id': 'w1k5Rn0BM5UnHl8RINZK'},
{'_id': 'xFk5Rn0BM5UnHl8RINZe'}]}}
# 多个条件用逗号隔开
In [149]: es.search(index='wl_test',doc_type='data',filter_path=('hits.hits._id','hits.hits._source'))
d:\python36\lib\site-packages\elasticsearch\connection\base.py:209: ElasticsearchWarning: [types removal] Specifying types in search requests is deprecated.
warnings.warn(message, category=ElasticsearchWarning)
Out[149]:
{'hits': {'hits': [{'_id': 'wVk5Rn0BM5UnHl8RH9ZL',
'_source': {'title': '美国留给伊拉克的是个烂摊子吗',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2011-12-16'}},
{'_id': 'wlk5Rn0BM5UnHl8RINYy',
'_source': {'title': '公安部:各地校车将享最高路权',
'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
'date': '2011-12-16'}},
{'_id': 'w1k5Rn0BM5UnHl8RINZK',
'_source': {'title': '中韩渔警冲突调查:韩警平均每天扣1艘中国渔船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2011-12-17'}},
{'_id': 'xFk5Rn0BM5UnHl8RINZe',
'_source': {'title': '中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2011-12-18'}}]}}
# count 获取数量
In [150]: es.count(index='wl_test',doc_type='data')
Out[150]:
{'count': 4,
'_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}}
# 度量类聚合
# 搜索所有数据,并获取age最小的值
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"min_age":{ # 最小值的key,随便指定
"min":{ # 最小
"field":"age" # 查询"age"的最小值
}
}
}
}
# 搜索所有数据,并获取age最大的值
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"max_age":{ # 最大值的key
"max":{ # 最大
"field":"age" # 查询"age"的最大值
}
}
}
}
# 搜索所有数据,并获取所有age的和,时间类型也可以
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"sum_age":{ # 和的key
"sum":{ # 和
"field":"age" # 获取所有age的和
}
}
}
}
# 搜索所有数据,获取所有age的平均值
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"avg_age":{ # 平均值的key
"avg":{ # 平均值
"field":"age" # 获取所有age的平均值
}
}
}
}
# from ,size
# from:从“第几条”开始查询
# size:查询多少条
body = {
"query":{
"match_all":{}
},
"size":"50",
"from":"0"
}
bool 和 filtered
es 5.0版本更新后,filtered的查询将替换为bool查询。
filtered是比较老的的版本的语法。现在目前已经被bool替代。推荐使用bool。
must 与 filter
must, 返回的文档必须满足must子句的条件,并且参与计算分值
filter, 返回的文档必须满足filter子句的条件。但是跟Must不一样的是,不会计算分值, 并且可以使用缓存
从上面的描述来看,你应该已经知道,如果只看查询的结果,must和filter是一样的。区别是场景不一样。如果结果需要算分就使用must,否则可以考虑使用filter。
为了说明filter查询高效的原因,我们需要引入ES的一个概念 query context和 filter context。
query context
query context关注的是,文档到底有多匹配查询的条件,这个匹配的程度是由相关性分数决定的,分数越高自然就越匹配。所以这种查询除了关注文档是否满足查询条件,还需要额外的计算相关性分数.
filter context
filter context关注的是,文档是否匹配查询条件,结果只有两个,是和否。没有其它额外的计算。它常用的一个场景就是过滤时间范围。
并且filter context会自动被ES缓存结果,效率进一步提高。
对于bool查询,must使用的就是query context,而filter使用的就是filter context。
除了是否计算相关性算分的差别,经常使用的过滤器将被Elasticsearch自动缓存,以提高性能。
filter 查询
{
"query":{
"bool":{
"filter":[
{"term":
{"price":40}
}
]
}
}
}
filter的两种用法
1 嵌套在bool下
{
"query": {
"bool": {
"must": {
"term": {
"term":{"title":"kitchen3"}
}
},
"filter": {
"term": {
"price":1000
}
}
}
}
}
2 在根目录下使用
{
"query":{
"term":{"title":"kitchen3"}
},
"filter":{
"term":{"price":1000}
}
}
位置:bool下
区别:在filter query先执行filter,不计算score,再执行query。
位置:根目录下
区别:根目录中的filter在query后执行。
备注:如果还要在搜索结果中执行aggregation操作,filter query聚合的是filter和query之后的结果,而filter聚合的是query的结果
script 获取值
search,用doc
update,用ctx