丰富的搜索功能

73 阅读7分钟

搜索辅助功能

  1. 指定返回的字段 考虑到性能问题,需要对搜索结果进行瘦身,指定返回的字段,在ES中,通过_source子句可以设定返回结果的字段,_source指向一个JSON数组,数组中的元素是希望会字段名称。
get /hotel/_search
{
    "_source":["title","city"], //设定只返回title和city字段
    "query":{
        "term":{
            "city":{
                "value":"北京"
                }
            }
        }}
  1. 结果计数
get /hotel/_count
{
    "query":{
        "term":{
            "city":{
                "value":"北京"}}
                }
 }

3.结果分页 默认情况下ES返回前10个搜索匹配的文档,用户可以通过设置form和size来定义搜索位置和每页显示的文档数量。

GET /hotel/_search { 
"from": 0, //设置搜索的起始位置 
"size": 20, //设置搜索返回的文档个数
"query": { //搜索条件 
    "term": { "city": { "value": "北京" } } } 
}

默认情况下用户可以取得最多10000个文档,如果超过该值,ES将会报错。如果要返回多于该值的数据,可以修改max_result_window的值:

put /hotel/_settings{
    "index":{
        "max_result_window":20000
        }
}

作为一个分布式搜索引擎,一个ES索引的数据分布在多个分片中,而这些分片又分配在不同的节点上。一个带有分页的搜索请求往往会跨越多个分片,每个分片必须在内存中构建一个长度为from+size的、按照得分排序的有序队列,用以存储命中的文档。然后这些分片对应的队列数据都会传递给协调节点,协调节点将各个队列的数据进行汇总,需要提供一个长度为number_of_shards*(from+size)的队列用以进行全局排序,然后再按照用户的请求从from位置开始查找,找到size个文档后进行返回。 基于上述原理,ES不适合深翻页。什么是深翻页呢?简而言之就是请求的from值很大。假设在一个3个分片的索引中进行搜索请求,参数from和size的值分别为1000和10

image.png 当深翻页的请求过多时会增加各个分片所在节点的内存和CPU消耗。尤其是协调节点,随着页码的增加和并发请求的增多,该节点需要对这些请求涉及的分片数据进行汇总和排序,过多的数据会导致协调节点资源耗尽而停止服务。

  1. 性能分析
GET /hotel/_search { 
"profile": "true", //打开性能剖析开关 
"query": { //查询条件
    "match": { "title": "金都" } 
    }
}
  1. 评分分析

在使用搜索引擎时,一般都会涉及排序功能。如果用户不指定按照某个字段进行升序或者降序排列,那么ES会使用自己的打分算法对文档进行排序。有时我们需要知道某个文档具体的打分详情,以便于对搜索DSL问题展开排查。ES提供了explain功能来帮助使用者查看搜索时的匹配详情。

GET /hotel/_explain/002 { 
"query": { 
    "match": { //搜索酒店名称匹配“金都”的文档 
    "title": "金都" } } }

丰富的搜索匹配功能

  1. 查询所有文档

GET /hotel/_search { 
    "_source": [ //只返回title和city字段 
        "title", "city" ], 
     "query": { 
         "match_all": { //查询所有文档 
             "boost": 2 //设定所有文档的分值为2.0 
             } }
}
  1. term级别查询
  • term查询是结构化精准查询的主要查询方式,用于查询待查字段和查询值是否完全匹配,
GET /hotel/_search {
    "query": { 
        "term": { 
            "price": { //搜索字段为price,字段类型为double 
            "value": "500" //搜索值为500 
            } } }
}
  • terms查询

查询一个或多个值与待查字段是否完全匹配

GET /hotel/_search { 
    "query": { 
        "terms": { 
            "city": [ //指定查询字段为city 
                "北京", "天津" ] 
                } } 
}
  • range查询

用于范围查询,一般是对数值类型和日期类型数据的查询

标题
gt大于
lt小于
gte大于等于
lte小于或等于
GET /hotel/_search { 
    "query": { 
        "range": { //range查询 
            "price": { //指定字段为price,此处包含边界值 
                "gte": 300, 
                "lte": 500 } 
            } } 
}
  • exists查询

可以用exists搜索字段不为空的条件:

  • 值存在且不是null
  • 值不是空数组
  • 值是数组,但不是[null]
GET /hotel_1/_search { 
    "query": { 
        "exists": { //查询tag字段不为空的文档 
            "field": "tag" 
           } } 
}
  1. 布尔查询
  • must
GET /hotel/_search { 
   "query": { 
       "bool": { 
           "must": [ //must查询,数组内可封装各类子查询 
               { 
               //第一个子查询:城市为北京 
               "term": { 
                   "city": { 
                       "value": "北京" } } },
               { //第二个子查询:价格>=350且价格<=400 
                   "range": { 
                       "price": { "gte": 350, "lte": 400 } } } 
] 
} } 
}**
  • should

  • must not

  • filter

    GET /hotel/_search { 
        "query": { 
            "bool": { 
                "filter": [ 
                    // filter查询,数组内可封装各类子查询 
                    { //第一个子查询:城市为北京 
                    "term": { "city": "北京" } }, 
                    { //第一个子查询:满房状态为否 
                    "term": { "full_room": false } } 
                    ] } } 
    }
    
  1. filter查询原理

在执行过滤条件时,会查询缓存中是否有字段值对应的bitse数据,位图,可以用非常紧凑的格式来表示给定范围内的连续数据,如果有对应的位图数据,则取出备用,如果没有则在es查询后将其放入缓存,并构建位图.

  1. constant score 查询 如果不想让检索频率TF对搜索结果排序有影响,指向过滤某个文本字段是否包含某个词,可以使用constant score将查询语句包装起来。
GET /hotel/_search { 
    "_source": ["amenities"], 
    "query": { 
        "constant_score": { //满足条件即打分为1 
            "filter": { 
                "match": { //查询设施中包含“停车场”的文档 
                    "amenities": "停车场" } } } } 
}
  1. function Score查询

当使用ES进行搜索时,命中的文档默认按照相关度进行排序。有些场景下用户需要干预该“相关度”,此时就可以使用Function Score查询。使用时,用户必须定义一个查询以及一个或多个函数,这些函数为每个文档计算一个新分数。

GET /hotel/_search { 
"_source": ["title","city"],
"query": { 
"function_score": { 
"query": { //查询符合条件的文档 
"term": { 
"city": { 
"value": "北京" } } }, 
"functions": [ //定义函数
{ //此处只定义了一个函数:随机数函数 
"random_score": {} } ],
"score_mode": "sum" //最终分数是各个函数的加和值 
} } }
  1. 全文搜索
  • match查询,只要分词中的一个或多个在文档中存在即可。
  • multi_match查询,多个字段中查询关键字,除了使用布尔查询封装多个match查询之外,可替代的方案时使用muti_match
GET /hotel/_search { 
    "_source": ["title","amenities"], 
          "query": { 
            "multi_match": { "query": "假日", //匹配关键字为“假日” 
            "fields": [ //设置匹配的字段为title和amenities 
                "title",
                "amenities" ] } } 
}
  • match_phrase查询 用途关于匹配短语,搜索确切的短语或邻近的词语。
  1. 基于地理位置查询
GET /hotel/_search { 
    "_source": [ //只返回部分字段 
        "title", 
        "city", 
        "location" ], 
    "query": { 
        "geo_distance": { 
            "distance": "5km", //设置距离范围为5km 
            "location": { //设置中心点经纬度 
                "lat": "39.915143", //设置纬度 
                "lon": "116.4039" //设置经度 } } }
}
  1. 搜索建议

在搜索时,用户每输入一个字符,前端就需要向后端发送一次查询请求对匹配项进行查询,因此这种场景对后端响应速度的要求比较高。为了使用Completion Suggester,其对应的字段类型需要定义为completion类型。在以下示例中定义了一个酒店搜索建议的索引:

PUT /hotel_sug { 
    "mappings": { 
        "properties": { 
            "query_word": { //定义query_word字段,类型为completion 
                "type": "completion" } } } 
}


GET /hotel_sug/_search { 
    "suggest": {
        "hotel_zh_sug": { //定义搜索建议名称 
            "prefix": "如家", //设置搜索建议的前缀 
            "completion": { //设置搜索建议对应的字段名称 
                "field": "query_word" } } } 
}

按字段值排序

  1. 按普通字段值排序

使用sort自居对字段进行排序时需要指定排序的字段,默认按照升序,可以设置order参数为asc或者desc

GET /hotel/_search { 
    "_source": [ //只返回部分字段 
        "title", "price" ], 
     "query": { //搜索条件 
        "match": { 
            "title": "金都" }
            }, 
        "sort": [ { //按照价格降序排列 
            "price": { "order": "desc" } } 
] }
  1. 按照地理距离排序
GET /hotel/_search { 
    "_source": [ //返回部分字段 
        "title", "city", "location" ], 
    "query": { 
        "geo_distance": { 
            "distance": "5km", //设置地理范围为5km 
            "location": { //设置中心点坐标 
                "lat": "39.915143", 
                "lon": "116.4039" } } }, 
    "sort": [ //设置排序逻辑
        { "_geo_distance": { 
            "location": { //设置排序的中心点坐标 
                "lat": "39.915143",
                "lon": "116.4039" }, 
             "order": "asc", //按距离由近到远进行排序
             "unit": "km", //排序所使用的距离的计量单位
             "distance_type": " plane " //排序所使用的距离计算算法 
} } ]
}