Match API:匹配搜索

很多时候我们在搜索的时候其实并不会去做精确匹配,一方面是没必要,一方面是浪费性能,可以说是吃力不讨好。

image.png

当数据索引的时候,Es会使用分词器对文本数据进行分词,并且统计每个词语出现的次数等等信息。当用户对文本数据进行检索的时候,Es会使用同样的分词器对输入内容进行分词,然后与文本内容进行匹配,根据统计信息给每一个词语打分,最后根据公式计算出相关性评分,并且返回相关性最高的 Top 文档。

Es支持全文搜索的API主要有以下几个:

API说明
match可以处理全文本,精确字段(日期,数字等等)。
match phrase短语匹配会将检索内容进行分词,这些词语必须全部出现在被搜索内容中,并且顺序必须一致,默认情况下,这些词必须都是连续的。
match phrase prefix类似 match phrase ,但是最后一个词项会作为前缀,并且匹配这个词项开头的任何词语。
multi match通过 multi match 可以在多个字段上执行相同的查询语句。
match boolean prefix结合bool查询和前缀查询。
match_all & match_none查询全部,和查询0条。

下面我们还是以订单表为例,进行全文搜索的使用分析。

本篇文章使用的 standard 分词器对文本进行分词处理,这个分词器会将文本按照单词切分并且转换为小写。

PUT order
{
  "mappings": {
    "properties": {
        "id": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "sub_order_no": {
          "type": "keyword"
        },
        "order_no": {
          "type": "keyword"
        },
        "price":{
          "type": "double"
        },
        "createTime":{
          "type": "date"
        }
      }
  },
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

## 简单造几条数据
PUT /order/_doc/d17be93b-648f-44b7-993f-98e2ae329306
{
    "id": "d17be93b-648f-44b7-993f-98e2ae329306",
    "name": "iphone13",
    "sub_order_no": [
        "3b9ebe09-99d7-4c79-85a4-e99a721c24b5",
        "86a38185-b100-4713-9a73-36c17f22fba4",
        "8887aed3-ac31-4826-9b1b-0b02e549b4fb",
        "47c735dd-735a-4a3e-a427-2b35f95c1ec3",
        "7ca1fcd4-faf9-44ac-b524-3e2844701dc9"
    ],
    "order_no": "d17be93b-648f-44b7-993f-98e2ae329306",
    "price": 2634.6998447085434,
    "createTime": 1688211961508
}

PUT /order/_doc/47c735dd-735a-4a3e-a427-2b35f95c1ec3
{
    "id": "47c735dd-735a-4a3e-a427-2b35f95c1ec3",
    "name": "xiaomi13",
    "sub_order_no": [],
    "order_no": "47c735dd-735a-4a3e-a427-2b35f95c1ec3",
    "price": 23.50361213313873,
    "createTime": 1688211961508
}

PUT /order/_doc/8887aed3-ac31-4826-9b1b-0b02e549b4fb
{
    "id": "8887aed3-ac31-4826-9b1b-0b02e549b4fb",
    "name": "huawei mate 50",
    "sub_order_no": [],
    "order_no": "8887aed3-ac31-4826-9b1b-0b02e549b4fb",
    "price": 21.132645156267703,
    "createTime": 1688211961508
}

PUT /order/_doc/86a38185-b100-4713-9a73-36c17f22fba4
{
    "id": "86a38185-b100-4713-9a73-36c17f22fba4",
    "name": "sanxing",
    "sub_order_no": [],
    "order_no": "86a38185-b100-4713-9a73-36c17f22fba4",
    "price": 35.66395720954454,
    "createTime": 1688211961508
}

PUT /order/_doc/7ca1fcd4-faf9-44ac-b524-3e2844701dc9
{
    "id": "7ca1fcd4-faf9-44ac-b524-3e2844701dc9",
    "name": "vivo",
    "sub_order_no": [],
    "order_no": "7ca1fcd4-faf9-44ac-b524-3e2844701dc9",
    "price": 18.91113695347623,
    "createTime": 1688211961508
}

PUT /order/_doc/3b9ebe09-99d7-4c79-85a4-e99a721c24b5
{
    "id": "3b9ebe09-99d7-4c79-85a4-e99a721c24b5",
    "name": "oppo phone is very good",
    "sub_order_no": [],
    "order_no": "3b9ebe09-99d7-4c79-85a4-e99a721c24b5",
    "price": 9.281517420473545,
    "createTime": 1688211961508
}

1.Match 匹配查询

匹配查询可以处理全文本、精确字段(日期、数字等)。

GET order/_search
{
  "query": {
    "match": {
      "name": "iphone13 xiaomi13"
    }
  }
}

我们通过 Match API 进行了一次全文本字段的查询,我们查询名字中含有 iphone13 xiaomi13 的文档,系统匹配了 iphone13xiaomi13两个文档。

在进行全文本字段检索的时候,Match API 提供了operatorminimum_should_match 参数:

参数说明
operator参数值可以为or 或者 and来控制检索词项间的关系,默认是or。所以上面的查询语句中只要出现了xiaomi13或者iphone13的文档都可以匹配上。
minimum_should_match可以指定词项的最少匹配个数,其值可以指定为某个具体的数字,但因为我们一般无法预估检索内容的词项数量,一般将其设置为一个百分比。

For Example:至少有百分之七十的词项匹配才会返回该文档。

GET order/_search
{
  "query": {
    "match": {
      "name": {
        "query": "oppo phone is very bad",
        "operator": "or",
        "minimum_should_match": "70%"
      }
    }
  }
}

另外,除了处理全文本字段外,Match API 支持查询包含精确字段的文档。

For Example:匹配创建时间为 2023-07-01 的文档。

GET order/_search
{
  "query": {
    "match": {
      "createTime": "2023-07-01"
    }
  }
}

Match API 是怎么样运行的呢? 匹配查询属于bool类型,这意味着他会对输入的数据进行分词,并且分词过程中会根据输入的数据构建出一个bool查询。运算符参数设置为 or 或者 and 来控制bool子句。可以使用 minimum_should_match 参数设置要匹配的可选 should 子句的最小数量。

另外可以设置分词器来控制哪个分词器对文本执行分析过程,默认为 Mapping 中指定的分词器 或默认搜索分词器。

可以将宽松参数设置为true,以忽略由数据类型不匹配导致的异常。比如尝试使用文本查询字符串数值字段,默认为 false

2.Match phrase 短语匹配

短语匹配会将检索内容进行分词,这些词语必须全部出现在被检索内容中,并且顺序必须一致,默认情况下这些词都必须连续。

For Example:

# 短语匹配
POST order/_search
{
  "query": {
    "match_phrase": {
      # 换成 "oppo phone very is"是查询不到的
      "name": "oppo phone is very" 
    }
  }
}

当我们根据oppo is搜索订单名字的时候搜索不到文档。因为没有订单的名字带有这个短语。这个时候可以通过 slop 参数指定词项之间的距离差值,即两个词项中可以含有多少个其他不相关的词语,默认值是0。

POST order/_search
{
  "query": {
    "match_phrase": {
      "name": {
        "query": "oppo is",
        "slop": 1
      }
    }
  }
}

slop 参数设置为1的时候 oppo is 也可以搜索到文档,原因是文档中 oppo phone is 中间的 phone 因为不相关被跳过了。

3.Match phrase prefix 短语前缀匹配

match phrase prefixmatch phrase 类似,但最后一个词项会作为前缀,并且匹配这个词项开头的任何词语。可以使用 max_expansions 参数来控制最后一个词项的匹配数量,此参数默认值为 50。

For Example:

# 匹配以 "oppo phone" 开头的短语
POST order/_search
{
  "query": {
    "match_phrase_prefix": {
      "name": "oppo phone"
    }
  }
}

# 匹配以 "oppo phone" 开头的短语,每个分片最多匹配 2 个
POST order/_search
{
  "query": {
    "match_phrase_prefix": {
      "name": {
        "query": "oppo phone",
         "max_expansions": 2
      }
    }
  }
}

上面第一条SQL可以匹配订单名中含有 "oppo phone12"、"oppo phone13" ...... 等短语的文档。而第二条SQL,我们限制了最后一个词项的通配匹配个数为 2。

max_expansions 参数是分片级别的,也就是 max_expansions = 2 的话,每个分片最多匹配 2 个文档,如果有 3 个分片的话,最多返回 6 个匹配的文档。

一般来说 match_phrase_prefix API 可以实现比较粗糙的自动建议功能,但要实现自动建议的功能,可以使用 Suggest API

4.Multi match 多字段匹配

multi-match API 构建在 match 查询的基础上,可以允许在多个字段上执行相同的查询。

GET /order/_search
{
  "query": {
    "multi_match": {
      "query": "oppo",
      "fields": ["nam*", "order_no^3"]
    }
  }
}
参数说明
best_fields默认的类型,会执行 match 查询并且将所有与查询匹配的文档作为结果返回,但是只使用评分最高的字段的评分来作为评分结果返回。
most_fields会执行 match 查询并且将所有与查询匹配的文档作为结果返回,并将所有匹配字段的评分加起来作为评分结果。
phrase在 fields 中的每个字段上均执行 match_phrase 查询,并将最佳匹配字段的评分作为结果返回。
phrase_prefix在 fields 中的字段上均执行 match_phrase_prefix 查询,并将最佳匹配字段的评分作为结果返回。
cross_fields它将所有字段当成一个大字段,并在每个字段中查找每个词。例如当需要查询英文人名的时候,可以将 first_name 和 last_name 两个字段组合起来当作 full_name 来查询。
bool_prefix在每个字段上创建一个 match_bool_prefix 查询,并且合并每个字段的评分作为评分结果。

上述的这几种类型,其实就是设置算分的方式和匹配文档的方式不一样,可以使用 type 字段来指定这些类型。

For Example:

GET /order/_search
{
  "query": {
    "multi_match": {
      "query": "oppo",
      "fields": ["name", "order_no"],
      "type": "best_fields",
      "tie_breaker": 0.3
    }
  }
}

该SQL将会在 order 索引中查找 "name" 字段包含 "oppo" 的文档或者在 "order_no" 字段中包含 "oppo" 的文档。

默认文档的相关性算分由得分最高的字段来决定的,但当指定 tie_breaker 的时候,算分结果将会由以下算法来决定:

  1. 令算分最高的字段的得分为 s1
  2. 令其他匹配的字段的算分 * tie_breaker 的和为 s2
  3. 最终算分为:s1 + s2

tie_breaker 的取值范围为:[0.0, 1.0]。当其为 0.0 的时候,按照上述公式来计算,表示使用最佳匹配字段的得分作为相关性算分。当其为 1.0 的时候,表示所有字段的得分同等重要。当其在 0.0 到 1.0 之间的时候,代表其他字段的得分也需要参与到总得分的计算当中去。通俗来说就是其他字段可以使用 "tie_breaker" 来进行“维权”  。

5.Match boolean prefix 布尔前缀匹配

一个 match_bool_prefix 查询会对输入的内容进行分词,并根据分词后的结果构造一个boolean查询。除最后一个Term外的每个Term都用于Term查询。最后一个Term用于前缀查询。

For Example:

GET order/_search
{
  "query": {
    "match_bool_prefix" : {
      "name" : "quick brown f"
    }
  }
}

其中分析生成Term [quick,brown,f]类似于下面的bool查询。

GET order/_search
{
  "query": {
    "bool" : {
      "should": [
        { "term": { "name": "quick" }},
        { "term": { "name": "brown" }},
        { "prefix": { "name": "f"}}
      ]
    }
  }
}

match_bool_prefixmatch_phrase_prefix 一个很重要的不同在于 match_phrase_prefix 将输入的内容作为一个短语匹配,但是 match_bool_prefix 可以在任何位置匹配它的Terms。举个例子,match_bool_prefix 可以匹配一个包含 "quick brown fox" 的字段,也可以匹配 "brown fox quick",甚至可以匹配一个包含 "quick" 的字段,或者匹配一个任意位置以 "f" 开始的Term。

match_bool_prefix 默认使用的分词器是查询字段的分词器,也可以额外指定分词器。

GET order/_search
{
  "query": {
    "match_bool_prefix": {
      "name": {
        "query": "quick brown f",
        "analyzer": "keyword"
      }
    }
  }
}

match_bool_prefix 查询支持 minimum_should_matchoperator 参数,这些参数的作用与match查询中描述的一样,并应用于构建的bool查询。构建的bool查询中的子句数量通常是通过对查询文本进行分析产生的术语数量。简单来说,match_bool_prefix 查询是一种结合了布尔查询和前缀查询的查询类型。minimum_should_match 参数指定了bool查询中至少应该匹配的子句数量,而 operator 参数指定了子句之间的逻辑操作符(AND或OR)。

[fuzziness、prefix_length、max_expansions、fuzzy_transpositions,fuzzy_rewrite]参数可以应用于为除最后一个术语之外的所有术语构建的子查询,但对于最后一个术语构建的前缀查询没有任何影响。简而言之,这些参数用于控制模糊匹配的行为。fuzziness参数指定了模糊匹配的容错度,prefix_length指定了前缀匹配的长度,max_expansions指定了允许的最大扩展数,fuzzy_transpositions指定了是否允许交换相邻字符进行匹配,而fuzzy_rewrite参数指定了模糊匹配的重写方法。

6.Match all & none

最常用的查询,给所有的文档都打1.0分。

GET order/_search
{
    "query": {
        "match_all": {}
    }
}

_score 可以通过 boost 参数修改。

GET order/_search
{
  "query": {
    "match_all": { "boost" : 1.2 }
  }
}

还有一个和 match_all 相反的API,一条记录也不返回。

GET order/_search
{
  "query": {
    "match_none": {}
  }
}

官方文档

全部评论

相关推荐

迷茫的大四🐶:自信一点,我认为你可以拿到50k,低于50k完全配不上你的能力,兄弟,不要被他们骗了,你可以的
点赞 评论 收藏
分享
点赞 评论 收藏
分享
24分钟1.自我介绍2.黑盒测试用例设计方法3.运用刚才的测试方法对手机端淘宝购物车结算页面进行测试4.测试流程5.需求文档没有标明边界值,怎么确定边界值,确定边界值后怎么测6.你们公司自动化测试是测业务主流程还是新需求反问:不足之处答:问答问题前思考3s再答,针对提问再答
一笑而过2222:边:边界值分析法(处理输入边界) 类:等价类划分法(划分有效 / 无效输入) 定:判定表法(多条件组合的逻辑判定) 因:因果图法(分析输入输出的因果关系) 迁:状态迁移法(覆盖系统状态转换路径) 场:场景法(模拟端到端业务流程) 正:正交试验法(多因素组合的测试优化) 错:错误推测法(基于经验推测潜在漏洞) 记忆逻辑链(按测试场景优先级排序) 先处理明确输入:边界值 + 等价类(边类) 再处理条件组合:判定表 + 因果图(定因) 接着处理状态与流程:状态迁移 + 场景法(迁场) 最后优化多因素与补漏:正交试验 + 错误推测(正错)
查看6道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务