当前位置: 首页 > 工具软件 > Match > 使用案例 >

ElasticSearch查询DSL之全文检索(match_all、match、match_phrase、match_phrase_prefix、multi_match)

秦弘亮
2023-12-01

全文检索

match_all

match_all是没有任何条件,检索全部数据

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

match(Match query

match用来做基本的模糊匹配,在es中会对文本进行分词,在match查询的时候也会对查询条件进行分词,然后通过倒排索引找到匹配的数据。在match中支持以下参数:

  • query:查询条件
  • operator:匹配条件(ANDOR (Default)
  • minimum_should_match:最小匹配的数量,用来指定文档中至少包含几个关键字才算匹配到
  • fuzziness:最大编辑距离,详细参考Term级别查询fuzzy查询一节的内容。
  • prefix_length:通过最大编辑距离进行模糊查询额时候,开始的多少个字符不允许被模糊,默认值为0。下面有例子
  • fuzzy_transpositions:boolean值,默认true,表示扩展模糊选项的时候,是否包含两个相邻字符的位置互换这种手段
  • fuzzy_rewrite:可以重写查询方法,目前还没实践到,关于更多说明可以参考:rewrite parameter
  • analyzer:可以指定分词器,如果不指定,用默认的
  • max_expansions:参考Term级别查询fuzzy查询一节的内容。
  • zero_terms_query:在实际的文档中,可能有很多这样的词,比如中文中的的,了,呢,或者英文中的or、and、is、do等。那么这样的词对我们的搜索可能是没有任何帮助的,我们把这些的词叫做停用词,ES中有一个停用词分析器:Stop token filter,如果我们的查询请求的关键字中包括这些词,并且用到了这个分析器,那么他会帮我们把这些停用词移除掉。如果我们的查询请求中所有的关键字都被移除掉了,就不会匹配到任何文档,那么这个时候,是否要给用户返回一个空呢?ES提供了两种策略,也就是通过这个字段去表示的:
    • none (default):不返回任何文档
    • all:返回所有文档,相当于执行了match_all
  • lenientlenient有仁慈的,宽容的意思。这里表示是否忽略一些输入错误,比如为一个数字类型的字段输入了一个字符串去匹配,如果设置为true,会忽略,默认值是false
  • auto_generate_synonyms_phrase_query:在有些场景,可能一个意思有两种写法,比如ElasticSearch有些人可能会写成ES,虽然写法不一样,但是描述的是一个东西,那么如果我们限定查询条件为ElasticSearch,其实也是希望能搜索到ES相关内容的。我们可以把这种词叫做同义词查询。因此ES为我们提供了这个参数,表示是否开启同义词查询,默认是true,也就是开启的。但是有一个问题就是ES他怎么知道哪些词是同义词呢?Lucene中有一个概念叫Synonym Graph Token Filter,那么ES中也是有的,我们可以通过对这个进行配置来实现同义词查询,配置方式参考: Synonym graph token filter
# match查询中,可以指定查询条件
# 下面的语句会将Eddie Underwood分词,然后只要customer_full_name有这Eddie和Underwood中一个就会命中
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "customer_full_name": "Eddie Underwood"
    }
  }
}

# 在上面的查询中,Eddieh或者Underwood出现一个就可以,有时候如果查询目标内容庞大,其中包括了我们检索的一两个字,也会被搜索到
# 但是其实可能相关性并不大,因此我们还可以通过minimum_should_match指定最少匹配到多少个单词才算匹配到
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "customer_full_name": {
        "query": "Eddie Underwood",
        "minimum_should_match": 2
      }
    }
  },
  "_source":["customer_full_name","currency"]
}

# 当然match中还支持通过operator来指定匹配条件
# 下面的语句会的意思是只有customer_full_name这个字段中同时出现Eddie和Underwood才能命中
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "customer_full_name": {
        "query": "Eddie Underwood",
        "operator": "and"
      }
    }
  }
}

# 很多时候可能一开始就犯错的机会不大,走着走着路才走偏了,所以如果这个参数设置合理,可以减少模糊的次数,提升性能
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "customer_full_name": {
        "query": "Eadie Underwood",
        "fuzziness": 2,  # 限定最大编辑距离为2
        "prefix_length": 2, # 这里默认是0,那么我们输入Eadie是可以匹配到Eddie的,但是如果指定为2,就匹配不到了
        "operator": "and"   # title中如果有Eadie和Underwood才会被匹配到,在文档中有Eddie Underwood的文档
      }
    }
  }, 
  "_source": "customer_full_name"
}

match_phrase(Match phrase query

match_phrase(phrase: 短语) 会对输入做分词,但是需要结果中也包含所有的分词,并且顺序要求一致。这个条件其实有一点苛刻了,有时候可能我输入错了,或者一个短语,只记得其中两个单词,第三个单词死活记不起来怎么办呢?ES也提供了slop这个参数帮我们解决这个问题:

  • slop (default 0):(slop:溢出)来指定额外加几个单词也可以命中。
# match_phrase这种方式,是把query中的条件作为一整个单词进行查询,只有精确匹配到Eddie Underwood才会命中
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match_phrase": {
      "customer_full_name": {
        "query": "Eddie Underwood", 
        "slop": 1  # 如果是Eddie test Underwood也可以被命中
      }
    }
  }
}

match_phrase_prefix(Match phrase prefix query

match_phrase_prefix可以看做match_phrase的一个扩展,他会把query的查询条件进行分词,然后把最后一个单词看做是一个前缀,匹配索引中所有以这个单词为前缀的单词,然后进行返回

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match_phrase_prefix": {
      "customer_full_name":{
        "query": "Eddie"  # 可以匹配到Eddie Underwood、Eddie Utest
      }
    }
  }
}

multi match(Multi-match query

multi_match是在match的基础进行了一些加强,他支持在对个字段进行查询。同时在字段的描述上还支持通配符。

# 在customer_full_name、customer_first_name中有一个字段包括查询条件就可以匹配到
GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "multi_match": {
      "query": "Eddie",
      "fields": ["customer_full_name","customer_first_name"]  # 可以写成["*_name"]
    }
  },
  "_source":["customer_full_name","currency","customer_first_name"]
}

在multi match查询的时候,有时候候可能存在这样的场景,比如说你在CSDN搜索一篇包括mutli和match关键字的博客,这两个关键字可能在文章的标题中有,在内容中也有,但是明显在标题中同时出现这两个关键字的文章可能是我们更关心的,也就是相关性更大的,那么如何让这篇文章脱颖而出,排列在搜索结果的最前面呢?

其实ES会为搜索到的每一个文档做一个算分,通过 _score 这个字段表示,最终会根据这个字段进行一个排序,分数越大的结果越靠前展示。那么在上面这个场景中,我们就可以指定让标题这个字段在算分的时候占有更大的权重,写法类似下面这样:

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "multi_match": {
      "query": "Eddie",
      "fields": ["customer_full_name^3","customer_first_name"]  # 这里通过^3来指定他的算分比重,这个数字越大,他占的比重就越大,其他字段占得比重就越小
    }
  },
  "_source":["customer_full_name","currency","customer_first_name"]
}

multi_match这种查询方式中,还有一个特别重要的字段type,他的每一个查询其实都是依赖这个字段的,虽然我们上面没有指定,那是因为他有一个默认值best_fields。接下来我们一起看看,除了best_fields之外还有那些类型以及每一个类型有什么用处。

  • best_fields:以前面讨论dis_max为例,参考DSL之组合查询的介绍。比如我们要在一个文档中的title和body两个字段中查找brown fox,如果我们要查找的两个单词按照顺序同时出现,那么肯定是比分别出现在不同的字段中是有意义的,我们就把两个单词同时按照顺序出现在一个字段中这种情况叫做best_fields。这样的话,multi_match就会被包装成dis_max这种方式,去查询。那我们知道dis_max还支持一个参数tie_breaker,因此这里也是支持的

    GET baike/_search
    {
      "query": {
        "multi_match": {
          "query": "Brown fox",
          "fields": ["title","body"],
          "type": "best_fields",
          "tie_breaker": 0.1
        }
      }
    }
    
    # 上面的请求最终会按照这样的方式去执行
    POST /baike/_search
    {
      "query": {
        "dis_max": {
          "queries": [
            {"match": {"title": "Brown fox"}},
            {"match": {"body": "Brown fox"}}
          ],
          "tie_breaker": 0.1
        }
      }
    }
    
  • most_fieldsbest_fields是取子查询中算分最高的作为最终算分,而most_fields则是取子查询算分的和作为最终算分,我们也可以理解按照关键字出现的次数来算,出现的越多,算分越高,这里还是可以参考前文关于对dis_max的讨论来理解。

    GET baike/_search
    {
      "query": {
        "multi_match": {
          "query": "Brown fox",
          "fields": ["title","body"],
          "type": "most_fields",
          "tie_breaker": 0.1
        }
      }
    }
    
    # 上面的请求最终会按照这样的方式去执行
    POST baike/_search
    {
      "query": {
        "bool": {
          "should": [
            {"match": {"title":"Brown fox"}},
            {"match": {"body":"Brown fox"}}
          ]
        }
      }
    }
    
  • phrase and phrase_prefix:这两种类型和best_fields的算分策略是一致的,我们讨论best_fields的时候在示例代码中说明他最终会被翻译为dis_max加上match去执行,那么这两种类型就是把matchmatch_phrasematch_phrase_prefix替换掉。

    GET baike/_search
    {
      "query": {
        "multi_match": {
          "query": "Brow",
          "fields": ["title","body"],
          "type": "phrase_prefix",
          "tie_breaker": 0.1
        }
      }
    }
    
    # 上面的请求最终会按照这样的方式去执行
    GET baike/_search
    {
      "query": {
        "dis_max": {
          "tie_breaker": 0.1,
          "queries": [
            {"match_phrase_prefix": {"title": "Brow"}},
            {"match_phrase_prefix": {"body": "Brow"}}
          ]
        }
      }
    }
    
  • bool_prefix:这种类型和most_fields的算分策略是一致的,不过他会用match_bool_prefix替换掉match

    GET baike/_search
    {
      "query": {
        "multi_match": {
          "query": "Brow",
          "fields": ["title","body"],
          "type": "bool_prefix"
        }
      }
    }
    
    # 上面的请求最终会按照这样的方式去执行
    GET baike/_search
    {
      "query": {
        "bool": {
          "should": [
            {"match_bool_prefix": {"title": "Brow"}},
            {"match_bool_prefix": {"body": "Brow"}}
          ]
        }
      }
    }
    
  • cross_fields:参考跨字段查询cross_fields中的介绍。

 类似资料: