DSL查询
Elasticsearch的查询可以分为两大类:
-  
叶子查询(Leaf query clauses):一般是在特定的字段里查询特定值,属于简单查询,很少单独使用。
 -  
复合查询(Compound query clauses):以逻辑方式组合多个叶子查询或者更改叶子查询的行为方式
 
语法示例:
GET /{索引库名}/_search
{
  "query": {
    "查询类型": {
      // .. 查询条件
    }
  }
} 
无条件查询的类型是:match_all:
GET /items/_search
{
  "query": {
    "match_all": {
      
    }
  }
} 
获取到的结果


你会发现虽然是match_all,但是响应结果中并不会包含索引库中的所有文档,而是仅有10条。这是因为处于安全考虑,elasticsearch设置了默认的查询页数。
叶子查询
叶子查询的类型也可以做进一步细分,详情大家可以查看官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/query-dsl.html
这里列举一些常见的,例如:
-  
全文检索查询(Full Text Queries):利用分词器对用户输入搜索条件先分词,得到词条,然后再利用倒排索引搜索词条。例如:
-  
match: -  
multi_match 
 -  
 -  
精确查询(Term-level queries):不对用户输入搜索条件分词,根据字段内容精确值匹配。但只能查找keyword、数值、日期、boolean类型的字段。例如:
-  
ids -  
term -  
range 
 -  
 -  
地理坐标查询:用于搜索地理位置,搜索方式很多,例如:
-  
geo_bounding_box:按矩形搜索 -  
geo_distance:按点和半径搜索 
 -  
 
全文检索查询
全文检索的种类也很多,详情可以参考官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/full-text-queries.html
match(匹配单个字段),语法如下:
GET /{索引库名}/_search
{
  "query": {
    "match": {
      "字段名": "搜索条件"
    }
  }
} 
使用实例:
GET /items/_search
{
  "query": {
    "match": {
      "name": "手机"
    }
  }
} 
查询结果

multi_match(匹配多个字段):语法如下
GET /{索引库名}/_search
{
  "query": {
    "multi_match": {
      "query": "搜索条件",
      "fields": ["字段1", "字段2"]
    }
  }
} 
实例,我们搜索带华为二字的品牌与名字的商品
GET /items/_search
{
  "query": {
    "multi_match": {
      "query": "华为",
      "fields": ["brand","name"]
    }
  }
} 

精确查询
推荐查找keyword、数值、日期、boolean类型的字段。
详情可以查看官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/term-level-queries.html
term查询(精确查询某个字段的相同值的文档),语法如下:
GET /{索引库名}/_search
{
  "query": {
    "term": {
      "字段名": {
        "value": "搜索条件"
      }
    }
  }
} 
实例,查询一个品牌为诺基亚的商品
GET /items/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "诺基亚"
      }
    }
  }
} 

range查询(处于某个字段范围内的文档),语法如下: 
GET /{索引库名}/_search
{
  "query": {
    "range": {
      "字段名": {
        "gte": {最小值},
        "lte": {最大值}
      }
    }
  }
} 
实例,查询价格处于500-1000的商品(由于数据库的存储单位为分,所以我们查询时多加两个零):
GET /items/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 50000,
        "lte": 100000
      }
    }
  }
} 

range是范围查询,对于范围筛选的关键字有:
-  
gte:大于等于 -  
gt:大于 -  
lte:小于等于 -  
lt:小于 
复合查询
算分函数查询
采用的相关性打分算法是BM25算法,公式如下:

基本语法:
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)基于运算模式做运算,得到最终结果,作为相关性算分。
 
因此,其中的关键点是:
-  
过滤条件:决定哪些文档的算分被修改
 -  
算分函数:决定函数算分的算法
 -  
运算模式:决定最终算分结果
 
实例,给IPhone这个品牌的手机算分提高十倍,分析如下:
-  
过滤条件:品牌必须为IPhone
 -  
算分函数:常量weight,值为10
 -  
算分模式:相乘multiply
 
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {  .... }, // 原始查询,可以是任意条件
      "functions": [ // 算分函数
        {
          "filter": { // 满足的条件,品牌必须是Iphone
            "term": {
              "brand": "Iphone"
            }
          },
          "weight": 10 // 算分权重为2
        }
      ],
      "boost_mode": "multipy" // 加权模式,求乘积
    }
  }
} 
bool查询
bool查询,即布尔查询。就是利用逻辑运算来组合一个或多个查询子句的组合。bool查询支持的逻辑运算有:
-  
must:必须匹配每个子查询,类似“与”
 -  
should:选择性匹配子查询,类似“或”
 -  
must_not:必须不匹配,不参与算分,类似“非”
 -  
filter:必须匹配,不参与算分
 
bool查询基本语法:
GET /items/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"name": "手机"}}
      ],
      "should": [
        {"term": {"brand": { "value": "vivo" }}},
        {"term": {"brand": { "value": "小米" }}}
      ],
      "must_not": [
        {"range": {"price": {"gte": 2500}}}
      ],
      "filter": [
        {"range": {"price": {"lte": 1000}}}
      ]
    }
  }
} 

出于性能考虑,与搜索关键字无关的查询尽量采用must_not或filter逻辑运算,避免参与相关性算分
 我们要搜索手机,但品牌必须是华为,价格必须是900~1599,那么可以这样写:
