Skip to content

第三章 DSL基本操作 v1.0 ​

一、索引库的创建 ​

1.mapper属性 ​

  1. type:字段数据类型,常见简单类型有以下几种

  2. 字符串:text(可分词的文本)、keyword(精确关键词,如:国家、品牌等)

  3. 数值:long、integer、short、byte、double、float

  4. 布尔:boolean

  5. 日期:date

  6. 对象:object

  7. 地理坐标:geo_point(经纬度确定的点)、geo_shape(多个经纬度确定的区域)

  8. index:是否创建倒排索引,默认true(若不创建倒排索引则不可以参与搜索)

  9. analyzer:使用哪种分词器,值为分词器的名称

  10. properties:字段的子字段

  11. copy_to:将字段拷贝到指定字段(用于提升搜索性能)

json
{
  "all": {
    "type": "text" ,
    "analyzer": "ik_max_word"
  },
  "brand": {
    "type": "keyword",
    "copy_to": "all"  #复制到all字段,让all字段可以根据brand结果被查询
  }
}

注:id应设置为keyword类型,且因要参与查询,index应当默认为true

2.创建索引库 ​

json
PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名1": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2": {
        "type": "keyword",
        "index": "false"
      },
      "字段名3": {
        "porperties":{
          "子字段名": {
            #...
          }
        }
      },
      #...
    }
  }
}

3.查询索引库 ​

json
GET /索引库名

4.删除索引库 ​

json
DELETE /索引库名

5.修改索引库 ​

索引库和mapping一旦创建无法修改,但是可以添加新的字段

json
PUT /索引库名/_mapping
{
  "perperties": {
    "新字段名" :{
      "type": "integer"
    }
  }
}

6.其他操作 ​

json
#查询所有索引库
GET /_cat/indices

1.新增文档 ​

json
POST /索引库名/_doc/文档id
{
  "字段1": "value1",
  "字段2": "value2",
  "字段3": {
    "子属性1": "value3",
    "子属性2": "value4"
  }
}

2.查询文档 ​

json
GET /索引库名/_doc/文档id

3.删除文档 ​

json
DELETE /索引库名/_doc/文档id

4.修改文档 ​

a.全量修改 ​

若文档存在,则覆写原有文档生成新的文档;若文档不存在,则新增文档

json
PUT /索引库名/_doc/文档id
{
  "字段1": "value1",
  "字段2": "value2",
  "字段3": {
    "子属性1": "value3",
    "子属性2": "value4"
  }
}
b.增量修改 ​

只修改指定的字段值

json
POST /索引库名/_update/文档id
{
  "doc": {  #此为固定写法
    "字段名": "新值"
  }
}

一、基本查询 ​

1.查询分类 ​

  • 查询所有:查询出所有数据,一般测试使用,如:match_all
  • 全文检索查询:利用分词器对用户输入内容分词,在倒排索引库中匹配

如:match_query、multi_match_query

  • 精确查询:根据精切词条查找数据,一般查找keyword、数值、日期、boolean等类型字段

如:ids、range、term

  • 地理查询:根据经纬度查询

如:geo_distance、geo_bounding_box

  • 复合查询:将上述各种查询条件组合起来,合并查询条件进行查询

如:bool、function_score

2.基本查询语法 ​

json
GET /indexName/_search
{
  "query": {
    "查询类型": {
      "查询条件": "查询值"
    }
  }
}

二、DSL查询简单语法 ​

1.查询所有 ​

json
GET /indexName/_search
{
  "query": {
    "match_all": {}
  }
}

2.全文检索查询 ​

json
GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"  #查询字段与检索词
    }
  }
}
json
GET /indexName/_search
{
  "query": {
    "multi_match": {
      "query": "TEXT",
      "fields": ["FIELD1","FIELD2", ...]  #字段
    }
  }
}

3.精确查询 ​

json
GET /indexName/_search
{
  "query": {
    "term": {
      "FIELD": {  #字段
        "value": "VALUE"  #查找值
      }
    }
  }
}
json
#需要字段中同时存在所有检索值才能检索到结果
GET /indexName/_search
{
  "query": {
    "terms": {
      "FIELD": ["FIELD1","FIELD2", ...]  #查询字段与检索词
    }
  }
}
json
GET /indexName/_search
{
  "query": {
    "range": {
      "FIELD": {  #字段
        "gte": 10,  #大于等于10,大于为gt
        "lte": 20  #小于等于20,小于为lt
      }
    }
  }
}

