ElasticSearch DSL 领域专用语言

博文目录


ElasticSearch DSL(Domain Specific Language) 领域专用语言

ES提供了基于JSON的DSL来定义查询。DSL由叶子查询子句和复合查询子句两种子句组成。

DSL查询语言中存在两种:查询DSL(query DSL)和过滤DSL(filter DSL)

Query DSL

在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高么?”

如何验证匹配很好理解,如何计算相关度呢?ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
在这里插入图片描述

Filter DSL

在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”

答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。

过滤上下文是在使用filter参数时候的执行环境,比如在bool查询中使用must_not或者filter

另外,经常使用过滤器,ES会自动的缓存过滤器的内容,这对于查询来说,会提高很多性能。

区别

queriesfilters
relevanceboolean yes/no
full textexact values
not cachedcached
slowerfaster

Filter first, than query remaining docs

记录查询

无查询条件

无查询条件是查询所有,默认是查询所有的,或者使用match_all表示所有

GET /es/_search

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

有查询条件

叶子查询(单条件)

模糊匹配

模糊匹配主要是针对text类型的字段,text类型的字段在入库时会对内容进行分词并索引,查询时也会对搜索条件进行分词,然后通过倒排索引查找到匹配的数据。模糊匹配主要通过match等参数来实现

  • match : match指定的搜索条件会做分词,使用其结果去查找倒排索引来匹配数据
  • prefix : 前缀匹配
  • regexp : 正则表达式匹配
match
// 模糊查询address字段, match会根据该字段的分词器,进行分词查询. match要求被查询字段类型为text
//  SQL: select * from user where address like '%广州%' limit 0, 2
POST /es/_search
{
	"from": 0,
	"size": 2, 
	"query": {
		"match": {
			"address": "广州"
		}
	}
}

match条件还支持以下参数:

  • query : 在match下使用子节点query来指定搜索条件,可省略query子节点直接在match后面指定搜索条件
  • operator : 搜索条件被分词后,用来指定每个分词被用来匹配时的关系
    • and : 某个文档(或其中的某个字段)的内容需要匹配所有的分词
    • or : 某个文档(或其中的某个字段)的内容至少匹配一个分词
  • minmum_should_match : 某个文档(或其中的某个字段)的内容需匹配分词的数量,可以是整数,也可以是百分比,如查询条件被分词后是3个词,可以指定至少有两个词匹配,也可以指定至少有67%的词匹配
// 下述搜索中,如果document中的remark字段包含java或developer词组,都符合搜索条件
GET /es/_search
{
	"query": {
		"match": {
			"remark": "java developer"
		}
	}
}
// 如果需要搜索的document中的remark字段,同时包含java和developer词组,则需要使用下述语法
// 如果将operator的值改为or。则与第一个案例搜索语法效果一致。默认的ES执行搜索的时候,operator就是or
GET /es/_search
{
	"query": {
		"match": {
			"remark": {
				"query": "java developer",
				"operator": "and"
			}
		}
	}
}
// 如果在搜索的结果document中,需要remark字段中包含多个搜索词条中的一定比例,可以使用下述语法实现搜索。
// 其中minimum_should_match可以使用百分比或固定数字。百分比代表query搜索条件中词条百分比,如果无法整除,向下匹配(如,query条件有3个单词,如果使用百分比提供精准度计算,那么是无法除尽的,如果需要至少匹配两个单词,则需要用67%来进行描述。如果使用66%描述,ES则认为匹配一个单词即可)。固定数字代表query搜索条件中的词条,至少需要匹配多少个
GET /es/_search
{
	"query": {
		"match": {
			"remark": {
				"query": "java architect assistant",
				"minimum_should_match": "68%"
			}
		}
	}
}
// 如果使用should+bool搜索的话,也可以控制搜索条件的匹配度。具体如下
// 下述案例代表搜索的document中的remark字段中,必须匹配java、developer、assistant三个词条中的至少2个
GET /es/_search
{
	"query": {
		"bool": {
			"should": [
				{ 
					"match": {
						"remark": "java"
					} 
				},
				{
					"match": {
						"remark": "developer"
					}
				},
				{
					"match": {
						"remark": "assistant"
					}
				}
			],
			"minimum_should_match": 2
		}
	}
}
match 的底层转换

