1. DSL查询分类和基本语法

ElasticSearch提供了基于Json的DSL来定义查询,常见的查询类型包括:
• 查询所有:查询出所有数据,一般测试用,一般不是查出所有,一次性查询20条。例如 match_all
• 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引中匹配,例如:
match_query、mutil_match_query
• 精确查询:根据精确词条查找数据,一般查找keyword、数值、日期、boolean等类型字段。例如:
ids、range(数字或者日期范围)、term(具体的某一个数字);
• 地理(geo)查询:根据经纬度查询。例如:
geo_diatance、geo_bounding_box
• 复合查询,复合查询可以将上述各种查询条件组合几来,合并查询条件,例如:
bool、function_score。

2. DSL全文检索查询语句

会对用户输入内容分词,常用于搜索框搜索。
match:根据一个字段查询,会对用户输入内容分词,然后去倒排索引库检索,语法:

GET/indexName/_search{
"query":{
     "match"{   #  查询方式,全文检索
        "FIELD":"TEXT"  # 字段:查询文本
            }
        }
}

multi_match:根据多个字段查询,参与查询字段越多,查询性能越差,语法:

Get/indexName/_search
{
"query":{
    "multi_match":{
        "query": "text",
        "fields": ["field1", "field2"]
            }
      }
}

如果想要查询多个字段,建议使用match,将多个字段使用copy to的方式拷贝到一个字段中去,创建一个新的字段名。

3. DSL精确查询语句

精确查询一般是查找keyword、数值、日期、boolean等类型字段,所以不会对搜索条件分词,常见的有:
term:根据词条精确值查询,语法如下:

Get/indexName/_search
{
    "query":{
        "term"{
           "FIELD"{
               "value": "value"
            }
        }
    }
}

range:根据值范围查询,例如根据日期,数值类型查询,语法如下:

Get/indexName/_search
{
  “query”:{
    "range":{
      "FIELD":{
          "gte": 10,
          "lte": 20
        }
      }
   }
}
4 .DSL地理查询语句

根据经纬度查询。常见得场景有:
• 携程:搜索我附近的酒店。
• 滴滴:搜索我附近的滴滴。
• 微信:收缩我附近的人。
地理查询的方式有很多总,如下:
• geo_bounding_box:查询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
                 }
              }
          }
      }
}

top_left 和bottom_right代表地图上的两个点,以两个点为准,画出一个矩形,查询的是在矩形范围内的文档,如下图所示:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP
geo_distance:查询到指定中心点小于某个距离值的所有文档,语法如下:

GET /indexName/_search
{
   "query":{
        "geo_distance":{
                "distance:"15km",    ## 距离
                "FIELD": "31.21,121.5"   ## 中心点
               }
        }
}

如下图所示,查询的就是以31.21,121.5为中心点,小于15Km范围内的所有文档,适合用于查询附近的人的场景:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP

5. DSL复合查询

复合查询:可以将其他简单查询组合起来,实现更加复杂的搜索逻辑。

5.1 function score

算分函数查询,可以控制文档相关性算分,控制文档排名。例如,当我们利用match查询时,文件结果会根据与搜索词条的关联度打分,返回结果时按照分值降序排列。
如下图所示,我们搜索 “虹桥如家”,结果如下,会发现包含虹桥如家的分数最高,排在最前面。
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP

打分的算法如下图所示:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP
ES中使用的是BM25算法,BM25算法相对于TF-IDF算法的优势就是,随着词频不断增加,相关性算分不会无限增加,而TF-IDF算法随着词频不断增加,相关性算分会不断无限增加,如下图所示:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP
总结:ES中的相关性打分算法在ES5.0之前采用的是TF-IDF,会随着词频增加而越来越大,在ES5.0之后,采用BM25,会随着词频增加而不断增大,但增长曲线会趋于水平。
上面介绍了ES的相关性打分,但是我们可以根据需求修改文档的相关性算分,人为控制排名,根据新得到的算分排序,就需要讲解function score query

5.2 复合查询- function score query

语法:

GET /hotel/_search
{
  "query":{
    "function score":{
      "query":{"match":{"all":"外滩"}}, ## 原始查询条件,搜索文档并根据相关性打分(query score)
      "functions":[
               {
                  "filter": {"term":{"id":"1"}},  ## 过滤条件,复合条件的文档才会被重新算分,例如这里是id为1的会重新算分
                  "weight": 10  
                  ## 算分函数,算分函数的结果称为function score,将来会与query score 运算,得到新的算分,常见的算分函数有
                  ## 1.weight:给一个常量值,作为函数结果(function score);
                  ## 2.field_value_factor:用文档中的某个字段值作为函数结果;
                  ## 3. random_score:随机生成一个值,作为函数结果;
                  ## 4. script_socre:自定义计算公式,公式结果作为函数结果
               }
             ],
           "boost mode":"multiply"
           ## 加权模式,定义function score与query score的运算方式,包括:
           ## 1.multiply:两者相乘。默认就是这个;
           ## 2.replace:用function score 替换 query score;
           ## 3.其他:sum、avg、max、min。
       }
    }
 }

案例:给“如家”这个品牌的酒店排名靠前一些。我们需要考虑function score的三要素:
• 哪些文档需要算分加权?这里就是对应的filter,品牌为如家的需要过滤。
• 算分函数是什么?这里我们可以选择weight算分函数。
• 加权模式是什么?这里对应的就是boost_mode,我们可以选择multiply。
查询语句如下:

GET /hotel/_search
{
 "query":{"
   function_score":{
     "query":{//..·},
     "functions":[ // 算分函数
       "filter":{ // 满足的条件,品牌必须是如家
         "term":{
           "brand":"如家"
           }
         },
       "weight":2 // 算分权重为2
       }
     },
    "boost_mode": "sum"
    }
  }
}
4.5.3 复合查询-Boolean Query

复合查询中的布尔查询是一个或多个查询子句的组合。子句的组合方式有:
• must:必须匹配每个子查询,类似“与”;
• should:选择性匹配子查询,类似“或”;
• must_not : 必须不匹配,不参与算分,类似 “非”;
• filter:必须匹配,不参与算分。
案例,如下图所示:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP

6. DSL搜索结果处理
6.1 排序

ES支持对搜索结果的排序,默认是根据相关度算分来排序。一旦指定自己的排序字段,ES就放弃相关度算分,查询的效率也会提升。可以排序的字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
keyword类型、数值类型、日期类型排序语法:

GET /indexName/_search
{
"query":{
    "match_all":{}
    },
"sort":[
   {
    "FIELD1": "desc"//排序字段和排序方式ASC、DESC
    },
    {
    "FIELD2": "asc"//排序字段和排序方式ASC、DESC
     }
    ]
  }

根据地理坐标排序语法:

GET /indexName/search
{
 "query":{
   "match_all":{}
  },
 "sort":[
  {
   "_geo_distance" :{
           "FIELD1":"纬度,经度",
            "order" : "asc",
            "unit" :"km"
          }
       }
    ]
}
6.2 分页

ES默认情况下只返回top 10条数据。而如果要查询更多数据就需要修改分页参数了。
ES中通过修改from 、size参数来控制要返回的分页结果,语法如下:

GET /hotel/_search
{
  "query": {
     "match_all": {}
   },
   "from":990,## 分页开始的位置,默认为0;
   "size":10,## 期望获取的文档总数
   "sort":[
         {"price": "asc"}
    ]
}

ES分页使用虽然跟mysql很像,只需要指定from,size即可,但原理和mysql存在较大的差异,ES分页的原理采用的是一种逻辑分页,假如现在
需要查990到1000的文档数据,如下图所示,ES会查出从0-1000的所有数据,然后截出990-1000的文档数据:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP
上面这种截取方式对于单个节点的ES是没有问题的,但是在实际情况下,为了能够存储更多的数据,那么ES是会进行集群部署的,一旦进行集群,ES就会对文档数据进行拆分,放到不同的机器上,如下图所示:
ElasticSearch DSL查询、排序 、分页的原理及语法-LMLPHP
ES是分布式的,所以会面临深度分页的问题,例如现在按照price字段排序后,获取from = 990, size = 10的数据,那么就会获取0到1000的文档数据,然后截取,但是现在有多个ES集群部署了,如果只是截取某一台0-1000数据文档的10条数据那么就存在问题。ES集群情况下分页截取的原理如下:
• 首先在每个集群数据分片上都排序并查询前1000条文档;
• 然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档;
• 最后从这1000条中,选取从990开始的10条文档。

如果搜索页数过深,或者结果集(from+size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000,一旦超过10000,就会报错,一般我们在实际情况中,从业务层面就限定查询不能超过10000条,如果实际的业务就要查询超过10000条,ES提供了两种解决办法:
• search after:分页时需要排序,原理是从上一次的排序字段后的末尾值开始,走位查询条件,查询下一页数据,分页只能往后翻,不能往前翻。官方推荐使用的方式;
• scroll:原理将排序数据形成快照,保存在内存,对内存消耗非常大。官方已经不推荐使用。

总结:
from + size:
• 优点:支持随机翻页;
• 缺点:深度分页问题,默认查询上限(from+size)是10000场景;
• 百度、京东、谷歌、淘宝这样的随机翻页搜索。
after search :
• 优点:没有查询上限(单次查询的size不超过10000);
• 缺点:只能向后逐页查询,不支持随机翻页;
• 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页。
scroll:
• 优点:没有查询上限(单次查询的size不超过10000);
• 缺点:会有额外内存消耗,并且搜索结果是非实时的
• 场景:海量数据的获取和迁移。从ES7.1开始不推荐,建议用 aftersearch方案。

6.3 高亮

就是在搜索结果中把搜索关键字突出显示。
原理如下:
• 将搜索结果中的关键字用标签标记出来
• 在页面中给标签添加css样式
语法如下:

GET /hotel/_search
{
  "query":{
    "match" :{
      "FIELD": "TEXT"
    }
 },
"highlight": {
 "fields":{ ## 指定要高亮的字段
  "FIELD":{
    "pre_tags": "<em>", ## 用来标记高亮字段的前置标签
    "post_tags":"</em>” ## 用来标记高亮字段的后置标签
    }
   }
  }
 }
02-27 05:54