Elasticsearch学习(五)Elasticsearch中的mapping问题,Search 搜索详解

2021-03-02 14:46:25 浏览数 (1)

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参数还是很常用的。

0 人点赞