DSL操作ES-操作索引库
# 创建索引库
PUT test
# 查看索引库
GET test
# 删除索引库
DELETE test
# 关闭索引库
POST test/_close
# 打开索引库
POST test/_open
GET test/_search # 查询全部
GET test/_mapper #查询映射数据
DSL操作ES-区分类型-操作映射
1)简单数据类型:
text:可以分词,不支持聚合(统计)
keyword:不会分词,将全部内容作为一个词条,支持聚合(统计) name keyword
布尔(boolean)
二进制(binary)
范围类型(integer_range, float_range, long_range, double_range, date_range)
日期(date)
2)复杂数据类型
-
数组 []:没有专用的array数据类型,任何一个字段的值,都可以被添加0个到多个,但要求他们的类型必须一致,当类型一直含有多个值存储到ES中会自动转化成数组类型 ["eric","jack"] List Set
-
对象 {} Map User {key:value,key:value}
3)GEO: geo_point 地理位置坐标值
简单试水:
# 创建一个person的索引库,并且有映射
PUT person
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
}
}
}
}
# 查看映射
GET person/_mapping
# 添加字段
PUT person/_mapping
{
"properties":{
"sex":{
"type":"keyword"
}
}
}
添加文档,指定id
POST person/_doc/1
{
"name":"张三",
"age":18,
"sex":"男"
}
添加文档,不指定id
POST person/_doc
{
"name":"翠花",
"age":20,
"sex":"女"
}
删除文档
DELETE person/_doc/1
# ==========文档的操作=============
# 指定id添加文档
POST person/_doc/1
{
"name":"如花",
"age": 18,
"sex":"男"
}
# 不指定id添加文档
POST person/_doc/2
{
"name":"翠花",
"age": 19,
"sex":"女"
}
# 查看所有文档
GET person/_search
# 根据id查看文档
GET person/_doc/rdJ9qYIB33sH7ezNb5Mu
# 修改文档
PUT person/_doc/rdJ9qYIB33sH7ezNb5Mu
{
"name":"玫瑰花",
"age":"20"
}
# 根据id删除文档
DELETE person/_doc/rdJ9qYIB33sH7ezNb5Mu
#注: 添加文档不指定id会自动生成一个id值
分词器
-
Standard Analyzer - 默认分词器,按词/字切分,小写处理 (英文)
-
Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
-
Stop Analyzer - 小写处理,停用词过滤(the,a,is)
-
Whitespace Analyzer - 按照空格切分,不转小写
-
Keyword Analyzer - 不分词,直接将输入当作输出
-
Patter Analyzer - 正则表达式,默认\W+(非字符分割) (中文会被去掉)
-
Language - 提供了30多种常见语言的分词器
第三方ik 分词器
ik_smart:最小分词法
ik_max_word:最细分词法
Elasticsearch高级搜索:查询接口说明
查询所有:查询出所有数据,一般测试用。例如:match_all
全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
-
multi_match_query
-
match_query
精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
- ids
- range
- term
地理(geo)查询:根据经纬度查询。例如:
- geo_distance
- geo_bounding_box
复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
- bool
- function_score
常见的全文检索查询包括:
match查询:单字段查询
multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件
match查询语法如下:
GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
mulit_match语法如下:
GET /indexName/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"]
}
}
}
精准查询
-
term:根据词条精确值查询(针对非数值类型)
-
range:根据值的范围查询(针对数值类型)
# 精确检索
# 需求:查询上海的酒店
GET hotel/_search
{
"query": {
"term": {
"city": {
"value": "上海"
}
}
}
}
# 需求:查询五星级的酒店
GET hotel/_search
{
"query": {
"term": {
"starName": {
"value": "五星级"
}
}
}
}
# 需求:查询价格500-100的酒店
GET hotel/_search
{
"query": {
"range": {
"price": {
"gte": 500,
"lte": 1000
}
}
}
}
地理坐标查询
矩形范围查询,也就是geo_bounding_box查询,查询坐标落在某个矩形范围的所有文档:
附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。
# 矩形位置检索
GET hotel/_search
{
"query": {
"geo_bounding_box":{
"location":{
"top_left":{
"lat": 31.1,
"lon": 121.5
},
"bottom_right":{
"lat": 30.9,
"lon": 121.7
}
}
}
}
}
# 距离位置检索(圆形位置检索)
GET hotel/_search
{
"query": {
"geo_distance":{
"location":"31.1,121.5",
"distance": "10km"
}
}
}
复合查询-算分函数查询
使用场景
当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。
elasticsearch会根据词条和文档的相关度做打分,算法由两种:
-
TF-IDF算法
-
BM25算法,elasticsearch5.1版本后采用的算法
function score 查询中包含四部分内容:
-
原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)
-
过滤条件:filter部分,符合该条件的文档才会重新算分
-
算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数
-
weight:函数结果是常量
-
field_value_factor:以文档中的某个字段值作为函数结果
-
random_score:以随机数作为函数结果
-
script_score:自定义算分函数算法
-
-
运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
-
multiply:相乘
-
replace:用function score替换query score
-
其它,例如:sum、avg、max、min
-
function score的运行流程如下:
-
1)根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
-
2)根据过滤条件,过滤文档
-
3)符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
-
4)将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。
因此,其中的关键点是:
-
过滤条件:决定哪些文档的算分被修改
-
算分函数:决定函数算分的算法
-
运算模式:决定最终算分结果
# 复合查询-分值函数查询
# 优先显示"北京"地区的 "如家"酒店
GET hotel/_search
{
"query": {
"function_score": {
"query": {
"match": {
"all": "如家"
}
},
"functions": [
{
"filter": {
"term": {
"city": "北京"
}
},
"weight": 20
}
],
"boost_mode": "multiply"
}
}
}
复合查询-布尔查询
使用场景
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:
-
must:必须匹配每个子查询,类似“与”
-
should:选择性匹配子查询,类似“或”
-
must_not:必须不匹配,不参与算分,类似“非”
-
filter:必须匹配,不参与算分
# 复合查询- 布尔查询
# 搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店
GET hotel/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"all": "如家"
}
}
],
"must_not": [
{
"range": {
"price": {
"gt": 400
}
}
}
],
"filter": {
"geo_distance": {
"distance": "10km",
"location": "31.21,121.5"
}
}
}
}
}
lasticsearch高级搜索:搜索结果处理
普通字段排序
keyword、数值、日期类型排序的语法基本一致。
DSL语法:
排序条件是一个数组,也就是可以写多个排序条件。按照声明的顺序,当第一个条件相等时,再按照第二个条件排序,以此类推
# 普通字段排序
# 需求:对“如家”酒店价格进行倒序排序
GET hotel/_search
{
"query": {
"match": {
"all": "如家"
}
},
"sort": [
{
"price": {
"order": "asc"
}
}
]
}
# 地理位置排序
# 需求:我的坐标是31.034661,121.612282,寻找我周围距离最近的酒店(由近到远)
GET hotel/_search
{
"sort": [
{
"_geo_distance": {
"location": "31.034661,121.612282",
"order": "asc",
"unit": "km"
}
}
]
}
分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:
-
from:从第几个文档开始,从0开始的
-
size:总共查询几个文档
# 结果分页
# 需求:查询第1页,每页显示20条
# from值算法=(页码-1)* size
GET hotel/_search
{
"query": {
"match": {
"all": "虹桥如家"
}
},
"from": 20,
"size": 20
}
高亮显示
高亮显示的实现分为两步:
-
1)给文档中的所有关键字都添加一个标签,例如
<em>
标签 -
2)页面给
<em>
标签编写CSS样式
# 高亮显示
# 对name字段内容进行高亮
# 高亮属性配置
# require_field_match: 该高亮字段是否必须参与搜索条件,true:必须参数(默认值),false: 可以不参与
# pre_tags和post_tags:修改高亮样式,默认<em></em>
GET hotel/_search
{
"query": {
"match": {
"all": "虹桥如家"
}
},
"highlight": {
"fields": {// 指定要高亮的字段
"name": {
"require_field_match": "false"
}
},
"pre_tags": "<font color='red'>",// 用来标记高亮字段的前置标签
"post_tags": "</font>"// 用来标记高亮字段的后置标签
}
}
Elasticsearch聚合搜索:聚合分类
聚合常见的有三类:
-
桶(Bucket)聚合:用来对文档做分组 类似mysql的group by
-
TermAggregation:按照文档字段值分组,例如按照品牌值分组、按照国家分组
-
Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
-
-
度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等
-
类似于mysql统计函数 count,sum,max,min,avg
-
Avg:求平均值
-
Max:求最大值
-
Min:求最小值
-
Stats:同时求max、min、avg、sum等
-
-
管道(pipeline)聚合:其它聚合的结果为基础做聚合
Bucket聚合
根据这个需求,其实就是按照品牌对数据分组。此时可以根据酒店品牌的名称做聚合,也就是Bucket聚合。
GET /hotel/_search
{
"size": 0, // 设置size为0,结果中不包含文档,只包含聚合结果
"aggs": { // 定义聚合
"brandAgg": { //给聚合起个名字
"terms": { // 聚合的类型,按照品牌值聚合,所以选择term
"field": "brand", // 参与聚合的字段
"size": 20 // 希望获取的聚合结果数量
}
}
}
}
聚合结果排序
默认情况下,Bucket聚合会统计Bucket内的文档数量,记为count,并且按照count降序排序。
我们可以指定order属性,自定义聚合的排序方式:
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"order": {
"_count": "asc" // 按照_count升序排列
},
"size": 20
}
}
}
}
限定聚合范围
默认情况下,Bucket聚合是对索引库的所有文档做聚合,但真实场景下,用户会输入搜索条件,因此聚合必须是对搜索结果聚合。那么聚合必须添加限定条件。
我们可以限定要聚合的文档范围,只要添加query条件即可:
# 注意:通常在做聚合查询时,可以设置size=0,为了取消查询列表数据
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 // 只对200元以下的文档聚合
}
}
},
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}
Metric聚合
这就要用到Metric聚合了,例如stat聚合:就可以获取min、max、avg等结果。
语法如下:
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
},
"aggs": { // 是brands聚合的子聚合,也就是分组后对每组分别计算
"score_stats": { // 聚合名称
"stats": { // 聚合类型,这里stats可以计算min、max、avg等
"field": "score" // 聚合字段,这里是score
}
}
}
}
}
}
过滤条件聚合显示
搜索页面的品牌、城市等信息不应该是在页面写死,而是通过聚合索引库中的酒店数据得来的:
# 统计酒店的品牌,星级,城市
GET hotel/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"all": "酒店"
}
}
],
"filter": [
{
"term": {
"city": "上海"
}
} ,
{
"term": {
"brand": "如家"
}
} ,
{
"term": {
"starName": "二钻"
}
},
{
"range": {
"price": {
"gte": 100,
"lte": 200
}
}
}
]
}
},
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 100
}
},
"starAgg": {
"terms": {
"field": "starName",
"size": 100
}
},
"cityAgg": {
"terms": {
"field": "city",
"size": 100
}
}
}
}
自定义分词器
默认的拼音分词器会将每个汉字单独分为拼音,而我们希望的是每个词条形成一组拼音,需要对拼音分词器做个性化定制,形成自定义分词器。
elasticsearch中分词器(analyzer)的组成包含三部分:
-
character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符
-
tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
-
tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等
声明自定义分词器的语法如下:
PUT /test
{
"settings": {
"analysis": {
"analyzer": { // 自定义分词器
"my_analyzer": { // 分词器名称
"tokenizer": "ik_max_word",
"filter": "py"
}
},
"filter": { // 自定义tokenizer filter
"py": { // 过滤器名称
"type": "pinyin", // 过滤器类型,这里是pinyin
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "my_analyzer",
"search_analyzer": "ik_max_word"
}
}
}
}
结合以上的DSL,拼音分词,自定义分词, 自动补全,建立索引库
PUT /hotel
{
"settings": {
"analysis": {
"analyzer": {
"text_anlyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"id":{
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "text_anlyzer",
"search_analyzer": "ik_max_word",
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword",
"copy_to": "all"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "text_anlyzer",
"search_analyzer": "ik_max_word"
},
"suggestion":{
"type": "completion",
"analyzer": "text_anlyzer",
"search_analyzer": "ik_max_word"
}
}
}
}