Elasticsearch基础概念和Python操作

一、Elasticsearch 基础概念

1.1 基本概念

Elasticsearch 是一个分布式文档存储。Elasticsearch 不是将信息存储为列式数据行,而是存储已序列化为 JSON 文档的复杂数据结构。当集群中有多个 Elasticsearch 节点时,存储的文档会分布在整个集群中,并且可以从任何节点立即访问。

存储文档后,它会在近乎实时的时间内被索引并完全可搜索——在1 秒内。Elasticsearch 使用一种称为倒排索引的数据结构,它支持非常快速的全文搜索。倒排索引列出了出现在任何文档中的每个唯一单词,并标识了每个单词出现的所有文档。

1.2 REST API

Elasticsearch 提供了一个简单、连贯的 REST API,用于管理集群以及索引和搜索数据。

Elasticsearch REST API 支持结构化查询、全文查询和将两者结合的复杂查询。结构化查询类似于你可以在 SQL 中构造的查询类型。例如,你可以搜索索引中的genderage字段并按字段employee作为索引,使用hire_date进行排序。全文查询查找与查询字符串匹配的所有文档,并按相关性排序返回它们——它们与你的搜索词的匹配程度。

除了搜索单个术语外,你还可以执行短语搜索、相似性搜索和前缀搜索,并获得自动完成建议。

有要搜索的地理空间或其他数字数据吗?Elasticsearch 在支持高性能地理和数字查询的优化数据结构中索引非文本数据。

您可以使用 Elasticsearch 全面的 JSON 样式查询语言 ( Query DSL ) 访问所有这些搜索功能。您还可以构建SQL 样式的查询以在 Elasticsearch 内本地搜索和聚合数据,而 JDBC 和 ODBC 驱动程序使广泛的第三方应用程序能够通过 SQL 与 Elasticsearch 进行交互。

1.3 数据类型

Elasticsearch 在mappings参数中定义字段的,类似与数据库中的表结构的定义。

Elasticsearch 主要的基本数据类型有:

  1. 字符类型:text、keyword

    • text: 全文搜索,会被分词处理,如果进行聚合、排序计算需要开启fielddata进行内存预加载,不建议此使用方法。
    • keyword:精准值,不会被分词。进行过滤、排序、聚合等操作时, 就应该使用keyword类型。
  2. 数字类型:long、integer、short、byte、double、float、 half_float、 scaled_float

    尽可能选择范围小的数据类型, 字段的长度越短, 索引和搜索的效率越高,尽可能避免浮点类型,如果必须使用优先考虑使用带缩放因子的浮点类型。

  3. 日期: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"
                    }
                }
            }
        }
    }
    
  4. 日期 纳秒:date_nanos

  5. 布尔型:boolean

    true/false

  6. 二进制:binary

  7. 范围数据类型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" 
                }
            }
        }
    }
    

复杂的数据类型

  1. 对象类型: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"}
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
  2. 嵌套类型: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": {}}
                    }
                }
            }
        }
    }
    
    
  3. 地理数据类型

    1. Geo-point: 地理点类型,地理点类型用于存储地理位置的经纬度对, 可用于:

      • 查找一定范围内的地理点;
      • 通过地理位置或相对某个中心点的距离聚合文档;
      • 将距离整合到文档的相关性评分中;
      • 通过距离对文档进行排序.
      // 添加映射
      {
          "mappings": {
              "developer": {
                  "properties": {
                      "location": {"type": "geo_point"}
                  }
              }
          }
      }
      
    2. Geo-shape: 地理形状类型

  1. 特殊数据类型

    1. IP: ip (IPv4 和 IPv6 地址)
    2. Completion:completion (to provide auto-complete suggestions)
    3. Token count:token_count (to count the number of tokens in a string)
    4. mapper-murmur3:murmur3(to compute hashes of values at index-time and store them in the index)
    5. mapper-annotated-text:annotated-text (to index text containing special markup (typically used for identifying named entities))
    6. Percolator:(Accepts queries from the query-dsl)
    7. Join:(Defines parent/child relation for documents within the same index)
    8. Alias:(Defines an alias to an existing field.)
    9. Rank feature:(Record numeric feature to boost hits at query time.)
    10. Rank features:(Record numeric features to boost hits at query time.)
    11. Dense vector:(Record dense vectors of float values.)
    12. Sparse vector:(Record sparse vectors of float values.)
    13. Search-as-you-type:(A text-like field optimized for queries to implement as-you-type completion)
  2. 数组类型

    在Elasticsearch中,数组不需要一个特定的数据类型,任何字段都默认可以包含一个或多个值,当然,这多个值都必须是字段指定的数据类型。

  3. 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]
