elasticsearch之match系列

准备数据

PUT zhifou/doc/1
{
  "name":"顾老二",
  "age":30,
  "from": "gu",
  "desc": "皮肤黑、武器长、性格直",
  "tags": ["", "", ""]
}

PUT zhifou/doc/2
{
  "name":"大娘子",
  "age":18,
  "from":"sheng",
  "desc":"肤白貌美,娇憨可爱",
  "tags":["", "",""]
}

PUT zhifou/doc/3
{
  "name":"龙套偏房",
  "age":22,
  "from":"gu",
  "desc":"mmp,没怎么看,不知道怎么形容",
  "tags":["造数据", "",""]
}


PUT zhifou/doc/4
{
  "name":"石头",
  "age":29,
  "from":"gu",
  "desc":"粗中有细,狐假虎威",
  "tags":["", "",""]
}

PUT zhifou/doc/5
{
  "name":"魏行首",
  "age":25,
  "from":"广云台",
  "desc":"仿佛兮若轻云之蔽月,飘飘兮若流风之回雪,mmp,最后竟然没有嫁给顾老二!",
  "tags":["闭月","羞花"]
}

 

match系列之match(按条件查找)

我们查看来自顾家的都有哪些人。

ET zhifou/doc/_search
{
  "query": {
    "match": {
      "from": "gu"
    }
  }
}

上例,查询条件是一步步构建出来的,将查询条件添加到match中即可,而match则是查询所有from字段的值中含有gu的结果就会返回。

match系列之match_all(查询全部)

除了按条件查询之外,我们还可以查询zhifou索引下的doc类型中的所有文档,那就是查询全部

GET zhifou/doc/_search
{
  "query": {
    "match_all": {}
  }
}

match_all的值为空,表示没有查询条件,那就是查询全部。就像select * from table_name一样。

返回的是zhifou索引下doc类型的所有文档!

match系列之match_phrase(短语查询)

我们现在已经对match有了基本的了解,match查询的是散列映射,包含了我们希望搜索的字段和字符串。也就说,只要文档中只要有我们希望的那个关键字,但也因此带来了一些问题。
首先来创建一些示例

PUT t1/doc/1
{
  "title": "中国是世界上人口最多的国家"
}
PUT t1/doc/2
{
  "title": "美国是世界上军事实力最强大的国家"
}
PUT t1/doc/3
{
  "title": "北京是中国的首都"
}

现在,当我们以中国作为搜索条件,我们希望只返回和中国相关的文档。我们首先来使用match

查询:

GET t1/doc/_search
{
  "query": {
    "match": {
      "title": "中国"
    }
  }
}

结果

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 0.68324494,
    "hits" : [
      {
        "_index" : "t1",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 0.68324494,
        "_source" : {
          "title" : "中国是世界上人口最多的国家"
        }
      },
      {
        "_index" : "t1",
        "_type" : "doc",
        "_id" : "3",
        "_score" : 0.5753642,
        "_source" : {
          "title" : "北京是中国的首都"
        }
      },
      {
        "_index" : "t1",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 0.39556286,
        "_source" : {
          "title" : "美国是世界上军事实力最强大的国家"
        }
      }
    ]
  }
}

虽然如期的返回了中国的文档。但是却把和美国的文档也返回了,这并不是我们想要的。是怎么回事呢?因为这是elasticsearch在内部对文档做分词的时候,对于中文来说,就是一个字一个字分的,所以,我们搜中国都符合条件,返回,而美国的也符合。
而我们认为中国是个短语,是一个有具体含义的词。所以elasticsearch在处理中文分词方面比较弱势。后面会讲针对中文的插件。
但目前我们还有办法解决,那就是使用短语查询:

GET t1/doc/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "中国"
      }
    }
  }
}

 

这里match_phrase是在文档中搜索指定的词组,而中国则正是一个词组,所以愉快的返回了。
那么,现在我们要想搜索中国世界相关的文档,但又忘记其余部分了,怎么做呢?用match也不行,那就继续用match_phrase试试:

GET t1/doc/_search
{
  "query": {
    "match_phrase": {
      "title": "中国世界"
    }
  }
}

返回结果也是空的,因为没有中国世界这个短语。
我们搜索中国世界这两个指定词组时,但又不清楚两个词组之间有多少别的词间隔。那么在搜的时候就要留有一些余地。这时就要用到了slop了。相当于正则中的中国.*?世界。这个间隔默认为0,导致我们刚才没有搜到,现在我们指定一个间隔。

GET t1/doc/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "中国世界",
        "slop": 2
      }
    }
  }
}

现在,两个词组之间有了2个词的间隔,这个时候,就可以查询到结果了:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.7445889,
    "hits" : [
      {
        "_index" : "t1",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 0.7445889,
        "_source" : {
          "title" : "中国是世界上人口最多的国家"
        }
      }
    ]
  }
}

slop间隔你可以根据需要适当改动。

match系列之match_phrase_prefix(最左前缀查询)

现在凌晨2点半,单身狗小黑为了缓解寂寞,就准备搜索几个beautiful girl来陪伴自己。但是由于英语没过2级,但单词beautiful拼到bea就不知道往下怎么拼了。这个时候,我们的智能搜索要帮他啊,elasticsearch就看自己的词库有啥事bea开头的词,结果还真发现了两个:

PUT t3/doc/1
{
  "title": "maggie",
  "desc": "beautiful girl you are beautiful so"
}
PUT t3/doc/2
{
  "title": "sun and beach",
  "desc": "I like basking on the beach"
}

但这里用matchmatch_phrase都不太合适,因为小黑输入的不是完整的词。那怎么办呢?我们用match_phrase_prefix来搞:

GET t3/doc/_search
{
  "query": {
    "match_phrase_prefix": {
      "desc": "bea"
    }
  }
}

结果

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.39556286,
    "hits" : [
      {
        "_index" : "t3",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 0.39556286,
        "_source" : {
          "title" : "maggie",
          "desc" : "beautiful girl,you are beautiful so"
        }
      },
      {
        "_index" : "t3",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 0.2876821,
        "_source" : {
          "title" : "sun and beach",
          "desc" : "I like basking on the beach"
        }
      }
    ]
  }
}

前缀查询是短语查询类似,但前缀查询可以更进一步的搜索词组,只不过它是和词组中最后一个词条进行前缀匹配(如搜这样的you are bea)。应用也非常的广泛,比如搜索框的提示信息,当使用这种行为进行搜索时,最好通过max_expansions来设置最大的前缀扩展数量,因为产生的结果会是一个很大的集合,不加限制的话,影响查询性能。

GET t3/doc/_search
{
  "query": {
    "match_phrase_prefix": {
      "desc": {
        "query": "bea",
        "max_expansions": 1
      }
      
    }
  }
}

但是,如果此时你去尝试加上max_expansions测试后,你会发现并没有如你想想的一样,仅返回一条数据,而是返回了多条数据。
max_expansions执行的是搜索的编辑(Levenshtein)距离。那什么是编辑距离呢?编辑距离是一种计算两个字符串间的差异程度的字符串度量(string metric)。我们可以认为编辑距离就是从一个字符串修改到另一个字符串时,其中编辑单个字符(比如修改、插入、删除)所需要的最少次数。俄罗斯科学家Vladimir Levenshtein于1965年提出了这一概念。
我们再引用elasticsearch官网的一段话:该max_expansions设置定义了在停止搜索之前模糊查询将匹配的最大术语数,也可以对模糊查询的性能产生显着影响。但是,减少查询字词会产生负面影响,因为查询提前终止可能无法找到某些有效结果。重要的是要理解max_expansions查询限制在分片级别工作,这意味着即使设置为1,多个术语可能匹配,所有术语都来自不同的分片。此行为可能使其看起来好像max_expansions没有生效,因此请注意,计算返回的唯一术语不是确定是否有效的有效方法max_expansions。
我想你也没看懂这句话是啥意思,但我们只需知道该参数工作于分片层,也就是Lucene部分,超出我们的研究范围了。
我们快刀斩乱麻的记住,使用前缀查询会非常的影响性能,要对结果集进行限制,就加上这个参数。

match系列之multi_match(多字段查询)

现在,我们有一个50个字段的索引,我们要在多个字段中查询同一个关键字,该怎么做呢?

PUT t3/doc/1
{
  "title": "maggie is beautiful girl",
  "desc": "beautiful girl you are beautiful so"
}
PUT t3/doc/2
{
  "title": "beautiful beach",
  "desc": "I like basking on the beach,and you? beautiful girl"
}

我们先用原来的方法查询:

GET t3/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "beautiful"
          }
        },
        {
          "match": {
            "desc": "beautiful"
          }
        }
      ]
    }
  }
}

使用must来限制两个字段(值)中必须同时含有关键字。这样虽然能达到目的,但是当有很多的字段呢,我们可以用multi_match来做:

GET t3/doc/_search
{
  "query": {
    "multi_match": {
      "query": "beautiful",
      "fields": ["title", "desc"]
    }
  }
}

我们将多个字段放到fields列表中即可。以达到匹配多个字段的目的。
除此之外,multi_match甚至可以当做match_phrasematch_phrase_prefix使用,只需要指定type类型即可:

GET t3/doc/_search
{
  "query": {
    "multi_match": {
      "query": "gi",
      "fields": ["title"],
      "type": "phrase_prefix"
    }
  }
}
GET t3/doc/_search
{
  "query": {
    "multi_match": {
      "query": "girl",
      "fields": ["title"],
      "type": "phrase"
    }
  }
}

 

 

小结:

  • match:返回所有匹配的分词。
  • match_all:查询全部。
  • match_phrase:短语查询,在match的基础上进一步查询词组,可以指定slop分词间隔。
  • match_phrase_prefix:前缀查询,根据短语中最后一个词组做前缀匹配,可以应用于搜索提示,但注意和max_expanions搭配。其实默认是50.......
  • multi_match:多字段查询,使用相当的灵活,可以完成match_phrasematch_phrase_prefix的工作。

 

posted @ 2019-12-03 20:30  驰念  阅读(2446)  评论(0编辑  收藏  举报