Elasticsearch中的mapping问题
Mapping在Elasticsearch中是非常重要的一个概念。决定了一个index中的field使用什么数据格式存储,使用什么分词器解析,是否有子字段等。
为什么要学习Mapping?
如果没有mapping所有text类型属性默认都使用standard分词器。所以如果希望使用IK分词就必须配置自定义mapping。
1 mapping核心数据类型
Elasticsearch中的数据类型有很多,在这里只介绍常用的数据类型。 只有text类型才能被分词。其他类型不允许。 文本(字符串):text 整数:byte、short、integer、long 浮点型:float、double 布尔类型:boolean 日期类型:date 数组类型:array {a:[]} 对象类型:object {a:{}} 不分词的字符串(关键字): keyword
2 dynamic mapping对字段的类型分配
true or false -> boolean 123 -> long 123.123 -> double 2018-01-01 -> date hello world -> text [] -> array {} -> object 在上述的自动mapping字段类型分配的时候,只有text类型的字段需要分词器。默认分词器是standard分词器。
3 查看索引mapping
可以通过命令查看已有index的mapping具体信息,语法如下: GET 索引名/_mapping
如:
GET test_index/_mapping
结果:
代码语言:javascript复制{
"test_index": { # 索引名
"mappings": { # 映射列表
"test_type": { # 类型名
"properties": { # 字段列表
"age": { # 字段名
"type": "long" # 字段类型
},
"gender": {
"type": "text",
"fields": { # 子字段列表
"keyword": { # 子字段名
"type": "keyword", # 子字段类型,keyword不进行分词处理的文本类型
"ignore_above": 256 # 子字段存储数据长度
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
4 custom mapping
可以通过命令,在创建index和type的时候来定制mapping映射,也就是指定字段的类型和字段数据使用的分词器。 手工定制mapping时,只能新增mapping设置,不能对已有的mapping进行修改。 如:有索引a,其中有类型b,增加字段f1的mapping定义。后续可以增加字段f2的mapping定义,但是不能修改f1字段的mapping定义。 通常都是手工创建index,并进行各种定义。如:settings,mapping等。
4.1创建索引时指定mapping
代码语言:javascript复制语法:
PUT 索引名称
{
"mappings":{
"类型名称":{
"properties":{
"字段名":{
"type":类型,
["analyzer":字段的分词器,]
["fields":{
"子字段名称":{
"type":类型,
"ignore_above":长度限制
}
}]
}
}
}
}
}
举一个例子:
代码语言:javascript复制PUT /test_index
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
},
"mappings": {
"test_type":{
"properties": {
"author_id" : {
"type": "byte",
"index": false
},
"title" : {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword" : {
"type": "keyword",
"ignore_above": 256
}
}
},
"content" : {
"type": "text",
"analyzer": "ik_max_word"
},
"post_date" : {
"type": "date"
}
}
}
}
}
代码语言:javascript复制"index" - 是否可以作为搜索索引。可选值:true | false
"analyzer" - 指定分词器。
"type" - 指定字段类型
Search 搜索详解
代码语言:javascript复制PUT test_search
{
"mappings": {
"test_type" : {
"properties": {
"dname" : {
"type" : "text",
"analyzer": "standard"
},
"ename" : {
"type" : "text",
"analyzer": "standard"
},
"eage" : {
"type": "long"
},
"hiredate" : {
"type": "date"
},
"gender" : {
"type" : "keyword"
}
}
}
}
}
代码语言:javascript复制POST test_search/test_type/_bulk
{ "index": {}}
{ "dname" : "Sales Department", "ename" : "张三", "eage":20, "hiredate" : "2019-01-01", "gender" : "男性" }
{ "index": {}}
{ "dname" : "Sales Department", "ename" : "李四", "eage":21, "hiredate" : "2019-02-01", "gender" : "男性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : "王五", "eage":23, "hiredate" : "2019-01-03", "gender" : "男性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : "赵六", "eage":26, "hiredate" : "2018-01-01", "gender" : "男性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : "韩梅梅", "eage":24, "hiredate" : "2019-03-01", "gender" : "女性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : "钱虹", "eage":29, "hiredate" : "2018-03-01", "gender" : "女性" }
2query string search
search的参数都是类似http请求头中的字符串参数提供搜索条件的。 GET [/index_name/type_name/]_search[?parameter_name=parameter_value&…]
2.1全搜索
timeout参数:是超时时长定义。代表每个节点上的每个shard执行搜索时最多耗时多久。不会影响响应的正常返回。只会影响返回响应中的数据数量。 如:索引a中,有10亿数据。存储在5个shard中,假设每个shard中2亿数据,执行全数据搜索的时候,需要耗时1000毫秒。定义timeout为10毫秒,代表的是shard执行10毫秒,搜索出多少数据,直接返回。 在商业项目中,是禁止全数据搜索的。必须指定搜索的索引,类型和关键字。如果没有指定索引或类型,则代表开发目的不明确,需要重新做用例分析。如果没有关键字,称为索引内全搜索,也叫魔鬼搜索。
代码语言:javascript复制语法:
GET [索引名/类型名/]_search?timeout=10ms
代码语言:javascript复制get test_search/_search
代码语言:javascript复制{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 6,
"max_score" : 1.0,
"hits" : [
{
"_index" : "test_search",
"_type" : "test_type",
"_id" : "tke_pXcBCmx352yzWc4R",
"_score" : 1.0,
"_source" : {
"dname" : "Sales Department",
"ename" : "张三",
"eage" : 20,
"hiredate" : "2019-01-01",
"gender" : "男性"
}
},
{
"_index" : "test_search",
"_type" : "test_type",
"_id" : "uke_pXcBCmx352yzWc4R",
"_score" : 1.0,
"_source" : {
"dname" : "Development Department",
"ename" : "韩梅梅",
"eage" : 24,
"hiredate" : "2019-03-01",
"gender" : "女性"
}
},
{
"_index" : "test_search",
"_type" : "test_type",
"_id" : "t0e_pXcBCmx352yzWc4R",
"_score" : 1.0,
"_source" : {
"dname" : "Sales Department",
"ename" : "李四",
"eage" : 21,
"hiredate" : "2019-02-01",
"gender" : "男性"
}
},
{
"_index" : "test_search",
"_type" : "test_type",
"_id" : "uEe_pXcBCmx352yzWc4R",
"_score" : 1.0,
"_source" : {
"dname" : "Development Department",
"ename" : "王五",
"eage" : 23,
"hiredate" : "2019-01-03",
"gender" : "男性"
}
},
{
"_index" : "test_search",
"_type" : "test_type",
"_id" : "uUe_pXcBCmx352yzWc4R",
"_score" : 1.0,
"_source" : {
"dname" : "Development Department",
"ename" : "赵六",
"eage" : 26,
"hiredate" : "2018-01-01",
"gender" : "男性"
}
},
{
"_index" : "test_search",
"_type" : "test_type",
"_id" : "u0e_pXcBCmx352yzWc4R",
"_score" : 1.0,
"_source" : {
"dname" : "Development Department",
"ename" : "钱虹",
"eage" : 29,
"hiredate" : "2018-03-01",
"gender" : "女性"
}
}
]
}
}
代码语言:javascript复制结果:
{
"took": 144, #请求耗时多少毫秒
"timed_out": false, #是否超时。默认情况下没有超时机制,也就是客户端等待Elasticsearch搜索结束(无论执行多久),提供超时机制的话,Elasticsearch则在指定时长内处理搜索,在指定时长结束的时候,将搜索的结果直接返回(无论是否搜索结束)。指定超时的方式是传递参数,参数单位是:毫秒-ms。秒-s。分钟-m。
"_shards": {
"total": 1, #请求发送到多少个shard上
"successful": 1,#成功返回搜索结果的shard
"skipped": 0, #停止服务的shard
"failed": 0 #失败的shard
},
"hits": {
"total": 1, #返回了多少结果
"max_score": 1, #搜索结果中,最大的相关度分数,相关度越大分数越高,_score越大,排位越靠前。
"hits": [ #搜索到的结果集合,默认查询前10条数据。
{
"_index": "test_index", #数据所在索引
"_type": "test_type", #数据所在类型
"_id": "1", #数据的id
"_score": 1, #数据的搜索相关度分数
"_source": { # 数据的具体内容。
"field": "value"
}
}
]
}
}
2.2multi index搜索
**所谓的multi-index就是从多个index中搜索数据。**相对使用较少,只有在复合数据搜索的时候,可能出现。一般来说,如果真使用复合数据搜索,都会使用_all。
如:搜索引擎中的无条件搜索。(现在的应用中都被屏蔽了。使用的是默认搜索条件,执行数据搜索。 如: 电商中的搜索框默认值, 搜索引擎中的类别)
无条件搜索,在搜索应用中称为“魔鬼搜索”,代表的是,搜索引擎会执行全数据检索,效率极低,且对资源有非常高的压力。
代码语言:javascript复制语法:
GET _search
GET 索引名1,索引名2/_search # 搜索多个index中的数据
GET 索引名/类型名/_search # 所属一个index中type的数据
GET prefix_*/_search # 通配符搜索
GET *_suffix/_search
GET 索引名1,索引名2/类型名/_search # 搜索多个index中type的数据
GET _all/_search # _all代表所有的索引
GET 索引名/_search?q=字段名:搜索条件
示例:get test_search/test_type/_search?q=eage:26 对搜索条件为中文支持不友好。
2.3分页搜索
默认情况下,Elasticsearch搜索返回结果是10条数据。从第0条开始查询。 size和from是es中具有特定含义的属性名。 语法:
代码语言:javascript复制GET 索引名/_search?size=10 # size查询数据的行数
GET 索引名/_search?from=0&size=10 # from 从第几行开始查询,行号从0开始。
2.4 /-搜索
代码语言:javascript复制语法:
GET 索引名/_search?q=字段名:条件
GET 索引名/_search?q= 字段名:条件
GET 索引名/_search?q=-字段名:条件
- :和不定义符号含义一样,就是搜索指定的字段中包含key words的数据
- : 与 符号含义相反,就是搜索指定的字段中不包含key words的数据 示例: 搜索dname中包含Sales单词的内容。dname使用standard分词器,会把内容进行拆分为单词。搜索Sales可以匹配到单词,但是搜索Sal是无法匹配到单词。 get test_search/test_type/_search?q= dname:Sales
2.5排序
代码语言:javascript复制语法:GET 索引名/_search?sort=字段名:排序规则
排序规则: asc(升序) | desc(降序)
GET test_search/_search?sort=eage:asc
GET test_search/_search?sort=eage:desc
3 query DSL
DSL - Domain Specified Language , 特殊领域的语言。 请求参数是请求体传递的。在Elasticsearch中,请求体的字符集默认为UTF-8。
代码语言:javascript复制语法格式:
GET 索引名/_search
{
"command":{ "parameter_name" : "parameter_value"}
}
3.1查询所有数据
代码语言:javascript复制GET 索引名/_search
{
"query" : { "match_all" : {} }
}
3.2match search(项目搜索功能使用此命令)
全文检索。要求查询条件拆分后的任意词条与具体数据匹配就算搜索结果。
代码语言:javascript复制GET 索引名/_search
{
"query": {
"match": {
"字段名": "搜索条件"
}
}
}
3.3phrase search
短语检索。要求查询条件必须和具体数据完全匹配才算搜索结果。其特征是: 1.对搜索条件进行拆词 2.把拆词当作一个整体,整体去索引(索引是存储内容被拆词后的结果)中匹配,必须严格匹配(存储内容拆词后是:北京,大兴,朝阳,条件拆词是:北京,朝阳。这种情况是不能被查询的,因为北京和朝阳之前还有大兴。)才能查询到
代码语言:javascript复制GET 索引名/_search
{
"query": {
"match_phrase": {
"字段名": "搜索条件"
}
}
}
3.4range
代码语言:javascript复制范围比较搜索
GET 索引名/类型名/_search
{
"query" : {
"range" : {
"字段名" : {
"gt" : 搜索条件1,
"lte" : 搜索条件2
}
}
}
}
3.5多条件复合搜索
在一个请求体中,有多个搜索条件,就是复合搜索。如:搜索数据,条件为部门名称是Sales Department,员工年龄在20到26之间,部门员工姓名叫张三。上述条件中,部门名称为可选条件,员工年龄必须满足要求,部门员工姓名为可选要求。这种多条件搜索就是复合搜索。
代码语言:javascript复制GET 索引名/类型名/_search
{
"query": {
"bool": {
"must": [ #数组中的多个条件必须同时满足
{
"range": {
"字段名": {
"lt": 条件
}
}
}
],
"must_not":[ #数组中的多个条件必须都不满足
{
"match": {
"字段名": "条件"
}
},
{
"range": {
"字段名": {
"gte": "搜索条件"
}
}
}
]
"should": [# 数组中的多个条件有任意一个满足即可。
{
"match": {
"字段名": "条件"
}
},
{
"range": {
"字段名": {
"gte": "搜索条件"
}
}
}
]
}
}
}
3.6排序
在Elasticsearch的搜索中,默认是使用相关度分数实现排序的。可以通过搜索语法实现定制化排序。
代码语言:javascript复制GET 索引名/类型名/_search
{
"query": {
[搜索条件]
},
"sort": [
{
"字段名1": {
"order": "asc"
}
},
{
"字段名2": {
"order": "desc"
}
}
]
}
注意:在Elasticsearch中,如果使用text类型的字段作为排序依据,会有问题。Elasticsearch需要对text类型字段数据做分词处理。如果使用text类型字段做排序,Elasticsearch给出的排序结果未必友好,毕竟分词后,先使用哪一个单词做排序都是不合理的。所以Elasticsearch中默认情况下不允许使用text类型的字段做排序,如果需要使用字符串做结果排序,则可使用keyword类型字段作为排序依据,因为keyword字段不做分词处理。
3.7分页
DSL分页也是使用from和size实现的。
代码语言:javascript复制GET 索引名称/_search
{
"query":{
"match_all":{}
},
"from": 起始下标,
"size": 查询记录数
}
3.8highlight display
在搜索中,经常需要对搜索关键字做高亮显示,这个时候就可以使用highlight语法。
代码语言:javascript复制语法:
GET 索引名/_search
{
"query": {
"match": {
"字段名": "条件"
}
},
"highlight": {
"fields": {
"要高亮显示的字段名": {
"fragment_size": 5, #每个分段长度,默认20
"number_of_fragments": 1 #返回多少个分段,默认3
}
},
"pre_tags": ["前缀"],
"post_tags": ["后缀"]
}
}
// 演示案例
GET test_search/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"dname": "Development department"
}
},
{
"match": {
"gender": "男性"
}
}
]
}
},
"highlight": {
"fields": {
"dname": {
"fragment_size": 20,
"number_of_fragments": 1
},
"gender": {
"fragment_size": 20,
"number_of_fragments": 1
}
},
"pre_tags":["<span style='color:red'>"],
"post_tags":["</span>"]
},
"from": 2,
"size": 2
}
代码语言:javascript复制fragment_size:代表字段数据如果过长,则分段,每个片段数据长度为多少。长度不是字符数量,是Elasticsearch内部的数据长度计算方式。默认不对字段做分段。
number_of_fragments:代表搜索返回的高亮片段数量,默认情况下会将拆分后的所有片段都返回。
pre_tags:高亮前缀
post_tags:高亮后缀
很多搜索结果显示页面中都不会显示完整的数据,这样在数据过长的时候会导致页面效果不佳,都会按照某一个固定长度来显示搜索结果,所以fragment_size和number_of_fragments参数还是很常用的。