写在前面
本文看下es的mapping的设置。es支持两种mapping,一种式dynamic mapping,另外一种是显式的mapping设置。分别来看下。
在正式开始之前我们需要先看下es提供的字段数据类型:
1:dynamic mapping
我们在使用关系型数据库的时候必须先建表,并指定有哪些字段,什么数据类型,否则将不能保存数据,但是在es中这个建表,并指定有哪些字段,什么数据类型
的过程es可以自动的帮助我们完成,就叫做dynamic mapping,具体是这样子的:
当我们向一个不存在的index插入数据的时候,es会自动的根据我们所插入的数据信息,反推出字段信息,并通过dynamic mapping的机制,自动的给我们创建mapping信息,然后再将文档进行保存,自然,这种自动的动作,肯定不可能百分百符合我们的预期,所以在工作中,还是手动创建比较靠谱些,关于如何手动创建,我们在文章的后边也会一起来看下。
如下我们向一个不存在的index中插入数据:
查看自动生成的mapping信息:
1.1:更改 mapping
分新增字段和已有字段两种情况:
- 新增字段
看图吧!
- 已有字段
不允许修改。
我就非得要修改,咋办,可以使用reindex API,重建索引。
1.2:测试dynamic false和stric
此时新增的字段将无法被搜索,但是可以存储。
如下操作实验:
# 1:写入文档
PUT dynamic_mapping_test/_doc/1
{
"newField": "someValue"
}
# 2:可以搜索到
POST dynamic_mapping_test/_search
{
"query": {
"match": {
"newField": "someValue"
}
}
}
# 3:修改dynamic为false
PUT dynamic_mapping_test/_mapping
{
"dynamic": false
}
# 4:在添加一个新字段,将不能被搜索,因为dynamic设置为false了
PUT dynamic_mapping_test/_doc/10
{
"anotherField": "someValue"
}
# 5:就搜索不到了,因为字段并没有创建倒排,即没有被索引
POST dynamic_mapping_test/_search
{
"query": {
"match": {
"anotherField": "someValue"
}
}
}
# 6:查看mapping信息并没有anotherField
GET dynamic_mapping_test/_mapping
# 7:再将dynamic修改为strict
PUT dynamic_mapping_test/_mapping
{
"dynamic": "strict"
}
# 8:就报错了,不让添加新字段
# mapping set to strict, dynamic introduction of [anotherField1] within [_doc] is not allowed
PUT dynamic_mapping_test/_doc/10
{
"anotherField1": "someValue"
}
2:显式定义mapping信息
格式:
咋写?
建议第二种写法,稍微改吧改吧就行了,不易出错,效率高。简答来说就是因为es自动mapping也能生成个八九不离十,我们只需要在再对其中不正确的部分,做小改动就可以了。
3:字段的常用配置
3.1:index
通过该选项的true和false,来控制字段是否可以被搜索,如果某个字段不需要或者是不希望被搜索,则可以再mapping中将该字段设置为false。如:
查询没有被索引的字段将会报错(但search uri的方式不报错,只是查不到)
,测试一下:
# 1:创建索引users,并指定mobile不索引
PUT users
{
"mappings": {
"properties": {
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
},
"mobile": {
"type": "text",
"index": false
}
}
}
}
# 2:查看创建的索引users,确定mobile的index就是false,如果不是就回过头检查一下
GET users/_mapping
# 3:插入一条数据
PUT users/_create/1
{
"firstName": "jack",
"lastName": "james",
"mobile": "13652525252"
}
# 4:查询mobile,会报错Cannot search on field [mobile] since it is not indexed
POST users/_search
{
"query": {
"match": {
"mobile": "13652525252"
}
}
}
# 5:但search uri的方式是可以查询的,不报错,只是查询不到
GET users/_search?q=mobile:13652525252
3.2:index_option
用来设置索引记录的内容,默认是docs,只记录doc_id,具体如下:
3.3:null_value
如果某些场景下希望搜索出值为null的文档,则可以使用null_value来给null一个特殊的值,搜索时,搜索这个特质就可以了,如下:
# 1:先删除索引,因为前面创建过了
DELETE users
# 2:创建索引,并指定firstName的null_value
PUT users
{
"mappings": {
"properties": {
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
},
"mobile": {
"type": "keyword",
"null_value": "imnull"
}
}
}
}
# 3:创建新数据
POST users/_create/1
{
"firstName": "jack",
"lastName": "james",
"mobile": null
}
# 4:通过mobile的null值搜索,可以搜索到,并且mobile的值是 "mobile" : null
GET users/_search?q=mobile:"imnull"
3.4:copy_to
如果是希望将多个字段汇总到一个字段中,并搜索,可以使用copy_to,如下:
测试:
# 1:先删除索引,因为前面创建过了
DELETE users
# 2:创建索引,并指定firstName的null_value
PUT users
{
"mappings": {
"properties": {
"firstName": {
"type": "text",
"copy_to": "fullName"
},
"lastName": {
"type": "text",
"copy_to": "fullName"
}
}
}
}
# 3:创建新数据
POST users/_create/1
{
"firstName": "jack",
"lastName": "james"
}
# 4:搜索在fulleName中包含jack或者是包含james的,可以正常搜索到
GET users/_search?q=fullName:(jack james)
{
"profile": "true"
}
3.5:多字段类型
考虑这样的场景,对于文档的某个字段,有时候我们需要对其进行模糊查询,有时候需要对其进行精确查询,此时,就可以考虑使用多字段类型,即给字段设置一个子字段,当然,在当前假设的场景下,子字段的类型需要设置为keyword
,来满足精确查找的需求,如下:
# 1:先删除索引,因为前面创建过了
DELETE users
# 2:创建索引,并指定firstName的null_value
PUT users
{
"mappings": {
"properties": {
"firstName": {
"type": "text",
"fields": {
"my_keyword": {
"type": "keyword"
}
}
},
"lastName": {
"type": "text"
}
}
}
}
# 3:插入数据
POST users/_create/1
{
"firstName": "AA BB CC",
"lastName": "james"
}
# 4:使用firstName查询“AA BB”可以查询到,此时是模糊匹配
GET users/_search?q=firstName:"AA BB"
{
"profile": "true"
}
# 5:使用firstName的子字段my_keyword查询“AA BB”查询不到,因为是精准匹配
GET users/_search?q=firstName.my_keyword:"AA BB"
{
"profile": "true"
}
# 6:使用firstName的子字段my_keyword查询“AA BB CC"可以查询到
GET users/_search?q=firstName.my_keyword:"AA BB CC"
{
"profile": "true"
}
注意keyword和text的关键区别就是,keyword是精确匹配,text是模糊匹配。当然text当给定的是完全匹配的值时也是可以查询到的,但本质上还是通过模糊查询的方式匹配到的,这点要注意。
当然我们也可以设置一个指定了自定义分词器的子字段,如下:
具体的可以根据业务场景来灵活定义。
3.6:Exact Values 和 Full text
Exact values就是精确值,需要进行精确匹配,比如一个身份证号,下载地址等,Full text就是模糊匹配,比如一段描述性的文本,对于Exact Value需要设置type:"keyword"
,对于Full Text需要设置type:"text"
,如下图:
4:自定义分词器
我们在前面 看过分词器由三部分组成:
charanter Filters:无
Tokennizer:按词切分,就是按照空格切分吧
Token Filters:小写处理
本部分我们继续来看下自定义分词器的相关内容。
4.1:Character Filters有哪些
4.2:Tokennizer有哪些
4.3:Token Filters有哪些
4.4:自定义一些分词器
4.4.1:Character Filter使用html_strip,tokennizer使用keyword
# 1:<b>hello world</b> character filter处理后变为hello world
# 2:hello world tokenizer处理后变为hello world
# 3:没有token filter,所以结果就是hello world
POST _analyze
{
"tokenizer": "keyword",
"char_filter": ["html_strip"],
"text": "<b>hello world</b>"
}
4.4.2:Character Filter使用mapping,tokennizer使用standard(按词切分)
# 1:"123-456, I-test! test-990 650-550-1234" 经过character filter变为"123_456, I_test! test_990 650_550_1234"
# 2:"123_456, I_test! test_990 650_550_1234" 经过tokenizer,按照单词切分变为 ["123_456", "I_test", "test_990", "650_550_1234" ]
# 3:因为没有token filters,所以结果就是 ["123_456", "I_test", "test_990", "650_550_1234" ]
POST _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type": "mapping",
"mappings": [ "- => _" ]
}
],
"text": "123-456, I-test! test-990 650-550-1234"
}
4.4.3:Character Filter使用正则,tokennizer使用standard(按词切分)
4.4.4:tokennizer使用path_hiarrarchy,切分路径
4.4.5:tokennizer使用whitespace(按照空格切分)
,token filter使用stop删除the,am,a等
# 结果中The还在,因为stop过滤的是小写的the,因此如果想要过滤掉大The的话,只需要修改"filter": ["stop"] 为 "filter": ["lowercase", "stop"],即The先变为the
POST _analyze
{
"tokenizer": "whitespace",
"filter": ["stop"],
"text": "The rain in Spain falls mianly on the plain"
}