其实在ES中,执行match搜索的时候,ES底层通常都会对搜索条件进行底层转换,来实现最终的搜索结果。如

GET /es/_search
{
	"query": {
		"match": {
			"remark": "java developer"
		}
	}
}

// 转换后是:
GET /es/_search
{
	"query": {
		"bool": {
			"should": [
				{
					"term": {
						"remark": "java"
					}
				},
				{
					"term": {
						"remark": {
							"value": "developer"
						}
					}
				}
			]
		}
	}
}
GET /es/_search
{
	"query": {
		"match": {
			"remark": {
				"query": "java developer",
				"operator": "and"
			}
		}
	}
}

// 转换后是:
GET /es/_search
{
	"query": {
		"bool": {
			"must": [
				{
					"term": {
						"remark": "java"
					}
				},
				{
					"term": {
						"remark": {
							"value": "developer"
						}
					}
				}
			]
		}
	}
}
GET /es/_search
{
	"query": {
		"match": {
			"remark": {
				"query": "java architect assistant",
				"minimum_should_match": "68%"
			}
		}
	}
}

// 转换后为:
GET /es/_search
{
	"query": {
		"bool": {
			"should": [
				{
					"term": {
						"remark": "java"
					}
				},
				{
					"term": {
						"remark": "architect"
					}
				},
				{
					"term": {
						"remark": "assistant"
					}
				}
			],
			"minimum_should_match": 2
		}
	}
}

如果不怕麻烦,尽量使用转换后的语法执行搜索,效率更高。

如果开发周期短,工作量大,使用简化的写法。

multi_match
// 多字段模糊匹配查询 multi_match
// SQL: select * from student where name like '%张三%' or address like '%张三%' 	
POST /es/_search
{
	"query":{
		"multi_match":{
			"query":"张三",
			"fields":["address","name"]
		}
	}
}
match_phrase

短语搜索。就是搜索条件不分词。代表搜索条件不可分割。

如果hello world是一个不可分割的短语,我们可以使用前文学过的短语搜索match phrase来实现。语法如下:

GET _search
{
	"query": {
		"match_phrase": {
			"remark": "java assistant"
		}
	}
}
match phrase 原理 - term position

ES是如何实现match phrase短语搜索的?其实在ES中,使用match phrase做搜索的时候,也是和match类似,首先对搜索条件进行分词-analyze。将搜索条件拆分成hello和world。既然是分词后再搜索,ES是如何实现短语搜索的?

这里涉及到了倒排索引的建立过程。在倒排索引建立的时候,ES会先对document数据进行分词,如

GET _analyze
{
	"text": "hello world, java spark",
	"analyzer": "standard"
}

分词的结果是

{
"tokens": [
    {
      "token": "hello",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "world",
      "start_offset": 6,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "java",
      "start_offset": 13,
      "end_offset": 17,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "spark",
      "start_offset": 18,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 3
    }
  ]
}

从上述结果中,可以看到。ES在做分词的时候,除了将数据切分外,还会保留一个position。position代表的是这个词在整个数据中的下标。当ES执行match phrase搜索的时候,首先将搜索条件hello world分词为hello和world。然后在倒排索引中检索数据,如果hello和world都在某个document的某个field出现时,那么检查这两个匹配到的单词的position是否是连续的,如果是连续的,代表匹配成功,如果是不连续的,则匹配失败。

match phrase搜索参数 - slop

在做搜索操作的是,如果搜索参数是hello spark。而ES中存储的数据是hello world, java spark。那么使用match phrase则无法搜索到。在这个时候,可以使用match来解决这个问题。但是,当我们需要在搜索的结果中,做一个特殊的要求:hello和spark两个单词距离越近,document在结果集合中排序越靠前,这个时候再使用match则未必能得到想要的结果。