GET /items/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"name": "手机"}}
      ],
      "filter": [
        {"term": {"brand": { "value": "华为" }}},
        {"range": {"price": {"gte": 90000, "lt": 159900}}}
      ]
    }
  }
} 

排序
elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。不过分词字段无法排序,能参与排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
详细说明可以参考官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/sort-search-results.html
语法如下:
GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "排序字段": {
        "order": "排序方式asc和desc"
      }
    }
  ]
} 
实例,按照商品价格进行排序
GET /items/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
} 
可以看到商品价格呈现降序

分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。
基础分页
elasticsearch中通过修改from、size参数来控制要返回的分页结果:
-  
from:从第几个文档开始 -  
size:总共查询几个文档 
类似于mysql中的limit ?, ?
官方文档如下:
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/paginate-search-results.html
基本语法:
GET /items/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0, // 分页开始的位置,默认为0
  "size": 10,  // 每页文档数量,默认10
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
} 
深度分页
针对深度分页,elasticsearch提供了两种解决方案:
-  
search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。 -  
scroll:原理将排序后的文档id形成快照,保存下来,基于快照做分页。官方已经不推荐使用。 
详情见文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/paginate-search-results.html
总结:
大多数情况下,我们采用普通分页就可以了。查看百度、京东等网站,会发现其分页都有限制。例如百度最多支持77页,每页不足20条。京东最多100页,每页最多60条。
因此,一般我们采用限制分页深度的方式即可,无需实现深度分页。
高亮
-  
高亮词条都被加了
<em>标签 -  
<em>标签都添加了红色样式 
高亮标签肯定是由服务端提供数据的时候已经加上的。
基本语法如下:
GET /{索引库名}/_search
{
  "query": {
    "match": {
      "搜索字段": "搜索关键字"
    }
  },
  "highlight": {
    "fields": {
      "高亮字段名称": {
        "pre_tags": "<em>",
        "post_tags": "</em>"
      }
    }
  }
} 
注意:
-  
搜索必须有查询条件,而且是全文检索类型的查询条件,例如
match -  
参与高亮的字段必须是
text类型的字段 -  
默认情况下参与高亮的字段要与搜索字段一致,除非添加:
required_field_match=false 
实例,我们将华为二字高亮显示
GET /items/_search
{
  "query": {
    "match": {
      "name": "华为"
    }
  },
  "highlight": {
    "fields": {
      "name": {
        "pre_tags": "<em>",
        "post_tags": "</em>"
      }
    }
  }
} 

总结:
查询的DSL是一个大的JSON对象,包含下列属性:
-  
query:查询条件 -  
from和size:分页条件 -  
sort:排序条件 -  
highlight:高亮条件 
数据聚合
Bucket聚合
其实就是以分类(category)字段对数据分组。category值一样的放在同一组,属于Bucket聚合中的Term聚合。
基本语法如下:
GET /items/_search
{
  "size": 0, 
  "aggs": {
    "category_agg": {
      "terms": {
        "field": "category",
        "size": 20
      }
    }
  }
} 
语法说明:
-  
size:设置size为0,就是每页查0条,则结果中就不包含文档,只包含聚合 -  
aggs:定义聚合-  
category_agg:聚合名称,自定义,但不能重复-  
terms:聚合的类型,按分类聚合,所以用term-  
field:参与聚合的字段名称 -  
size:希望返回的聚合结果的最大数量 
 -  
 
 -  
 
 -  
 
带条件聚合
我们需要从需求中分析出搜索查询的条件和聚合的目标:
-  
搜索查询条件:
-  
价格高于3000
 -  
必须是手机
 
 -  
 -  
聚合目标:统计的是品牌,肯定是对brand字段做term聚合
 
语法如下:
GET /items/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "category": "手机"
          }
        },
        {
          "range": {
            "price": {
              "gte": 300000
            }
          }
        }
      ]
    }
  }, 
  "size": 0, 
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    }
  }
} 
Metric聚合
语法如下:
GET /items/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "category": "手机"
          }
        },
        {
          "range": {
            "price": {
              "gte": 300000
            }
          }
        }
      ]
    }
  }, 
  "size": 0, 
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brand",
        "size": 20
      },
      "aggs": {
        "stats_meric": {
          "stats": {
            "field": "price"
          }
        }
      }
    }
  }
} 
可以看到我们在brand_agg聚合的内部,我们新加了一个aggs参数。这个聚合就是brand_agg的子聚合,会对brand_agg形成的每个桶中的文档分别统计。
-  
stats_meric:聚合名称-  
stats:聚合类型,stats是metric聚合的一种-  
field:聚合字段,这里选择price,统计价格 
 -  
 
 -  
 
由于stats是对brand_agg形成的每个品牌桶内文档分别做统计,因此每个品牌都会统计出自己的价格最小、最大、平均值。
总结
aggs代表聚合,与query同级,此时query的作用是?
-  
限定聚合的的文档范围
 
聚合必须的三要素:
-  
聚合名称
 -  
聚合类型
 -  
聚合字段
 
聚合可配置属性有:
-  
size:指定聚合结果数量
 -  
order:指定聚合结果排序方式
 -  
field:指定聚合字段
 
![C语言程序设计-[4] 算法和程序结构](https://i-blog.csdnimg.cn/direct/bf674de5b3c1460f8737b7d274729a00.png)


