float32位单精度浮点数
double64位双精度浮点数
half_float16位半精度IEEE754浮点类型
scaled_float缩放类型的的浮点数,比如price:字段只需精确到分,57.34缩放因子为100,存储结果为5734

文本类型

类型描述
text会被分词处理。如果进行聚合、排序计算需要开启fielddata进行内存预加载,不建议此使用方法。
keyword精准值,不会被分词。进行过滤、排序、聚合等操作时, 就应该使用keyword类型。

范围类型

类型描述
integer_range整数范围
long_range长整数范围
float_range浮点数范围
double_range长浮点数范围
date_range日期范围
ip_rangeip地址范围

其他类型

类型描述
date日期类型,可以设置日期的格式
date_nanos日期纳秒类型
boolean布尔类型
binary二进制

复杂类型

类型描述
objectjson对象嵌套类型。文档可以包含内部对象, 内部对象也可以包含内部对象
nested嵌套类型是对象数据类型的一个特例, 可以让array类型的对象被独立索引和搜索.
数组数组不需要一个特定的数据类型,任何字段都默认可以包含一个或多个值,当然,这多个值都必须是字段指定的数据类型。
geo-point地理点类型,地理点类型用于存储地理位置的经纬度。
geo-shape地理形状类型
multi-fieldsMulti-fields 通常用来以不同的方式或目的索引同一个字段。比如,一个字符串类型字段可以同时被映射为 text 类型以用于全文检索、 keyword字段用于排序或聚合。

特殊类型

类型描述
ipip (IPv4 和 IPv6 地址)
completionto provide auto-complete suggestions
token_countto count the number of tokens in a string
murmur3to compute hashes of values at index-time and store them in the index
annotated-textto index text containing special markup (typically used for identifying named entities)
percolatorAccepts queries from the query-dsl
joinDefines parent/child relation for documents within the same index
aliasDefines an alias to an existing field
rank featureRecord numeric feature to boost hits at query time.
rank featuresRecord numeric features to boost hits at query time
dense vectorRecord dense vectors of float values
sparse vectorRecord sparse vectors of float values.
search-as-you-typeA 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 官方文档

elasticsearch 官方文档2

elasticsearch-dsl 官方文档

首先启动一个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_templatemappings中是数据的结构和类型:

# 创建模板
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)

方法二(简单):

具体参考:elasticsearch中批量的upsert

可以设置参数"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 测试分析器

analyzeAPI是查看分析器生成术语的工具。可以在请求中内联指定内置分析器:

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
    }
  ]
}

位置和字符偏移

analyzeAPI 的输出可以看出,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_settingsput_mappings

前面的示例使用了标记器、标记过滤器和字符过滤器及其默认配置,但可以创建每个配置的版本并在自定义分析器中使用它们。

示例二

这是一个更复杂的示例,它结合了以下内容:

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字段指定一个分析器,如为字段指定分析器中所述

这种方法适用于 Elasticsearch 的默认行为,让您可以使用相同的分析器进行索引和搜索。它还可以让您使用get mapping API快速查看哪个分析器适用于哪个字段。

如果您通常不为索引创建映射,则可以使用 索引模板来实现类似的效果。

5.4.1 Elasticsearch 索引分析器

Elasticsearch 通过依次检查以下参数来确定要使用的索引分析器:

  1. 字段的analyzer映射参数。请参阅为字段指定分析器
  2. 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 通过依次检查以下参数来确定要使用的分析器:

  1. 搜索查询中的analyzer参数。请参阅为查询指定搜索分析器
  2. 字段的search_analyzer映射参数。请参阅为字段指定搜索分析器
  3. analysis.analyzer.default_search索引设置 。请参阅为索引指定默认搜索分析器
  4. 字段的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 APIadd操作。如果别名不存在,则请求创建它.

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 其他操作

具体详见:官方别名操作

引用:

3.elasticsearch数据类型总结

“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 其他操作

具体详见:官方别名操作

引用:

3.elasticsearch数据类型总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力生活的黄先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值