ES的搜索中,对match phrase提供了参数slop。slop代表match phrase短语搜索的时候,单词最多移动多少次,可以实现数据匹配。在所有匹配结果中,多个单词距离越近,相关度评分越高,排序越靠前。

这种使用slop参数的match phrase搜索,就称为近似匹配(proximity search)

如:
数据为: hello world, java spark
搜索为: match phrase : hello spark。
slop为: 3 (代表单词最多移动3次。)
执行短语搜索的时候,将条件hello spark分词为hello和spark两个单词。并且连续。
hello spark
接下来,可以根据slop参数执行单词的移动。
下标 : 0 1 2 3
doc : hello world java spark
搜索 : hello spark
移动1:hello ------- spark
移动2:hello ------- ------- spark
匹配成功,不需要移动第三次即可匹配。

如果:
数据为: hello world, java spark
搜索为: match phrase : spark hello。
slop为: 5 (代表单词最多移动5次。)
执行短语搜索的时候,将条件hello spark分词为hello和spark两个单词。并且连续。
spark hello
接下来,可以根据slop参数执行单词的移动。
下标 : 0 1 2 3
doc : hello world java spark
搜索 :spark hello
移动1:spark/hello
移动2:hello spark
移动3:hello ------- spark
移动4:hello ------- ------- spark
匹配成功,不需要移动第五次即可匹配。

如果当slop移动次数使用完毕,还没有匹配成功,则无搜索结果。如果使用中文分词,则移动次数更加复杂,因为中文词语有重叠情况,很难计算具体次数,需要多次尝试才行。

GET _analyze
{
	"text": "hello world, java spark",
	"analyzer": "standard"
}

POST /test_a/_doc/3
{
	"f" : "hello world, java spark"
}

GET /test_a/_search
{
	"query": {
		"match_phrase": {
			"f" : {
				"query": "hello spark",
				"slop" : 2
			}
		}
	}
}

GET /test_a/_search
{
	"query": {
		"match_phrase": {
			"f" : {
				"query": "spark hello",
				"slop" : 4
			}
		}
	}
}
GET _analyze
{
	"text": "中国,一个世界上最强的国家",
	"analyzer": "ik_max_word"
}

POST /test_a/_doc/1
{
	"f" : "中国,一个世界上最强的国家"
}

GET /test_a/_search
{
	"query": {
		"match_phrase": {
			"f" : {
				"query": "中国最强",
				"slop" : 5
			}
		}
	}
}

GET /test_a/_search
{
	"query": {
		"match_phrase": {
			"f" : {
				"query": "最强中国",
				"slop" : 9
			}
		}
	}
}
query_string
// 未指定字段条件查询 query_string , 含 AND 与 OR 条件
POST /es/_search
{
	"query":{
		"query_string":{
			"query":"广州 OR 长沙"
		}
	}
}

// 指定字段条件查询 query_string , 含 AND 与 OR 条件
POST /es/_search
{
	"query":{
		"query_string":{
			"query":"admin OR 长沙",
			"fields":["name","address"]
		}
	}
}
前缀搜索 prefix

使用前缀匹配实现搜索能力。通常针对keyword类型字段,也就是不分词的字段。

// 查询所有name字段以Java开头的文档(name是keyword字段)
GET /es/_search
{
    "query": {
        "prefix": {
            "name": "Java"
        }
    }
}

// 查询所有name字段以Java开头的文档(name不是keyword字段,这种方式在聚合统计中也会使用到)
GET /es/_search
{
    "query": {
        "prefix": {
            "name.keyword": {
				"value": "Java"
			}
        }
    }
}

针对前缀搜索,是对keyword类型字段而言。而keyword类型字段数据大小写敏感。

前缀搜索效率比较低,会扫描所有倒排索引,性能较差。前缀搜索不会计算相关度分数。前缀越短,效率越低。如果使用前缀搜索,建议使用长前缀。因为前缀搜索需要扫描完整的索引内容,所以前缀越长,相对效率越高。

通配符搜索 wildcard

ES中也有通配符。但是和java还有数据库不太一样。通配符可以在倒排索引中使用,也可以在keyword类型字段中使用。

常用通配符:
?: 一个任意字符
*: 0~n个任意字符

GET /test_a/_search
{
	"query": {
		"wildcard": {
			"f.keyword": {
				"value": "?e*o*"
			}
		}
	}
}

性能也很低,也是需要扫描完整的索引。不推荐使用。

正则搜索

ES支持正则表达式。可以在倒排索引或keyword类型字段中使用。

常用符号:
[]: 范围,如: [0-9]是0~9的范围数字
.: 一个字符
+: 前面的表达式可以出现多次

GET /test_a/_search
{
	"query": {
		"regexp" : {
			"f.keyword" : "[A-z].+"
		}
	}
}

性能也很低,需要扫描完整索引。

搜索推荐

搜索推荐: search as your type, 搜索提示。如:索引中有若干数据以“hello”开头,那么在输入hello的时候,推荐相关信息。(类似百度输入框)

GET /test_a/_search
{
	"query": {
		"match_phrase_prefix": {
			"f": {
				"query": "java s",
				"slop" : 10,
				"max_expansions": 10
			}
		}
	}
}

其原理和match phrase类似,是先使用match匹配term数据(java),然后在指定的slop移动次数范围内,前缀匹配(s),max_expansions是用于指定prefix最多匹配多少个term(单词),超过这个数量就不再匹配了。
这种语法的限制是,只有最后一个term会执行前缀搜索。
执行性能很差,毕竟最后一个term是需要扫描所有符合slop要求的倒排索引的term。
因为效率较低,如果必须使用,则一定要使用参数max_expansions。

模糊搜索技术 fuzzy

搜索的时候,可能搜索条件文本输入错误,如:hello world 写成了 hello word。这种拼写错误还是很常见的。fuzzy技术就是用于解决错误拼写的(在英文中很有效,在中文中几乎无效。)。其中fuzziness代表value的值word可以修改多少个字母来进行拼写错误的纠正(修改字母的数量包含字母变更,增加或减少字母。)。f代表要搜索的字段名称。

GET /test_a/_search
{
	"query": {
		"fuzzy": {
			"f" : {
				"value" : "word",
				"fuzziness": 2
			}
		}
	}
}
总结
  • match:模糊匹配,需要指定字段名,但是输入的值会进行分词,比如"hello world"会进行拆分为hello和world,然后匹配,如果字段中包含hello或者world,或者都包含的结果都会被查询出来,也就是说match是一个部分匹配的模糊查询。查询条件相对来说比较宽松。
  • term: 这种查询和match在有些时候是等价的,比如我们查询单个的词hello,那么会和match查询结果一样,但是如果查询"hello world",结果就相差很大,因为这个输入不会进行分词,就是说查询的时候,是查询字段分词结果中是否有"hello world"的字样,而不是查询字段中包含"hello world"的字样。当保存数据"hello world"时,elasticsearch会对字段内容进行分词,“hello world"会被分成hello和world,不存在"hello world”,因此这里的查询结果会为空。这也是term查询和match的区别。
  • match_phase:会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样。以"hello world"为例,要求结果中必须包含hello和world,而且还要求他们是连着的,顺序也是固定的,hello that world不满足,world hello也不满足条件。
  • query_string:和match类似,但是match需要指定字段名,query_string是在所有字段中搜索,范围更广泛。
精确匹配
  • term : 单个条件相等, 不会做分词
  • terms : 单个字段属于某个值数组内的值
  • range : 字段属于某个范围内的值
  • exists : 某个字段的值是否存在
  • ids : 通过ID批量查询
