文章目录
一、Elasticsearch 基础概念
1.1 基本概念
Elasticsearch 是一个分布式文档存储。Elasticsearch 不是将信息存储为列式数据行,而是存储已序列化为 JSON 文档的复杂数据结构。当集群中有多个 Elasticsearch 节点时,存储的文档会分布在整个集群中,并且可以从任何节点立即访问。
存储文档后,它会在近乎实时的时间内被索引并完全可搜索——在1 秒内。Elasticsearch 使用一种称为倒排索引的数据结构,它支持非常快速的全文搜索。倒排索引列出了出现在任何文档中的每个唯一单词,并标识了每个单词出现的所有文档。
1.2 REST API
Elasticsearch 提供了一个简单、连贯的 REST API,用于管理集群以及索引和搜索数据。
Elasticsearch REST API 支持结构化查询、全文查询和将两者结合的复杂查询。结构化查询类似于你可以在 SQL 中构造的查询类型。例如,你可以搜索索引中的gender
和age
字段并按字段employee
作为索引,使用hire_date
进行排序。全文查询查找与查询字符串匹配的所有文档,并按相关性排序返回它们——它们与你的搜索词的匹配程度。
除了搜索单个术语外,你还可以执行短语搜索、相似性搜索和前缀搜索,并获得自动完成建议。
有要搜索的地理空间或其他数字数据吗?Elasticsearch 在支持高性能地理和数字查询的优化数据结构中索引非文本数据。
您可以使用 Elasticsearch 全面的 JSON 样式查询语言 ( Query DSL ) 访问所有这些搜索功能。您还可以构建SQL 样式的查询以在 Elasticsearch 内本地搜索和聚合数据,而 JDBC 和 ODBC 驱动程序使广泛的第三方应用程序能够通过 SQL 与 Elasticsearch 进行交互。
1.3 数据类型
Elasticsearch 在mappings参数中定义字段的,类似与数据库中的表结构的定义。
Elasticsearch 主要的基本数据类型有:
-
字符类型:text、keyword
- text: 全文搜索,会被分词处理,如果进行聚合、排序计算需要开启fielddata进行内存预加载,不建议此使用方法。
- keyword:精准值,不会被分词。进行过滤、排序、聚合等操作时, 就应该使用keyword类型。
-
数字类型:long、integer、short、byte、double、float、 half_float、 scaled_float
尽可能选择范围小的数据类型, 字段的长度越短, 索引和搜索的效率越高,尽可能避免浮点类型,如果必须使用优先考虑使用带缩放因子的浮点类型。
-
日期:date
由于json中无日期类型,所以es中日期可能是:
- 包含格式化日期的字符串, “2018-10-01”, 或"2018/10/01 12:10:30"
- 代表时间毫秒数的长整型数字
- 代表时间秒数的整数
- 注意日期格式
es系统初始化需要指定时区
如果时区未指定, 日期将被转换为UTC格式, 但存储的却是长整型的毫秒值.
可以自定义日期格式, 若未指定, 则使用默认格式: strict_date_optional_time||epoch_millis设置多种日期格式
{ "mappings": { "blog": { "properties": { "date": { "type": "date", // 可以接受如下类型的格式 "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" } } } } }
-
日期 纳秒:date_nanos
-
布尔型:boolean
true/false
-
二进制:binary
-
范围数据类型range: integer_range,、float_range、 long_range、double_range,、date_range、ip_range
例子:
// 添加映射create { "mappings": { "department": { "properties": { "expected_number": { // 预期员工数 "type": "integer_range" }, "time_frame": { // 发展时间线 "type": "date_range", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" }, "ip_whitelist": { // ip白名单 "type": "ip_range" } } } } } // 添加数据index { "expected_number" : { "gte" : 10, "lte" : 20 }, "time_frame" : { "gte" : "2018-10-01 12:00:00", "lte" : "2018-11-01" }, "ip_whitelist": "192.168.0.0/16" } // 查询数据search { "query": { "term": { "expected_number": { "value": 12 } } } } { "query": { "range": { "time_frame": { "gte": "208-08-01", "lte": "2018-12-01", "relation": "within" } } } }
复杂的数据类型
-
对象类型:object
JSON文档是分层的: 文档可以包含内部对象, 内部对象也可以包含内部对象。例如:
// json { "name": "ma_shoufeng", "address": { "region": "China", "location": {"province": "GuangDong", "city": "GuangZhou"} } } // es存储方式 { "name": "ma_shoufeng", "address.region": "China", "address.location.province": "GuangDong", "address.location.city": "GuangZhou" } // 此文档的映射mappings方式如下: { "mappings": { "developer": { "properties": { "name": {"type": "text", "index": "true"}, "aggress": { "properties": { "region": {"type": "keyword", "index":"true"}, "location": { "properties": { "province": {"type": "keyword", "index": "true"}, "city": {"type": "keyword", "index": "true"} } } } } } } } }
-
嵌套类型:nested
嵌套类型是对象数据类型的一个特例, 可以让array类型的对象被独立索引和搜索.
// array类型,数据结构 { "group": "stark", "performer": [ {"first": "John", "last": "Snow"}, {"first": "Sansa", "last": "Stark"} ] } // 内部存储结构 { "group": "stark", "performer.first": [ "john", "sansa" ], "performer.last": [ "snow", "stark" ] }
可以看出, user.first和user.last会被平铺为多值字段, 这样一来, John和Snow之间的关联性就丢失了。在查询时, 可能出现John Stark的结果。
用nested类型解决object类型的不足
如果需要对以最对象进行索引, 且保留数组中每个对象的独立性, 就应该使用嵌套数据类型.
—— 嵌套对象实质是将每个对象分离出来, 作为隐藏文档进行索引.// 创建映射create PUT game_of_thrones { "mappings": { "role": { "properties": { "performer": {"type": "nested"} } } } } // 添加数据index PUT game_of_thrones/role/1 { "group" : "stark", "performer" : [ {"first": "John", "last": "Snow"}, {"first": "Sansa", "last": "Stark"} ] } // 搜索数据search { "query": { "nested": { "path": "performer", "query": { "bool": { "must": [ {"match": {"performer.first": "John"}}, {"match": {"performer.last": "Snow"}} ] } }, "inner_hits": { "highlight": { "fields": {"performer.first": {}} } } } } }
-
地理数据类型
-
Geo-point: 地理点类型,地理点类型用于存储地理位置的经纬度对, 可用于:
- 查找一定范围内的地理点;
- 通过地理位置或相对某个中心点的距离聚合文档;
- 将距离整合到文档的相关性评分中;
- 通过距离对文档进行排序.
// 添加映射 { "mappings": { "developer": { "properties": { "location": {"type": "geo_point"} } } } }
-
Geo-shape: 地理形状类型
-
-
特殊数据类型
- IP: ip (IPv4 和 IPv6 地址)
- Completion:completion (to provide auto-complete suggestions)
- Token count:token_count (to count the number of tokens in a string)
- mapper-murmur3:murmur3(to compute hashes of values at index-time and store them in the index)
- mapper-annotated-text:annotated-text (to index text containing special markup (typically used for identifying named entities))
- Percolator:(Accepts queries from the query-dsl)
- Join:(Defines parent/child relation for documents within the same index)
- Alias:(Defines an alias to an existing field.)
- Rank feature:(Record numeric feature to boost hits at query time.)
- Rank features:(Record numeric features to boost hits at query time.)
- Dense vector:(Record dense vectors of float values.)
- Sparse vector:(Record sparse vectors of float values.)
- Search-as-you-type:(A text-like field optimized for queries to implement as-you-type completion)
-
数组类型
在Elasticsearch中,数组不需要一个特定的数据类型,任何字段都默认可以包含一个或多个值,当然,这多个值都必须是字段指定的数据类型。
-
Multi-fields
Multi-fields 通常用来以不同的方式或目的索引同一个字段。比如,一个字符串类型字段可以同时被映射为 text 类型以用于全文检索、 keyword字段用于排序或聚合。又或者,你可以用standard分析器、english分析器和french分析器来索引同一个 text字段。
1.4 数据类型总结
数字类型
类型 | 描述 |
---|---|
byte | 有符号的8位整数,范围:[-128~127] |
short | 有符号的16位整数,范围:[-32768 ~ 32767] |
integer | 有符号的32位整数,范围:[ − 2 31 -2^{31} −231 ~ 2 31 2^{31} 231-1] |
long | 有符号的64位整数,范围:[ − 2 63 -2^{63} −263 ~ 2 63 2^{63} 263-1] |
float | 32位单精度浮点数 |
double | 64位双精度浮点数 |
half_float | 16位半精度IEEE754浮点类型 |
scaled_float | 缩放类型的的浮点数,比如price:字段只需精确到分,57.34缩放因子为100,存储结果为5734 |
文本类型
类型 | 描述 |
---|---|
text | 会被分词处理。如果进行聚合、排序计算需要开启fielddata进行内存预加载,不建议此使用方法。 |
keyword | 精准值,不会被分词。进行过滤、排序、聚合等操作时, 就应该使用keyword类型。 |
范围类型
类型 | 描述 |
---|---|
integer_range | 整数范围 |
long_range | 长整数范围 |
float_range | 浮点数范围 |
double_range | 长浮点数范围 |
date_range | 日期范围 |
ip_range | ip地址范围 |
其他类型
类型 | 描述 |
---|---|
date | 日期类型,可以设置日期的格式 |
date_nanos | 日期纳秒类型 |
boolean | 布尔类型 |
binary | 二进制 |
复杂类型
类型 | 描述 |
---|---|
object | json对象嵌套类型。文档可以包含内部对象, 内部对象也可以包含内部对象 |
nested | 嵌套类型是对象数据类型的一个特例, 可以让array类型的对象被独立索引和搜索. |
数组 | 数组不需要一个特定的数据类型,任何字段都默认可以包含一个或多个值,当然,这多个值都必须是字段指定的数据类型。 |
geo-point | 地理点类型,地理点类型用于存储地理位置的经纬度。 |
geo-shape | 地理形状类型 |
multi-fields | Multi-fields 通常用来以不同的方式或目的索引同一个字段。比如,一个字符串类型字段可以同时被映射为 text 类型以用于全文检索、 keyword字段用于排序或聚合。 |
特殊类型
类型 | 描述 |
---|---|
ip | ip (IPv4 和 IPv6 地址) |
completion | to provide auto-complete suggestions |
token_count | to count the number of tokens in a string |
murmur3 | to compute hashes of values at index-time and store them in the index |
annotated-text | to index text containing special markup (typically used for identifying named entities) |
percolator | Accepts queries from the query-dsl |
join | Defines parent/child relation for documents within the same index |
alias | Defines an alias to an existing field |
rank feature | Record numeric feature to boost hits at query time. |
rank features | Record numeric features to boost hits at query time |
dense vector | Record dense vectors of float values |
sparse vector | Record sparse vectors of float values. |
search-as-you-type | A text-like field optimized for queries to implement as-you-type completion |
二、Elasticsearch 基础操作
使用REST API可以对进行Elasticsearch 操作,详细的操作,查看官方文档:Elasticsearch 官方文档
接下来,使用Elasticsearch python客户端对es 操作。
首先安装Elasticsearch python库
pip install elasticsearch-dsl elasticsearch
elasticsearch-dsl
是对elasticsearch
库进一步封装,也添加了elasticsearch
中没有的复杂数据类型。
这两个库的官方文档如下:
首先启动一个elasticsearch环境:
from elasticsearch import Elasticsearch, helpers
from elasticsearch_dsl import Search, Q
hosts = 'http://localhost:9200'
es = Elasticsearch(hosts=hosts)
s = Search(using=es)
# elasticsearch的启动信息
es.info()
2.1 对index操作
elasticsearch库中,对index进行操作,都是在es.indices.
下面的方法中进行操作的。
下面是简单的对index增加、删除和判断是否存在的操作:
# 判断index是否存在
es.indices.exists(index='world')
# 创建index
es.indices.create(index='blog')
# 删除index
es.indices.delete(index='blog')
有时我们需要设置数据结构和类型,来创建index,需要设置mappings
:
# 设置创建数据的结构和类型,以及index的参数
body = {
"mappings": {
"properties": {
"group": {"type": "text"},
"performer": {"type": "nested"} # 嵌套数据类型
}
}
}
es.indices.create(index="demo3", **body)
有时我们需要根据模板创建index,首先创建一个模板es.indices.put_template
,mappings
中是数据的结构和类型:
# 创建模板
body = {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
}
}
},
"settings": { # 设置
"number_of_shards": 1 # 分片数量
},
"index_patterns": ["te*", "bar*"],
# "priority": 500, # 优先级
"version": 0, # 版本号
}
es.indices.put_template(name='template_1', **body)
# 判断模板是否存在
es.indices.exists_template(name='template_1')
# 根据模板创建索引
es.indices.create(index='test1')
2.2 对document操作
对文档数据进行操作,在es.
下面进行操作:
# 插入数据,一般只需要设置id和document就可以了
# 若没有index,则会动态创建一个index,并插入数据
dody = {
"id": 0,
"document": {
"name": "wangwu",
"age":20
}
}
es.index(index='world', **dody)
# 更新数据,
# id是需要更新的文档id;doc是需要更新的数据。
body = {
"id": 0,
"doc": {
"age": 25
}
}
es.update(index='world', **body)
# 删除数据,删除对应id的文档
es.delete(index='world',id=5)
查询数据:
# get是获取一条文档
es.get(index='world',id='3')
# search是根据查询语句,对index进行查询
# index可省略,这样是在全局进行搜索。
body = {
"query": {
"match_all": {}
}
}
es.search(index='world', **body)
搜索语句的语法在下面详细说明。
三、Elasticsearch 查询 DSL
Elasticsearch 查询 DSL大致关键字,首先浏览一下:
query: 查询语句
match 匹配,可进行模糊匹配
match_phrase 完全匹配
match_all 全部输出
term 完全匹配
terms 一个字段,多值完全匹配
multi_match 多字段,一个值匹配
query
fields
prefix 前缀
wildcard 模糊匹配 ?代表一个字符,*代表0个或多个字符
regexp 正则匹配
bool 条件查询
must 全都满足,相当于and,计入得分
filter 全都满足,相当于and,不计入得分
must_not 全都不满足
should 有一个满足,相当于or
exists 字段存在
range 范围查询
field 具体字段
gte 大于等于
lt 小于
sort 排序语句
field 具体字段
order: desc/asc 升降序
aggs 聚合字段
num
size limit,限制输出的量
scroll 滚动输出,设置滚动存在的时间
3.1 query 部分
- match
match是模糊匹配,是对分词之后的结果(text类型)进行匹配。
body = {
'query': {
'match': {
'message': 'hello' # 只要message中包括hello,就会返回数据
}
}
}
es.search(index='world', **body)['hits']['hits']
- match_phrase
match_phrase是完全匹配, 对分词之前的文本进行匹配
body = {
'query': {
'match_phrase': {
'message': 'hello' # message为hello的文档,才会返回
}
}
}
es.search(index='world', **body)['hits']['hits']
- match_all
match_all全部输出。
- term
term 完全匹配,才会返回文档。
body = {
'query': {
'term': {
'message': 'hello' # message为hello的文档,才会返回
}
}
}
es.search(index='world', **body)['hits']['hits']
- terms
一个字段多值匹配的
body = {
'query': {
'terms': {
'message': ['hello', 'world'] # message为 hello或world 的文档,才会返回
}
}
}
es.search(index='world', **body)['hits']['hits']
- multi_match
multi_match: 多字段匹配某个值
body = {
'query': {
'multi_match': {
'query': 'zhangsan',
'fields': ['name', 'info']
}
}
}
es.search(index='world', **body)['hits']['hits']
- prefix
prefix根据前缀进行模糊匹配
body = {
'query': {
'prefix': {
'name': 'zhang'
}
}
}
es.search(index='world', **body)['hits']['hits']
- wildcard
wildcard 模糊匹配,?代表一个字符,*代表0个或多个字符
body = {
'query': {
'wildcard': {
'name': '?hang*'
}
}
}
es.search(index='world', **body)['hits']['hits']
- regexp
regexp:根据正则表达式进行匹配。
body = {
'query': {
'regexp': {
'name': '\w+'
}
}
}
es.search(index='world', **body)['hits']['hits']
-
bool
bool :条件查询。其中有四种类型:must、filter、must_not、should
- must :全部条件都需要满足,相当于and,返回结果计入得分。
- filter:全部条件都需要满足,相当于and,返回结果不计入得分。
- must_not:全都不满足
- should:有一个满足就可以,相当于or
query = {
'bool': {
'must': [
{'regexp': {'name': '\w+'}},
{'term': {'age': 20}}
],
'should': [
{'regexp': {'name': 'wangwu'}},
{'term': {'age': 24}}
],
'must_not':[
{'match': {'name': 'wangwu'}},
{'term': {'age': 24}}
],
'should': [
{'match': {'name': 'lisi'}},
{'terms': {'age': [20, 24]}}
]
}
}
es.search(index='world',query=query)['hits']['hits']
- exists
exists:查询有某个字段的文档
body = {
'query': {
'exists': {
'field': 'name'
}
}
}
es.search(index='world', **body)
- range
range 范围查询。gte
:大于等于;lt
: 小于
body = {
'query': {
'range': {
'age': {
'gte': 20,
'lt': 30
}
}
}
}
es.search(index='world', **body)
…其他查询
3.2 其他部分
3.2.1 排序字段
sort,可设置输出文档的顺序。
body = {
'query': {
'match_all': {}
},
'sort': {
'age': {
'order': 'desc' # 根据年龄降序排序
}
}
}
es.search(index='world', **body)
3.2.2 聚合字段
3.2.3 size和scroll
size,相当于limit,限制输出的条数。
scroll,滚动数据,当一个查询的结果有很多数据时,可以使用滚动查询。此参数是设置滚动查询存在的时间。
# 翻页查询 scroll
query = {
'match_all': {}
}
size = 2
data = []
# 第一次查询
# 参数scroll的值是设置的 scroll 存活的时间,这里设置了2m,也就是2分钟
res = es.search(index='world', query=query, scroll='2m', size=size)
data.extend(res['hits']['hits'])
# 计算需要翻页查询的次数
total_num = res['hits']['total']['value']
times = total_num//size if total_num%size == 0 else total_num//size+1
# 滚动查询
for _ in range(times-1):
res = es.scroll(scroll_id = res['_scroll_id'], scroll='1m')
data.extend(res['hits']['hits'])
四、批量操作数据
4.1 批量插入/更新/删除数据
使用helpers.bulk(es, action)
可以批量处理数据。
其中action是一个包含操作的迭代器,尽量使用生成器, 这样大批量的数据就不用加载到内存中了。
actions, 中迭代的对象,尽量与search()返回值相同,类似如下:
{
'_index': 'index-name',
'_id': 42,
'_routing': 5,
'pipeline': 'my-ingest-pipeline',
'_source': {
"title": "Hello World!",
"body": "..."
}
}
如果_source不存在,它会从文档中弹出所有元数据字段,并将其余部分用作文档数据:
{
"_id": 42,
"_routing": 5,
"title": "Hello World!",
"body": "..."
}
除了默认的添加文档,可以设置_op_type值,为index/delete/update,用于删除或更新值,如下:
# 添加
{
'_op_type': 'index',
'_index': 'index-name',
'_id': 42,
'_source': {'question': 'The life, universe and everything.'}
}
# 更新
{
'_op_type': 'update',
'_index': 'index-name',
'_id': 42,
'_source': {'question': 'The life.'}
}
# 删除
{
'_op_type': 'delete',
'_index': 'index-name',
'_id': 42,
}
4.2 更新插入数据upsert
在一个kafka同步es的项目中,有两个topic需要消费,且需要把这个两个数据合并,于是就有了下面的需求:
- 当数据不存在时,插入数据。
- 当数据存在时,更新数据。
对于每条数据都进行判断的话,效率太低,于是就有了upsert
这个方法。
方法一:
具体参考:
{
"_op_type": '',
'_index': '',
'_id': 1,
'_source': {
'upsert': data,
'script': {
'source': script,
'params': data
}
}
}
执行的逻辑是:
- 若id不存在,则直接插入
upsert
的数据。 - 若id存在,则执行
script
脚本。
所以把script脚本写成更新脚本,就可以起到更新数据的效果。
具体实现:
data = {
'id': 0,
'name': 'zhang'
}
# 创建更新脚本
script = ''
for k in data.keys():
script += 'ctx._source.{0}=params.{0};'.format(k)
action = [{
'_op_type': 'update',
'_index': 'test',
'_id': data['id'],
'_source': {
'upsert': data,
'script': {
'source': script,
'params': data
}
}
}]
helpers.bulk(es, action)
方法二(简单):
可以设置参数"doc_as_upsert" : true
具体实现:
data = {
'id': 0,
'name': 'zhang'
}
action = [{
'_op_type': 'update',
'_index': 'world',
'_id': data['id'],
'doc_as_upsert' : True,
'doc': data
}]
helpers.bulk(es, action)
五、文本分析
基本定义内容:[text analyze](./text analyze.md)
默认情况下,Elasticsearch 使用standard
分析器进行所有文本分析。该standard
分析器为您提供对大多数自然语言和用例的开箱即用支持。如果您选择按原样使用standard
分析器,则无需进一步配置。
如果标准分析器不符合您的需求,请查看并测试 Elasticsearch 的其他内置分析器。内置分析器不需要配置,但有一些支持选项可用于调整其行为。例如,您可以使用standard
要删除的自定义停用词列表配置分析器。
如果没有适合您需求的内置分析器,您可以测试并创建自定义分析器。自定义分析器涉及选择和组合不同的 分析器组件,让您更好地控制过程。
5.1 测试分析器
analyze
API是查看分析器生成术语的工具。可以在请求中内联指定内置分析器:
POST _analyze
{
"analyzer": "whitespace",
"text": "The quick brown fox."
}
在python API中使用:
body = {
'analyzer': 'whitespace',
'text': "The quick brown fox."
}
es.indices.analyze(body=body)
它将返回:
{
"tokens": [
{
"token": "The",
"start_offset": 0,
"end_offset": 3,
"type": "word",
"position": 0
},
{
"token": "quick",
"start_offset": 4,
"end_offset": 9,
"type": "word",
"position": 1
},
{
"token": "brown",
"start_offset": 10,
"end_offset": 15,
"type": "word",
"position": 2
},
{
"token": "fox.",
"start_offset": 16,
"end_offset": 20,
"type": "word",
"position": 3
}
]
}
其中包括分词的结果,开始分词的偏移量,结束分词的偏移量,类型和分词流的位置。
一个分析器包括:
-
零个或多个字符过滤器
char_filter
-
一个分词器
tokenizer
-
零个或多个令牌过滤器
filter
比如使用自定义的分析器:
POST _analyze
{
"tokenizer": "standard",
"filter": [ "lowercase", "asciifolding" ],
"text": "Is this déja vu?"
}
python API
body = {
"tokenizer": "standard",
"filter": [ "lowercase", "asciifolding" ],
"text": "Is this déja vu?"
}
es.indices.analyze(body=body)
它将返回:
{
"tokens": [
{
"token": "is",
"start_offset": 0,
"end_offset": 2,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "this",
"start_offset": 3,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "deja",
"start_offset": 8,
"end_offset": 12,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "vu",
"start_offset": 13,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 3
}
]
}
位置和字符偏移
从
analyze
API 的输出可以看出,Analyzer 不仅将词转换为词,还记录每个词的顺序或相对位置(用于短语查询或词邻近查询),以及每个词的开始和结束字符偏移量。
当然也可以在特定索引上运行analyze
,API可以参考custom
分析器:
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"std_folded": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "std_folded"
}
}
}
}
GET my-index-000001/_analyze
{
"analyzer": "std_folded",
"text": "Is this déjà vu?"
}
GET my-index-000001/_analyze
{
"field": "my_text",
"text": "Is this déjà vu?"
}
python API
body = {
"settings": {
"analysis": {
"analyzer": {
"std_folded": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "std_folded"
}
}
}
}
# 创建index
es.indices.create(index='my-index-000001', **body)
# 可以指定分析器,或者指定某个text字段(使用它的分析器)
body = {
"analyzer": "std_folded",
"text": "Is this déjà vu?"
}
body = {
"field": "my_text",
"text": "Is this déjà vu?"
}
# 使用index中的分析器
es.indices.analyze(index='my-index-000001', body=body)
输出结果和上面单独使用analyze一样。
5.2 内置的分析器
内置分析器无需任何配置即可直接使用。但是,其中一些支持配置选项以改变其行为。例如,可以将standard
分析器配置为支持停用词列表:
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"std_english": {
"type": "standard",
"stopwords": "_english_"
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "standard",
"fields": {
"english": {
"type": "text",
"analyzer": "std_english"
}
}
}
}
}
}
POST my-index-000001/_analyze
{
"field": "my_text",
"text": "The old brown cow"
}
POST my-index-000001/_analyze
{
"field": "my_text.english",
"text": "The old brown cow"
}
python API:
body = {
"settings": {
"analysis": {
"analyzer": {
"std_english": {
"type": "standard",
"stopwords": "_english_"
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "standard",
"fields": {
"english": {
"type": "text",
"analyzer": "std_english"
}
}
}
}
}
}
es.indices.create(index='my-index-000001', **body)
body = {
'field': 'my_text',
'text': 'The old brown cow'
}
# and
body = {
'field': 'my_text.english',
'text': 'The old brown cow'
}
es.indices.analyze(index='my-index-000001', body=body)
我们将std_english
分析器定义为基于standard
分析器,但配置为删除预定义的英语停用词列表。
该my_text
字段standard
直接使用分析仪,无需任何配置。不会从此字段中删除停用词。 分词的结果是:[ the, old, brown, cow ]
该my_text.english
字段使用std_english
分析器,因此将删除英文停用词。分词的结果是: [ old, brown, cow ]
5.3 自定义分析器
当内置分析器不能满足您的需求时,您可以创建一个 custom
,它需要满足:
5.3.1 配置
custom
分析器接受以下参数:
配置 | 说明 |
---|---|
type | 分析仪类型。接受内置分析器类型。对于自定义分析器,使用custom 或省略此参数。 |
tokenizer | 内置或定制的分词器。(必需的) |
char_filter | 可选的内置或自定义 字符过滤器数组。 |
filter | 可选的内置或自定义 令牌过滤器数组。 |
position_increment_gap | 当索引一个文本值数组时,Elasticsearch 在一个值的最后一个词和下一个值的第一个词之间插入一个假的“间隙”,以确保一个短语查询不匹配来自不同数组元素的两个词。默认为100 . 查看position_increment_gap 更多 |
5.3.2 自定义分析器示例
示例一
这是一个结合了以下内容的示例:
-
字符过滤器
-
分词器
-
令牌过滤器
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"html_strip"
],
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
}
}
POST my-index-000001/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "Is this <b>déjà vu</b>?"
}
python API
body = {
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"html_strip"
],
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
}
}
es.indices.put_settings(index='my-index-000001', body=body)
# 使用上面的put_settings时,需要先关闭index ,使用时再打开index
# es.indices.close(index='')
# es.indices.open(index='')
body = {
"analyzer": "my_custom_analyzer",
"text": "Is this <b>déjà vu</b>?"
}
es.indices.analyze(index='my-index-000001', body=body)
返回的结果是:
[ is, this, deja, vu ]
关闭索引,可以修改index的分析器和相关性参数
python api使用
put_settings
和put_mappings
前面的示例使用了标记器、标记过滤器和字符过滤器及其默认配置,但可以创建每个配置的版本并在自定义分析器中使用它们。
示例二
这是一个更复杂的示例,它结合了以下内容:
-
字符过滤器
映射字符过滤器,配置为替换
:)
为_happy_
和:(``_sad_
-
分词器
Pattern Tokenizer,配置为在标点符号上拆分
-
令牌过滤器
Stop Token Filter,配置为使用预定义的英文停用词列表
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"char_filter": [
"emoticons"
],
"tokenizer": "punctuation",
"filter": [
"lowercase",
"english_stop"
]
}
},
"tokenizer": {
"punctuation": {
"type": "pattern",
"pattern": "[ .,!?]"
}
},
"char_filter": {
"emoticons": {
"type": "mapping",
"mappings": [
":) => _happy_",
":( => _sad_"
]
}
},
"filter": {
"english_stop": {
"type": "stop",
"stopwords": "_english_"
}
}
}
}
}
POST my-index-000001/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "I'm a :) person, and you?"
}
python API:
body = {
'settings': {
'analysis': {
'analyzer': {
'my_custom_analyzer': {
'char_filter': ['emoticons'],
'tokenizer': 'punctuation',
'filter': ['lowercase', 'english_stop']
}
},
'tokenizer': {
'punctuation': {
'type': 'pattern',
'pattern': '[ .,!?]'
}
},
'char_filter': {
'emoticons': {
'type': 'mapping',
'mappings': [
':) => _happy_',
':( => _sad_'
]
}
},
'filter': {
'english_stop': {
'type': 'stop',
'stopwords': '_english_'
}
}
}
}
}
es.indices.put_settings(index='my-index-000001', body=body)
# 记得先关闭index
body = {
'analyzer': 'my_custom_analyzer',
'text': "I'm a :) person, and you?"
}
es.indices.analyze(index='my-index-000001', body=body)
为索引分配一个默认的自定义分析器,my_custom_analyzer
. 此分析器使用稍后在请求中定义的自定义标记器、字符过滤器和标记过滤器。该分析器也省略了type
参数。
它将返回:
[ i'm, _happy_, person, you ]
5.4 指定分析器的方式
Elasticsearch 提供了多种方法来指定内置或自定义分析器:
- 按
text
字段,分词 - 对于索引的搜索时间
把事情简单化
在不同级别和不同时间指定分析仪的灵活性非常好……但仅在需要时。
在大多数情况下,一个简单的方法效果最好:为每个
text
字段指定一个分析器,如为字段指定分析器中所述。这种方法适用于 Elasticsearch 的默认行为,让您可以使用相同的分析器进行索引和搜索。它还可以让您使用get mapping API快速查看哪个分析器适用于哪个字段。
如果您通常不为索引创建映射,则可以使用 索引模板来实现类似的效果。
5.4.1 Elasticsearch 索引分析器
Elasticsearch 通过依次检查以下参数来确定要使用的索引分析器:
- 字段的
analyzer
映射参数。请参阅为字段指定分析器。 analysis.analyzer.default
索引设置 。请参阅为索引指定默认分析器。
如果没有指定这些参数, 则使用standard
分析器。
5.4.1.1 指定字段的分析器
映射索引时,可以使用analyzer
映射参数为每个text
字段指定一个分析器。
以下创建索引 API请求将 whitespace
分析器设置为title
字段的分析器。
PUT my-index-000001
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "whitespace"
}
}
}
}
5.4.1.2 指定索引的默认分析器
除了字段级分析器之外,您还可以设置备用分析器以使用该analysis.analyzer.default
设置。
以下创建索引 API请求将 simple
分析器设置为my-index-000001
.
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"default": {
"type": "simple"
}
}
}
}
}
5.4.2 Elasticsearch 搜索分析器
在大多数情况下,不需要指定不同的搜索分析器。这样做可能会对相关性产生负面影响并导致意外的搜索结果。
如果您选择指定单独的搜索分析器,我们建议您在部署到生产环境之前彻底测试您的分析配置。
在搜索时,Elasticsearch 通过依次检查以下参数来确定要使用的分析器:
- 搜索查询中的
analyzer
参数。请参阅为查询指定搜索分析器。 - 字段的
search_analyzer
映射参数。请参阅为字段指定搜索分析器。 analysis.analyzer.default_search
索引设置 。请参阅为索引指定默认搜索分析器。- 字段的
analyzer
映射参数。请参阅为字段指定分析器。
如果没有指定这些参数, 则使用standard
分析器。
5.4.2.1 指定查询的搜索分析器
编写全文查询时,可以使用analyzer
参数指定搜索分析器。如果提供,它将覆盖任何其他搜索分析器。
以下搜索 API请求将stop
分析器设置为match
查询的搜索分析器。
GET my-index-000001/_search
{
"query": {
"match": {
"message": {
"query": "Quick foxes",
"analyzer": "stop"
}
}
}
}
python API
body = {
'query': {
'match': {
'message': {
'query': 'Quick foxes',
'analyzer': 'stop'
}
}
}
}
es.search(index='my-index-000001', **body)
5.4.2.2 指定字段的搜索分析器
映射索引时,您可以使用search_analyzer
映射参数为每个text
字段指定搜索分析器。
如果提供了搜索分析器,则还必须使用analyzer
参数指定索引分析器。
以下创建索引 API请求将 simple
分析器设置为title
字段的搜索分析器。
PUT my-index-000001
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "whitespace",
"search_analyzer": "simple"
}
}
}
}
5.4.2.3 指定索引的默认搜索分析器
创建索引时,您可以使用该设置设置默认搜索分析器analysis.analyzer.default_search
。
如果提供了搜索分析器,则还必须使用该analysis.analyzer.default
设置指定默认索引分析器。
以下 创建索引 API请求将 whitespace
分析器设置为my-index-000001
索引的默认搜索分析器。
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"default": {
"type": "simple"
},
"default_search": {
"type": "whitespace"
}
}
}
}
}
六、别名alias
别名是一组数据流或索引的辅助名称。大多数 Elasticsearch API 接受别名来代替数据流或索引名称。
您可以随时更改别名的数据流或索引。如果您在应用程序的 Elasticsearch 请求中使用别名,则可以在不停机或更改应用程序代码的情况下重新索引数据。
6.1 别名类型
有两种类型的别名:
- 数据流别名指向一个或多个数据流 。
- 索引别名指向一个或多个索引 。
别名不能同时指向数据流和索引。也不能将数据流的支持索引添加到索引别名。
6.2 添加别名
要将现有数据流或索引添加到别名,请使用 aliases API的add
操作。如果别名不存在,则请求创建它.
POST _aliases
{
"actions": [
{
"add": {
"index": "logs-nginx.access-prod",
"alias": "logs"
}
}
]
}
python api
es.indices.put_alias(index='world', name='world-test1')
API的index和indices参数支持通配符(*)。同时匹配数据流和索引的通配符模式将返回错误。
POST _aliases
{
"actions": [
{
"add": {
"index": "logs-*",
"alias": "logs"
}
}
]
}
python api
es.indices.put_alias(index='wor*', name='world-test1')
6.3 删除别名
要删除别名,使用 aliases API 的remove
操作。
POST _aliases
{
"actions": [
{
"remove": {
"index": "logs-nginx.access-prod",
"alias": "logs"
}
}
]
}
python api
es.indices.delete_alias(index='world', name='world-test1')
6.4 别名多个操作
可以使用aliases API 在单个原子操作中执行多个操作。
例如,logs
别名指向单个数据流。以下请求将流交换为别名。在此交换期间,logs
别名没有停机时间,并且永远不会同时指向两个流。
POST _aliases
{
"actions": [
{
"remove": {
"index": "logs-nginx.access-prod",
"alias": "logs"
}
},
{
"add": {
"index": "logs-my_app-default",
"alias": "logs"
}
}
]
}
6.5 其他操作
具体详见:官方别名操作
引用:
“whitespace”
}
}
}
}
}
### 六、别名alias
别名是一组数据流或索引的辅助名称。大多数 Elasticsearch API 接受别名来代替数据流或索引名称。
您可以随时更改别名的数据流或索引。如果您在应用程序的 Elasticsearch 请求中使用别名,则可以在不停机或更改应用程序代码的情况下重新索引数据。
#### 6.1 别名类型
有两种类型的别名:
- **数据流别名**指向一个或多个数据流 。
- **索引别名**指向一个或多个索引 。
别名不能同时指向数据流和索引。也不能将数据流的支持索引添加到索引别名。
#### 6.2 添加别名
要将现有数据流或索引添加到别名,请使用 [aliases API](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html)的`add`操作。如果别名不存在,则请求创建它.
```json
POST _aliases
{
"actions": [
{
"add": {
"index": "logs-nginx.access-prod",
"alias": "logs"
}
}
]
}
python api
es.indices.put_alias(index='world', name='world-test1')
API的index和indices参数支持通配符(*)。同时匹配数据流和索引的通配符模式将返回错误。
POST _aliases
{
"actions": [
{
"add": {
"index": "logs-*",
"alias": "logs"
}
}
]
}
python api
es.indices.put_alias(index='wor*', name='world-test1')
6.3 删除别名
要删除别名,使用 aliases API 的remove
操作。
POST _aliases
{
"actions": [
{
"remove": {
"index": "logs-nginx.access-prod",
"alias": "logs"
}
}
]
}
python api
es.indices.delete_alias(index='world', name='world-test1')
6.4 别名多个操作
可以使用aliases API 在单个原子操作中执行多个操作。
例如,logs
别名指向单个数据流。以下请求将流交换为别名。在此交换期间,logs
别名没有停机时间,并且永远不会同时指向两个流。
POST _aliases
{
"actions": [
{
"remove": {
"index": "logs-nginx.access-prod",
"alias": "logs"
}
},
{
"add": {
"index": "logs-my_app-default",
"alias": "logs"
}
}
]
}
6.5 其他操作
具体详见:官方别名操作
引用: