因为elasticsearch 里默认的IK分词器是会将每一个中文都进行了分词的切割,所以你直接想查一整个词,或者一整句话是无返回结果的
设置了not_analyzed后,搜索的时候就不行了(因为没有进行分词,所以理解为精确查找)
如果没有设置"index":"not_analyzed" ,也可以采用下面的查询方式
短语匹配(Phrase Matching)
就像用于全文搜索的的match查询一样,当你希望寻找邻近的单词时,match_phrase查询可以帮你达到目的。
GET /my_index/my_type/_search { "query": { "match_phrase": { "title": "quick brown fox" } } }
和match查询类似,match_phrase查询首先解析查询字符串来产生一个词条列表。然后会搜索所有的词条,但只保留包含了所有搜索词条的文档,并且词条的位置要邻接。一个针对短语quick fox的查询不会匹配我们的任何文档,因为没有文档含有邻接在一起的quick和fox词条。
match_phrase查询也可以写成类型为phrase的match查询:
"match": { "title": { "query": "quick brown fox", "type": "phrase" } }
词条位置
当一个字符串被分析时,分析器不仅只返回一个词条列表,它同时也返回原始字符串的每个词条的位置、或者顺序信息:
GET /_analyze?analyzer=standard Quick brown fox
返回如下:
-
{
-
"tokens": [
-
{
-
"token": "quick",
-
"start_offset": 0,
-
"end_offset": 5,
-
"type": "
<ALPHANUM>",
-
"position": 1
-
},
-
{
-
"token": "brown",
-
"start_offset": 6,
-
"end_offset": 11,
-
"type": "
<ALPHANUM>",
-
"position": 2
-
},
-
{
-
"token": "fox",
-
"start_offset": 12,
-
"end_offset": 15,
-
"type": "
<ALPHANUM>",
-
"position": 3
-
}
-
]
-
}
位置信息可以被保存在倒排索引(Inverted Index)中,像match_phrase这样位置感知(Position-aware)的查询能够使用位置信息来匹配那些含有正确单词出现顺序的文档,且在这些单词之间没有插入别的单词。
短语是什么
对于匹配了短语"quick brown fox"的文档,下面的条件必须为true:
- quick、brown和fox必须全部出现在某个字段中。
- brown的位置必须比quick的位置大1。
- fox的位置必须比quick的位置大2。
如果以上的任何一个条件没有被满足,那么文档就不能被匹配。
在内部,match_phrase查询使用了低级的span查询族(Query Family)来执行位置感知的查询。span查询是词条级别的查询,因此它们没有解析阶段(Analysis Phase);它们直接搜索精确的词条。
幸运的是,大多数用户几乎不需要直接使用span查询,因为match_phrase查询通常已经够好了。但是,对于某些特别的字段,比如专利搜索(Patent Search),会使用这些低级查询来执行拥有非常特别构造的位置搜索。
以上参考:https://www.elastic.co/guide/en/elasticsearch/guide/current/phrase-matching.html#phrase-matching
混合起来(Mixing it up)
精确短语(Exact-phrase)匹配也许太过于严格了。也许我们希望含有"quick brown fox"的文档也能够匹配"quick fox"查询,即使位置并不是完全相等的。
我们可以在短语匹配使用slop参数来引入一些灵活性:
GET /my_index/my_type/_search { "query": { "match_phrase": { "title": { "query": "quick fox", "slop": 1 } } } }
slop参数告诉match_phrase查询词条能够相隔多远时仍然将文档视为匹配。相隔多远的意思是,你需要移动一个词条多少次来让查询和文档匹配?
我们以一个简单的例子来阐述这个概念。为了让查询quick fox能够匹配含有quick brown fox的文档,我们需要slop的值为1:
-
Pos 1 Pos 2 Pos 3
-
-----------------------------------------------
-
Doc: quick brown fox
-
-----------------------------------------------
-
Query: quick fox
-
Slop 1: quick ↳ fox
尽管在使用了slop的短语匹配中,所有的单词都需要出现,但是单词的出现顺序可以不同。如果slop的值足够大,那么单词的顺序可以是任意的。
为了让fox quick查询能够匹配我们的文档,需要slop的值为3:
-
Pos 1 Pos 2 Pos 3
-
-----------------------------------------------
-
Doc: quick brown fox
-
-----------------------------------------------
-
Query: fox quick
-
Slop 1: fox|quick ↵
-
Slop 2: quick ↳ fox
-
Slop 3: quick ↳ fox
[以上参考:](https://www.elastic.co/guide/en/elasticsearch/guide/current/slop.html)
elasticsearch 查询(match和term)
es中的查询请求有两种方式,一种是简易版的查询,另外一种是使用JSON完整的请求体,叫做结构化查询(DSL)。
由于DSL查询更为直观也更为简易,所以大都使用这种方式。
DSL查询是POST过去一个json,由于post的请求是json格式的,所以存在很多灵活性,也有很多形式。
这里有一个地方注意的是官方文档里面给的例子的json结构只是一部分,并不是可以直接黏贴复制进去使用的。一般要在外面加个query为key的机构。
match
最简单的一个match例子:
查询和"我的宝马多少马力"这个查询语句匹配的文档。
-
{
-
"query": {
-
"match": {
-
"content" : {
-
"query" :
"我的宝马多少马力"
-
}
-
}
-
}
-
}
上面的查询匹配就会进行分词,比如"宝马多少马力"会被分词为"宝马 多少 马力", 所有有关"宝马 多少 马力", 那么所有包含这三个词中的一个或多个的文档就会被搜索出来。
并且根据lucene的评分机制(TF/IDF)来进行评分。
match_phrase
比如上面一个例子,一个文档"我的保时捷马力不错"也会被搜索出来,那么想要精确匹配所有同时包含"宝马 多少 马力"的文档怎么做?就要使用 match_phrase 了
-
{
-
"query": {
-
"match_phrase": {
-
"content" : {
-
"query" :
"我的宝马多少马力"
-
}
-
}
-
}
-
}
完全匹配可能比较严,我们会希望有个可调节因子,少匹配一个也满足,那就需要使用到slop。
-
{
-
"query": {
-
"match_phrase": {
-
"content" : {
-
"query" :
"我的宝马多少马力",
-
"slop" :
1
-
}
-
}
-
}
-
}
multi_match
如果我们希望两个字段进行匹配,其中一个字段有这个文档就满足的话,使用multi_match
-
{
-
"query": {
-
"multi_match": {
-
"query" :
"我的宝马多少马力",
-
"fields" : [
"title",
"content"]
-
}
-
}
-
}
但是multi_match就涉及到匹配评分的问题了。
我们希望完全匹配的文档占的评分比较高,则需要使用best_fields
-
{
-
"query": {
-
"multi_match": {
-
"query":
"我的宝马发动机多少",
-
"type":
"best_fields",
-
"fields": [
-
"tag",
-
"content"
-
],
-
"tie_breaker":
0.3
-
}
-
}
-
}
意思就是完全匹配"宝马 发动机"的文档评分会比较靠前,如果只匹配宝马的文档评分乘以0.3的系数
我们希望越多字段匹配的文档评分越高,就要使用most_fields
-
{
-
"query": {
-
"multi_match": {
-
"query":
"我的宝马发动机多少",
-
"type":
"most_fields",
-
"fields": [
-
"tag",
-
"content"
-
]
-
}
-
}
-
}
我们会希望这个词条的分词词汇是分配到不同字段中的,那么就使用cross_fields
-
{
-
"query": {
-
"multi_match": {
-
"query":
"我的宝马发动机多少",
-
"type":
"cross_fields",
-
"fields": [
-
"tag",
-
"content"
-
]
-
}
-
}
-
}
term
term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
-
{
-
"query": {
-
"term": {
-
"content":
"汽车保养"
-
}
-
}
-
}
查出的所有文档都包含"汽车保养"这个词组的词汇。
使用term要确定的是这个字段是否“被分析”(analyzed),默认的字符串是被分析的。
拿官网上的例子举例:
mapping是这样的:
-
PUT my_index
-
{
-
"mappings": {
-
"my_type": {
-
"properties": {
-
"full_text": {
-
"type":
"string"
-
},
-
"exact_value": {
-
"type":
"string",
-
"index":
"not_analyzed"
-
}
-
}
-
}
-
}
-
}
-
-
PUT my_index/my_type/
1
-
{
-
"full_text":
"Quick Foxes!",
-
"exact_value":
"Quick Foxes!"
-
}
其中的full_text是被分析过的,所以full_text的索引中存的就是[quick, foxes],而extra_value中存的是[Quick Foxes!]。
那下面的几个请求:
-
GET my_index/my_type/_search
-
{
-
"query": {
-
"term": {
-
"exact_value":
"Quick Foxes!"
-
}
-
}
-
}
请求的出数据,因为完全匹配
-
GET my_index/my_type/_search
-
{
-
"query": {
-
"term": {
-
"full_text":
"Quick Foxes!"
-
}
-
}
-
}
请求不出数据的,因为full_text分词后的结果中没有[Quick Foxes!]这个分词。
bool联合查询: must,should,must_not
如果我们想要请求"content中带宝马,但是tag中不带宝马"这样类似的需求,就需要用到bool联合查询。
联合查询就会使用到must,should,must_not三种关键词。
这三个可以这么理解
- must: 文档必须完全匹配条件
- should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should
- must_not: 文档必须不匹配条件
比如上面那个需求:
-
{
-
"query": {
-
"bool": {
-
"must": {
-
"term": {
-
"content":
"宝马"
-
}
-
},
-
"must_not": {
-
"term": {
-
"tags":
"宝马"
-
}
-
}
-
}
-
}
-
}