term
// 精确查询name字段, term查询不会对字段进行分词查询,会采用精确匹配. term要求被查询字段类型为keyword
// SQL: select * from student where name = 'admin'
POST /es/_search
{
	"query": {
		"term": {
			"name": "admin"
		}
	}
}
range
// 范围查询
注:json请求字符串中部分字段的含义
	range:范围关键字
	gte 大于等于
	lte  小于等于
	gt 大于
	lt 小于
	now 当前时间	
// SQL: select * from user where age between 25 and 28
POST /es/_search
{
	"query" : {
		"range" : {
			"age" : {
				"gte":25,
				"lte":28
			}
		}
	}
}
// 分页、输出字段、排序综合查询
POST /es/_search
{
	"query" : {
		"range" : {
			"age" : {
				"gte":25,
				"lte":28
			}
		}
	},
	"from": 0,
	"size": 2,
	"_source": ["name", "age", "book"],
	"sort": {"age":"desc"}
}
keyword/text 与 term/match
  • term查询text: text字段入库时会被分词并索引, term的值不会被分词, 该值与分词中的任意一个匹配即可搜到
  • term查询keyword: keyword字段入库时不会被分词, 但会被索引, term的值不会被分词, 所以两个值必须完全匹配才能被搜到
  • match查询text: text字段入库时会被分词并索引, match的值会被分词, 两组分词中任意一个互相匹配即可搜到
  • match查询keyword: keyword字段入库时不会被分词, 但会被索引, match的值会被分词, 分词中的任意一个与字段值匹配即可搜到, 同时match的值和keyword的值完全匹配时也可以搜到

组合查询(多条件)

组合条件查询是将叶子条件查询语句进行组合而形成的一个完整的查询条件

  • bool : 各条件之间有and,or或not的关系
    • must : 各个条件都必须满足,即各条件是and的关系
    • should : 各个条件有一个满足即可,即各条件是or的关系
    • must_not : 不满足所有条件,即各条件是not的关系
    • filter : 不计算相关度评分,它不计算_score即相关度评分,效率更高
  • constant_score : 不计算相关度评分

must/filter/shoud/must_not 等的子条件是通过 term/terms/range/ids/exists/match 等叶子条件为参数的
注:以上参数,当只有一个搜索条件时,must等对应的是一个对象,当是多个条件时,对应的是一个数组

bool
// Filter过滤器方式查询,它的查询不会计算相关性分值,也不会对结果进行排序, 因此效率会高一点,查询的结果可以被缓存。会缓存
POST /es/_search
{
	"query" : {
		"bool" : {
			"filter" : {
				"term":{
					"age":25
				}
			},
			"must": {
			  "term": {
			    "sex": 1
			  }
			},
			"must_not": {
			  "term": {
			    "age": 26
			  }
			},
			"should": [
			  {
			    "term": {
  			    "age": 25
  			  }
			  },
			  {
			    "term": {
  			    "age": 26
  			  }
			  }
			]
		}
	}
}
dis_max
基于dis_max实现best fields策略进行多字段搜索

best fields策略:搜索的document中的某一个field,尽可能多的匹配搜索条件。
most fields策略:与best fields策略相反,尽可能多的字段匹配到搜索条件。如百度搜索使用这种策略。

优点:精确匹配的数据可以尽可能的排列在最前端,且可以通过minimum_should_match来去除长尾数据,避免长尾数据字段对排序结果的影响。

长尾数据:比如说我们搜索4个关键词,但很多文档只匹配1个,也显示出来了,这些文档其实不是我们想要的

缺点:相对排序不均匀。

dis_max语法: 直接获取搜索的多条件中的,单条件query相关度分数最高的数据,以这个数据做相关度排序。

下述的案例中,就是找name字段中rod匹配相关度分数或remark字段中java developer匹配相关度分数,哪个高,就使用哪一个相关度分数进行结果排序。

GET /es/_search
{
	"query": {
		"dis_max": {
			"queries": [
				{
					"match": {
						"name": "rod"
					}
				},
				{
					"match": {
						"remark": "java developer"
					}
				}
			]
		}
	}
}
基于tie_breaker参数优化dis_max搜索效果

dis_max是将多个搜索query条件中相关度分数最高的用于结果排序,忽略其他query分数,在某些情况下,可能还需要其他query条件中的相关度介入最终的结果排序,这个时候可以使用tie_breaker参数来优化dis_max搜索。

tie_breaker参数代表的含义是:将其他query搜索条件的相关度分数乘以参数值,再参与到结果排序中。如果不定义此参数,相当于参数值为0。所以其他query条件的相关度分数被忽略。

GET /es/_search
{
	"query": {
		"dis_max": {
			"queries": [
				{
					"match": {
						"name": "rod"
					}
				},
				{
					"match": {
						"remark": "java developer"
					}
				}
			],
			"tie_breaker":0.5
		}
	}
}
使用multi_match简化dis_max+tie_breaker

ES中相同结果的搜索也可以使用不同的语法语句来实现。不需要特别关注,只要能够实现搜索,就是完成任务

GET /es/_search
{
	"query": {
		"dis_max": {
			"queries": [
				{
					"match": {
						"name": "rod"
					}
				},
				{
					"match": {
						"remark": {
							"query": "java developer",
							"boost" : 2,
							"minimum_should_match": 2
						}
					}
				}
			],
			"tie_breaker": 0.5
		}
	}
}

// 使用multi_match语法为:其中type常用的有best_fields和most_fields。^n代表权重,相当于"boost":n。
GET /es/_search
{
	"query": {
		"multi_match": {
			"query": "rod java developer",
			"fields": ["name", "remark^2"],
			"type": "best_fields",
			"tie_breaker": 0.5,
			"minimum_should_match" : "50%"
		}
	}
}
cross fields 搜索

cross fields : 一个唯一的标识,分部在多个fields中,使用这种唯一标识搜索数据就称为cross fields搜索。如:人名可以分为姓和名,地址可以分为省、市、区县、街道等。那么使用人名或地址来搜索document,就称为cross fields搜索。
实现这种搜索,一般都是使用most fields搜索策略。因为这就不是一个field的问题。

Cross fields搜索策略,是从多个字段中搜索条件数据。默认情况下,和most fields搜索的逻辑是一致的,计算相关度分数是和best fields策略一致的。一般来说,如果使用cross fields搜索策略,那么都会携带一个额外的参数operator。用来标记搜索条件如何在多个字段中匹配。

当然,在ES中也有cross fields搜索策略。具体语法如下

GET /es/_search
{
	"query": {
		"multi_match": {
			"query": "java developer",
			"fields": ["name", "remark"],
			"type": "cross_fields",
			"operator" : "and"
		}
	}
}

上述语法代表的是,搜索条件中的java必须在name或remark字段中匹配,developer也必须在name或remark字段中匹配。

most field策略问题:most fields策略是尽可能匹配更多的字段,所以会导致精确搜索结果排序问题。又因为cross fields搜索,不能使用minimum_should_match来去除长尾数据。

所以在使用most fields和cross fields策略搜索数据的时候,都有不同的缺陷。所以商业项目开发中,都推荐使用best fields策略实现搜索。

连接查询(多文档合并查询)

  • 父子文档查询:parent/child
  • 嵌套文档查询: nested

boost 权重控制

搜索document中remark字段中包含java的数据,如果remark中包含developer或architect,则包含architect的document优先显示。(就是将architect数据匹配时的相关度分数增加)。

一般用于搜索时相关度排序使用。如:电商中的综合排序。将一个商品的销量,广告投放,评价值,库存,单价比较综合排序。在上述的排序元素中,广告投放权重最高,库存权重最低。

GET /es/_search
{
	"query": {
		"bool": {
			"must": [
				{
					"match": {
						"remark": "java"
					}
				}
			],
			"should": [
				{
					"match": {
						"remark": {
							"query": "developer",
							"boost" : 1
						}
					}
				},
				{
					"match": {
						"remark": {
							"query": "architect",
							"boost" : 3
						}
					}
				}
			]
		}
	}
}

copy_to 组合fields

京东中,如果在搜索框中输入“手机”,点击搜索,那么是在商品的类型名称、商品的名称、商品的卖点、商品的描述等字段中,哪一个字段内进行数据的匹配?如果使用某一个字段做搜索不合适,那么使用_all做搜索是否合适?也不合适,因为_all字段中可能包含图片,价格等字段。

假设,有一个字段,其中的内容包括(但不限于):商品类型名称、商品名称、商品卖点等字段的数据内容。是否可以在这个特殊的字段上进行数据搜索匹配?

{
  "category_name" : "手机",
  "product_name" : "一加6T手机",
  "price" : 568800,
  "sell_point" : "国产最好的Android手机",
  "tags": ["8G+128G", "256G可扩展"],
  "color" : "红色",
  "keyword" : "手机 一加6T手机 国产最好的Android手机"
}

copy_to : 就是将多个字段,复制到一个字段中,实现一个多字段组合。copy_to可以解决cross fields搜索问题,在商业项目中,也用于解决搜索条件默认字段问题。

如果需要使用copy_to语法,则需要在定义index的时候,手工指定mapping映射策略。

copy_to语法:

PUT /es/_mapping
{
	"properties": {
		"provice" : {
			"type": "text",
			"analyzer": "standard",
			"copy_to": "address"
		},
		"city" : {
			"type": "text",
			"analyzer": "standard",
			"copy_to": "address"
		},
		"street" : {
			"type": "text",
			"analyzer": "standard",
			"copy_to": "address"
		},
		"address" : {
			"type": "text",
			"analyzer": "standard"
		}
	}
}

上述的mapping定义中,是新增了4个字段,分别是provice、city、street、address,其中provice、city、street三个字段的值,会自动复制到address字段中,实现一个字段的组合。那么在搜索地址的时候,就可以在address字段中做条件匹配,从而避免most fields策略导致的问题。在维护数据的时候,不需对address字段特殊的维护。因为address字段是一个组合字段,是由ES自动维护的。类似java代码中的推导属性。在存储的时候,未必存在,但是在逻辑上是一定存在的,因为address是由3个物理存在的属性province、city、street组成的。

使用match和proximity search实现召回率和精准度平衡。

召回率:召回率就是搜索结果比率,如:索引A中有100个document,搜索时返回多少个document,就是召回率(recall)。

精准度:就是搜索结果的准确率,如:搜索条件为hello java,在搜索结果中尽可能让短语匹配和hello java离的近的结果排序靠前,就是精准度(precision)。

如果在搜索的时候,只使用match phrase语法,会导致召回率底下,因为搜索结果中必须包含短语(包括proximity search)。

如果在搜索的时候,只使用match语法,会导致精准度底下,因为搜索结果排序是根据相关度分数算法计算得到。

那么如果需要在结果中兼顾召回率和精准度的时候,就需要将match和proximity search混合使用,来得到搜索结果。

POST /test_a/_doc/3
{
	"f" : "hello, java is very good, spark is also very good"
}

POST /test_a/_doc/4
{
	"f" : "java and spark, development language "
}

POST /test_a/_doc/5
{
	"f" : "Java Spark is a fast and general-purpose cluster computing system. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs."
}

POST /test_a/_doc/6
{
	"f" : "java spark and, development language "
}

GET /test_a/_search
{
	"query": {
		"match": {
			"f": "java spark"
		}
	}
}

GET /test_a/_search
{
	"query": {
		"bool": {
			"must": [
				{
					"match": {
						"f": "java spark"
					}
				}
			],
			"should": [
				{
					"match_phrase": {
						"f": {
							"query": "java spark",
							"slop" : 50
						}
					}
				}
			]
		}
	}
}

结果聚合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值