一、索引
ES中索引类似于MySQL的表,在MySQL中,我们使用Create创建表,在ES中我们使用什么方式?在2 Elasticsearch基本概念中说过mapping类比于MySQL对应的表结构-Schema。所以索引的创建使用的是Mapping。
1.1mapping介绍
Mapping类似数据库中的schema的定义,会把Json文档映射成Lucene所需要的扁平格式,作用如下
- 定义索引中的字段的名称
- 定义字段的数据类型,如字符串,数字,布尔等
- 字段倒排索引的相关配置,如Analyzed or Not Analyzed
1.2动态mapping
介绍
- 在写入文档时,如果索引不存在,会自动创建索引
- Dynamic Mapping机制,使得我们无需手动定义Mappings。Elasticsearch会自动根据文档信息,推算出字段的类型
- 但有时推算的不对,例如地理位置信息推算成text格式。
- 如果类型设置的不对,会导致一些功能无法正常运行,如Range查询
实战
创建索引
PUT mapping_test/_doc/1
{
"firstName":"Chan",
"lastName": "Jackie",
"loginDate":"2018-07-24T10:29:48.103Z"
}
查看索引
GET mapping_test/_mapping
删除索引
DELETE mapping_test
修改索引
mapping中有个dynamic字段,有三个值,true(默认)、false、strict
- 为true时:一旦有新增字段的文档写入,mapping也同时被更新
- 为false时:mapping不会被更新,新增字段的数据无法被索引,但是信息会出现在_source中
- 为strict时:文档写入失败
对已有字段,一旦已经有数据写入,就不再支持修改字段定义,因为Lucene实现的倒排索引,一旦生成后,就不允许修改。如果希望修改字段类型,必须reindex api,重建索引
创建索引
PUT dynamic_mapping_test/_doc/1
{
"newField":"someValue"
}
索引dynamic设置为false
PUT dynamic_mapping_test/_mapping
{
"dynamic": false
}
//查看索引
GET dynamic_mapping_test/_mapping
{
"dynamic_mapping_test" : {
"mappings" : {
"dynamic" : "false",
"properties" : {
"newField" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
增加新字段
PUT dynamic_mapping_test/_doc/10
{
"anotherField":"someValue"
}
1.3显式mapping
介绍
动态索引有时候不太准确,我们可以显示的进行配置。纯手写可能出现错误,所以我们可以准备一些样本数据,使用动态方案创建临时index,获取临时index,进行修改后创建真正的索引,最后把临时索引删除。
实战
PUT users
{
"settings" : {
"index" : {
"number_of_shards" : 1,
"number_of_replicas" : 1
},
"analysis": {
"analyzer": {
"custom_analyzer":{
"type":"custom",
"char_filter":["html_strip"],
"tokenizer": "whitespace",
"filter": ["stop","snowball"]
}
}
}
},
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text",
"copy_to": "fullName"
},
"lastName" : {
"type" : "text",
"copy_to": "fullName"
},
"mobile" : {
"type" : "text",
"index": false
},
"bio" : {
"type" : "text",
"index_options":"offsets"
},
"interests" : {
"type" : "text"
},
"company" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"comment" : {
"type" : "text",
"fields" : {
"english_comment" : {
"type" : "text",
"analyzer" : "english",
"search_analyzer":"english"
}
}
}
}
}
}
- index字段控制是否被索引,默认为true,如果设置为false,该字段不可被搜索
- Index_options:有四种不同级别的配置,控制倒排索引记录的内容。text类型默认postions,其它默认docs。记录内容越多,占用存储空间越大
- docs,记录doc id
- freqs,记录doc id和term frequencies
- positions,记录doc id、term frequencies、term position
- offsets,记录doc id、term frequencies、term position、character offects
- copy_to:满足一些特定搜索需求,会将字段的数值拷贝到目标字段,用于搜索,但目标字段不出现在_source中
- 数组类型:ES不提供专门的数组类型,但是任何字段,都可以包含多个相同类型的数值
- 多字段类型:给同一个字段增加了多种搜索方式
- 如增加keyword字段,实现精确匹配
- 如使用不同的analyzer,更好的实现基于业务场景的搜索
- char_filter:在tokenizer之前对文本进行处理,例如增加删除及替换字符,可配置多个char_filter
- tokenizer:将原始的文本按照一定的规则,切分为词(term或token),有一些内置的tokenizers,如whitespace/standard/pattern等
- filter:将tokenizer输入的单词(term),进行增加、修改、删除,自带的如Lowercase/stop/synonym等
GET _analyze
{
"char_filter":["html_strip"],
"tokenizer": "whitespace",
"filter": ["stop","snowball"],
"text": "<b>hello world, Jason</b>"
}
如果索引中配置了某种analyzer,可以如此使用
POST users/_analyze
{
"analyzer":"custom_analyzer",
"text": "<b>hello world, Jason</b>"
}
- keyword和text的区别:keyword不需要分词,一般包括数字、日期或者具体的字符串
- 为了方便创建索引,我们还可以使用template
二、文档
在执行操作的时候,有一些常见错误码
2.1创建文档
Index方式
- 如果Index不存在,会创建Index
- 如果ID不存在,创建新的文档,如果ID已存在,先删除现有文档,再创建新文档,版本增加
POST users/_doc/1
{
"user" : "Mike",
"post_date" : "2019-04-15T14:12:12",
"message" : "trying out Kibana"
}
Create方式
- 支持指定文档id和自动生成文档id两种方式
- 如果ID已存在,则创建失败
PUT users/_create/3
{
"user" : "Jack",
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
POST users/_doc
{
"user" : "Mike",
"post_date" : "2019-04-15T14:12:12",
"message" : "trying out Kibana"
}
2.2查询文档
GET users/_doc/3
2.3更新文档
- update不会删除原来的文档,只会对相应字段做增量修改,版本增加
- 文档必须已经存在
- 可以增加新的字段
POST users/_update/3/
{
"doc":{
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch",
"now":"hello"
}
}
2.4删除文档
DELETE users/_doc/3/
2.5Bulk API
- 支持在一次API调用中,对不同的索引进行操作
- 支持四种类型Index、Create、Update、Delete,index、create、update后续要配置操作的数据
- 操作中单条操作失败,并不会影响其它操作
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
2.6批量读取mget
- 能够减少网络连接所产生的开销,提高性能
GET /_mget
{
"docs" : [
{
"_index" : "test",
"_id" : "1"
},
{
"_index" : "test",
"_id" : "2"
}
]
}
GET /test/_mget
{
"docs" : [
{
"_id" : "1"
},
{
"_id" : "2"
}
]
}
GET /_mget
{
"docs" : [
{
"_index" : "test",
"_id" : "1",
"_source" : false
},
{
"_index" : "test",
"_id" : "2",
"_source" : ["field3", "field4"]
},
{
"_index" : "test",
"_id" : "3",
"_source" : {
"include": ["user"],
"exclude": ["user.location"]
}
}
]
}
2.7批量查询msearch
POST test2/_msearch
{}
{"query" : {"match_all" : {}},"size":1}
{"index" : "test"}
{"query" : {"match_all" : {}},"size":2}
三、搜索
3.1介绍
ES有两种搜索方式
- URI Search:在URL中使用查询参数
- Request Body Search:使用ES提供的,基于JSON格式的更加完备的Query Domain Specific Language(DSL)。这种方式更加高级,一般建议使用这种方式。
3.2 Query String Syntax(查询语法)
指定字段 vs 泛查询
-
q=title:2012 -> 查询title字段包含2012的
-
q=2012 -> 查询所有字段中包含2012的
Term Query vs Phrase Query
- Beautiful Mind:等效于Beautiful OR Mind,查询字段中包含Beautiful或者Mind的
- ”Beautiful Mind“:等效于Beautiful AND Mind,查询字段中同时包含Beautiful和Mind,要求前后顺序保持一致
分组
- title:(Beautiful Mind) -> 表明Beautiful和Mind是一块的,有点类似于数学表达式中的括号,用于明确操作的优先级。
- title:(+Beautiful -Mind) -> +标识must,-标识must_not
布尔操作
- AND/OR/NOT 或者 &&/ || /!,必须大写
- title:(Beautiful NOT Mind) -> 查询包含Beautiful不包含Mind的
范围查询
区间查询:[]闭区间,{}开区间
-
year:{2018 TO 2019]
-
year:[* TO 2018]
算数符号
- year:>2010
- year:(>2010 AND <=2018) 查询大于2010年,小于等于2018年的电影
通配符查询
通配符查询效率低,占用内存大,不建议使用。?代表一个字符,*代表0或者多个字符
- title:mi?d
- title:be*
模糊匹配与近似查询
在使用term
查询或者match
查询时,如果在查询的字符串后面加上~
,可以实现近似匹配。这种模糊查询在处理拼写错误、用户输入可能存在小偏差等场景下非常有用,它可以扩大搜索结果的范围,提高搜索的召回率,使得即使输入的查询词不完全准确,也能找到相关的文档。
- title:beautifl~1
- title:“lord rings”~2
3.3实战
3.3.1查询指定的索引
集群上所有的索引
GET _search
指定索引
GET movies/_search
指定多个索引
GET test,test2/_search
指定开头的索引
GET test*/_search
3.3.2URI搜索
URI的样式如下,大家可以按照查询语法中的方式随意进行更改
- 使用q指定查询字符串,使用query string syntax
- df默认字段,不指定时,会对所有字段进行查询
- df和q是配合使用的,如果有df,则意味df的字段值为q
- 可以不使用df,q中直接写明字段和要查询的值
- sort排序方式
- from,size用于分页
- profile查看查询是如何被执行的
GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout=1s
{
"profile":"true"
}
3.3.3Request Body搜索
特殊配置
- 分页:从0开始,默认返回10个结果。获取靠后的翻页成本较高,因为在 Elasticsearch 中,当进行分页查询时,例如查询第
n
页(n
较大,即靠后的页面),默认情况下,Elasticsearch 需要从所有匹配的文档中获取到前面n - 1
页的文档后,才能得到第n
页的文档。 - 排序:最好在数字型或者日期型字段上排序。因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值
- sorce filtering:返回指定的字段,支持通配符
- 使用脚本对字段进行处理:
POST /movies/_search
{
"profile": true,
"from":10,
"size":20,
"sort":[{"year":"desc"}],
"_source":["title","year"],
"script_fields": {
"new_field": {
"script": {
"lang": "painless",
"source": "doc['year'].value+'hello'"
}
}
},
"query": {
"match_all": {}
}
}
查询表达式Match
搜索title包含last或者christmas
POST movies/_search
{
"profile": true,
"query": {
"match": {
"title": "last christmas"
}
}
}
搜索title同时包含last和christmas
POST movies/_search
{
"profile": true,
"query": {
"match": {
"title": {
"query": "last christmas",
"operator": "and"
}
}
}
}
短语搜索Match Phrase
在 ES 查询中,match
和match_phrase
的区别在于:
match
:会将查询字符串进行分词,然后在索引中查找与分词后的词项匹配的文档。它会根据查询字段的类型自动调整查询方式,例如对于日期或数值类型的字段,会将查询字符串转换为相应的日期或数值进行比较。match
查询在处理可分词的文本字段时非常有用,可以根据用户输入的关键词进行模糊匹配,找到包含相关词项的文档。match_phrase
:要求查询字符串中的短语按照顺序完整匹配字段内容。它不会对查询字符串进行分词,而是将整个查询字符串作为一个短语进行匹配。match_phrase
查询在需要精确匹配短语或按照特定顺序匹配关键词时非常有用。
查询title包含one love,中间可以有一个词的间隔
POST movies/_search
{
"profile": true,
"query": {
"match_phrase": {
"title":{
"query": "one love",
"slop": 1
}
}
}
}
Query String
类似于URI搜索
- default_field是df,指定要搜索的字段
- fields指查询的多个字段
POST movies/_search
{
"profile": true,
"query": {
"query_string": {
"default_field": "title",
"query": "Beautiful AND Mind"
}
}
}
POST movies/_search
{
"query": {
"query_string": {
"fields":["title","genre"],
"query": "(Beautiful OR Mind) OR (Drama AND Comedy)"
}
}
}
Simple Query String Query
- 类似Query String,但会忽略错误的语法,同时只支持部分查询语法
- 不支持AND OR NOT,会当做字符串处理
- Term之间默认的关系是OR,可以指定Operator
- 支持部分逻辑,+替代AND,|替代OR,-替代NOT
POST movies/_search
{
"profile": true,
"query": {
"simple_query_string": {
"fields": ["title"],
"query": "Beautiful Mind",
"default_operator":"AND"
}
}
}
GET /movies/_search
{
"profile":true,
"query":{
"simple_query_string":{
"fields":["title"],
"query":"Beautiful -mind"
}
}
}
资料
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
往期文章回顾: