search(10)- elastic4s-multi_match:多字段全文搜索

2020-05-18 15:55:22 浏览数 (2)

在全文搜索中我们常常会在多个字段中匹配同一个查询条件或者在不同的字段中匹配不同的条件。比如下面这个例子:

代码语言:javascript复制
GET /books/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":  "和平战争" }},
        { "match": { "author": "托斯泰"   }}
      ] 
    }
  } 
}

我们可以用boolQuery来进行查询语句的组合。全文搜索会产生匹配度评分。boolQuery采取的评分策略是:符合条件的语句越多,评分就越高。如果查询结果按评分倒排序的话,那么排在最前面的就是最有可能的结果了。boolQuery可以包含boolQuery,如下:

代码语言:javascript复制
GET /books/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":  "和平战争" }},
        { "match": { "author": "托斯泰"   }},
        "bool" : {
          "should" : [
            {"match" : { "translator" : "陈"}},
            {"match" : { "translator" : "王"}}
            ]
        }
      ] 
    }
  } 
}

增加条件的意思是:如果翻译者姓陈或姓王,那么评分就高点。不过把boolQuery嵌入另一个boolQuery会影响外部boolQuery的评分结果。因为嵌入的boolQuery只占总评分的三分之一。当然可以通过boost来平衡比重,如下:

代码语言:javascript复制
GET /books/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": 
          { 
            "title":  {
              "query": "和平战争",
              "boost": 2
            }
          }
        },
        { "match": { "author": "托斯泰"   }},
        "bool" : {
          "should" : [
            {"match" : { "translator" : "陈"}},
            {"match" : { "translator" : "王"}}
            ]
        }
      ] 
    }
  } 
}

从上面的例子里可以看到:boolQuery是典型的多字段多条件匹配查询,用户必须明确分辨那些条件在那些字段里匹配。但人们习惯于一句话里表达多个字段的条件。或者他们根本不想分辨任何字段,期望一句话就得到想要的结果。这个时候boolQuery就不太适合使用了。

首先,我们可以尝试在多个字段中匹配同样一个综合语句如:和平战争托斯泰。这时我们可能面临3种选择:

1、best-fields:同样一个条件在不同的字段里匹配产生多个评分,整体查询只取最优评分

2、most-fields: 这个方法有点复杂,需要在建索引时把一个字段按分词方式分成多个字段,查询时取满足条件最多字段的评分

3、cross-fields:把所有涉及的字段合成一个大字段,然后用条件来匹配这个组合而成的字段。这个方法应该最适合我们的要求

我们先分析一下具体场景:一个人想在网站上找一本书,应该会从书名、作者、出版社这几个方面提供查询条件(虽然是在一个输入框输入条件),也就是说用户提供的一个查询条件里可能包含了书名、作者、出版社这几方面的信息。那么第一个版本的搜索请求如下:

代码语言:javascript复制
GET /books/_search
{
   "query": {
     "multi_match": {
       "query": "和平与战争 托斯泰 人民出版社",
       "type": "cross_fields", 
       "fields": ["title","author","publisher"]
     }
   }
}

按理来讲书名的比重应该高于作者,出版社,所以应该为title加比重:

代码语言:javascript复制
GET /books/_search
{
   "query": {
     "multi_match": {
       "query": "和平与战争 托斯泰 人民出版社",
       "type": "cross_fields", 
       "fields": ["title^2","author","publisher"]
     }
   }
}

为了更精确的筛选,词句terms应该采取and进行关联:

代码语言:javascript复制
GET /books/_search
{
   "query": {
     "multi_match": {
       "query": "和平与战争 托斯泰 人民出版社",
       "type": "cross_fields", 
       "fields": ["title","author","publisher"],
       "operator": "and"
     }
   }
}

得出的结果集会大大缩短。用户可以取消一些条件来增加结果范围。做的再仔细点我们还可以在图书的内容上面做点功夫:

代码语言:javascript复制
GET /books/_search
{
   "query": {
     "multi_match": {
       "query": "和平与战争 托斯泰 人民出版社",
       "type": "cross_fields", 
       "fields": ["title^3","author^2","publisher^2","toc","intro"],
       "operator": "and"
     }
   }
}

增加了目录toc, 内容简介intro。不过它们的比重是最低的。

elastic4示例如下:

代码语言:javascript复制
 val qMultiMatch = search("books").query(
    multiMatchQuery("和平与战争 托斯泰 人民出版社")
      .matchType("cross_fields")
      .operator("and")
      .fields(
        "title^3",
        "author^2",
        "publisher^2",
        "toc",
        "intro"
      )
  ).sourceInclude("ISBN","title","publisher","price","author")

0 人点赞