4.地理查询 ​

json
#查询geo_point值落在某个矩形范围内的所有文档
GET /indexName/_search
{
  "query": {
    "geo_bounding_box": {
      "FIELD": {  #字段
        "top_left": {  #左上坐标
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": {  #右下坐标
          "lat": 30.9,
          "lon": 121.7
        }
      }
    }
  }
}
json
#查询geo_point值到指定中心点小于某个距离值的所有文档
GET /indexName/_search
{
  "query": {
    "geo_distance": {
      "distance": "10km",  #指定距中心店距离
      "FIELD": "10.00,120.00"  #指定经纬度坐标中心点
    }
  }
}

三、复合查询 ​

1.ES分数关联度打分 ​

当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score) , 返回结果时按照分值降序排列。

TF-IDF算法(ES5.0前使用) ​

(TF(词条频率)=\frac{词条出现次数})

(IDF(逆文档频率)=\log{\frac{文档总数}{包含词条的文档总数}})

(score=\sum^n_iTF(词条频率)\times IDF(逆文档频率))

BM25算法(ES5.0后使用) ​

(Score(Q,d)=\sum^n_i\log(1+\frac{N-n+0.5}{n+0.5})·\frac{f_i}{f_i+k_i·(1-b+b·\frac{dl}{avg dl})})

TF-IDF算法与BM25算法对比 ​

image.png

BM25算法降低了TF在过大时的权重,避免词频对查询影响过大

2.算分函数查询 ​

json
GET /indexName/_search
{
  "query": {
    "function_score": {
      "query": {"match": {"all": "外滩"}},  #原始查询条件
      "functions": [{
          "filter": {"term": {"id": "1"}},  #过滤条件,符合条件的文档才会被重新算分
          "weight": 10  #算分函数
        }],
      "boost_mode": "multiply"  #加权模式
    }
  }
}
[算分函数]

weight:给一个常量值,作为函数结果

feild_value_factor:用文档中的某个字段值作为函数结果

random_score:随机生成一个值作为函数结果

script_score:自定义计算公式,公式结果为函数结果

[加权模式]

multiply:两者相乘(默认)

replace:使用算分函数结果替换原有关联度打分(query score)

sum/avg/max/min:求和/求平均/取最大值/取最小值

3.布尔组合查询 ​

布尔查询时一个或多个子查询的组合,子查询的组合方式有如下几种:

  • must:必须匹配每个子查询,类似"与"
  • should:选择性匹配每个子查询,类似"或"
  • must_not:必须不匹配,不参与分数计算,类似"非"
  • filter:必须匹配,不参与分数计算
json
GET /hotel/ _ search
"query": {
  "bool": {
    "must": [  # city属性为:上海
      {"term": {"city": "上海" }},
    ],
    "should": [  # brand属性为:皇冠假日 或 华美达
      {"term": {"brand": "皇冠假日"}},
      {"term": {"brana": "华美达"}}
    ],
    "must_not": [  # price>500 (price不能小于等于500)
      { "range": { "price": { "lte": 500 } }}
    ],
    "filter": [  # score>45
      { "range": {"score": { "gte": 45 } }}
    ]
  }
}

四、搜索结果处理 ​

1.排序 ​

elasticsearch支持对搜索结果排序,默认是根据相关度算分(_ score) 来排序。可以排序字段类型有: keyword类型 、数值类型、地理坐标类型、日期类型等。

json
GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [{
      "FIELD": "desc"//排序字段和排序方式ASC、DESC
    }]
}
json
GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [{
    "_geo_distance" : {
      "FIELD" : "纬度,经度",  #另一种写法:{"lat":纬度,"lon":经度}
      "order" : "asc",
      "unit" : "km"
    }
  }]
}

2.分页 ​

json
GET /indexName/_search
{
  "query": {
    "查询类型": {
      "查询条件": "查询值"
    }
  },
  "from": 0,  #分页开始的索引,默认为0
  "size": 10  #一次获取的文档总数
}
深度搜索问题 ​

ES原生支持分布式,会面临深度搜索问题,其查询过程如下:

  1. 首先在每个分片上都排序查询对应要查询的前若干条文档
  2. 将所有的查询结果进行聚合,在内存中重新排序选出前若干条文档
  3. 从该若干条结果中选取需要的对应部分结果

如果搜索页数过深,结果集越大,对内存与CPU消耗也越高,因此ES设定结果集的查询上限是10000

深度搜索问题解决方案 ​
  1. search after:从上一次的排序值开始,查询下一页的数据,但只能向后逐页查询,不支持随机翻页
  2. scroll:将排序数据形成快照保存在内存,会有额外内存消耗,且搜索结果非实时(官方已不推荐使用)

3.高亮 ​

json
GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  },
  "hightlight": {
    "fields": {
      "FIELD": {  #指定高亮的字段
        "pre_tags": "<em>",  # 用于标记高亮字段的前置标签
        "post_tags": "</em>",  # 用于标记高亮字段的后置标签
        "require_field_match": "false"  #允许搜索字段与高亮字段不一致,默认必须一致才有效果
      }
    }
  }
}

一、聚合 ​

1.聚合的分类 ​

  1. 桶(Bucket)聚合:用于对文档做分组,常见的有如下几种:
  • TermAggregation:按照文档字段值分组
  • Date Histogram:按照日期阶梯分组,如一周为一组或一月为一组
  1. 度量(Metric)聚合:用于计算一些数值,常见的有如下几种:
  • Avg/Max/Min:求平均值/最大值/最小值
  • Stats:同时求max、min、avg、sum等
  1. 管道(pipeline)聚合:以其他聚合的结果为基础做聚合

2.实现聚合 ​

json
GET /indexName/_search
{
  "query": {  # 此处可用查询限定聚合范围,如下例
    "range": {
      "price": {
        "lte": 200  # 对price<200的文档聚合
      }
    }
  }
  "size": 0,  # 设置size为0,结果中不包含文档,只包含聚合结果
  "aggs": {  # 定义聚合
    "brandAgg": {  # 聚合名称
      "terms": {  # 聚合类型,按照品牌值聚合,所以选择terms
        "field": "brand",  # 参与聚合的字段
        "order": {  # 设置聚合结果排序
          "_count": "asc"  # 按照 _count字段(聚合文档数量) 升序排列
        },
        "size": 20  # 希望获取的聚合结果的数量
      }
    }
  }
}
json
GET /indexName/_search
{
  "size": 0,  # 设置size为0,结果中不包含文档,只包含聚合结果
  "aggs": {  # 定义聚合
    "brandAgg": {  # 聚合名称
      "terms": {  # 聚合类型,按照品牌值聚合,所以选择terms
        "field": "brand",  # 参与聚合的字段
        "size": 20  # 希望获取的聚合结果的数量
      },
      "aggs": {
        "score_stats": {  # 子聚合,分组后对结果进行统计
          "stats": {  # 聚合类型
            "field": "score"  # 聚合字段
          }
        }
      }
    }
  }
}

二、拼音自动补全 ​

1.安装自动补全分词器 ​

Github地址:下载地址

安装流程:

  1. 下载对应版本插件,解压
  2. 上传到ElasticSearch的plugin目录,重启ElasticSearch

2.分词器的组成 ​

  • character filters:在tokenizer之前对文本进行处理,例如删除字符、替换字符
  • tokenizer:将文本按照一定的规则切割成词条(term) ,例如keyword,就是不分词;还有ik_ smart
  • tokenizer filter:将tokenizer输出的词条做进一步处理,例如大小写转换、同义词处理、拼音处理等

image.png

3.自定义分词器 ​

json
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
        }
      }
    }
  },
  mapping: {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer",  # 创建索引时使用分词器
        "search_analyzer": "ik_smart"  # 指定搜索时使用的分词器
      }
    }
  }
}

注意:拼音分词器适合在创建倒排索引的时候使用,但不能在搜索的时候使用(会造成同音词混淆)。

4.自动补全实现 ​

为了提高补全查询的效率,对于文档中的字段的类型有一些约束:

  1. 参与补全查询的字段必须是completion类型
  2. 字段的内容一般是用来补全的多个词条形成的数组
json
GET /test/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s",  # 查询关键字
      "completion": {
        "field": "title,  # 补全查询的字段
        "skip_duplicates": true,  # 跳过重复的
        "size": 10  # 获取前10条结果
      }
    }
  }
}