小故事
恭喜宿主完成全文搜索前置任务 - 《学习简单 CURD》。系统解锁全文搜索功能。
"好家伙,让我瞧瞧牛逼的全文搜索功能", 陈皮唤起系统,自言自语道。
GET /知识百科/_search
{
"query": {
"match": {
"keyword": "染色工艺"
}
}
}
陈皮一顿操作,输入了牛皮的代码后。系统界面弹出染色工艺的相关资料。
"小公牛哭小母牛——牛逼死了,系统。等下把配方给老父亲看看"
"爹!爹!爹! 我有办法可以解决衣服的染色问题了。"
陈皮把从系统抄录的内容,拿给了陈平看。
陈平看了看纸上的内容,又看看了陈皮。
"嗯嗯嗯,这个解决方法确实巧妙。儿子,昏睡了几天,脑袋竟然开窍了?",陈平拍拍了拍陈皮的肩膀,哈哈哈大笑起来。
"好好好,我的儿子。为父就去试试新的配方,你刚醒不久,好好照顾自己的身体"
"叮咚,恭喜宿主帮助陈家解决染色工艺问题,获得 1 点系统积分。当前积分 1"
"系统系统,这积分能干啥哈?"
"积分能够允许宿主查询高级内容、招聘猛将"
"嗯嗯嗯,系统有这么高级的能力。复兴陈家有望,说不准还可以一统天下。难道我转生后,能当上帝王吗?"陈皮浮想连连。
"不过系统,说实话的,刚才我就搜索个 '染色工艺' 怎么头发染色相关的内容也有啊?我有办法就只搜染色工艺相关的内容吗?"
"叮咚,恭喜宿主触发关键词。颁发任务 《学习 term 查询》"
"我勒个xxx,这也行!!!"
未完待续......
前置
上篇章,介绍了 ES 基本的增删改查。本篇章,将介绍 ES 的全文搜索(full text query),term query 会放到下一章节介绍。
Intervals query
被搜索的关键字,要按照指定的顺序出现在文档中,该文档才能被检索。
例如。
PUT /test/_doc/1
{
"name": "do you like search? yes. i am. i like java"
}
GET /test/_search
{
"query": {
"intervals": {
"name": {
"all_of": {
"ordered": true,
"intervals": [
{ "match": { "query": "am" }},
{ "match": { "query": "i" }},
{ "match": { "query": "java" }}
]
}
}
}
}
}
该查询的意思为,文档中需要有对应的term, am i java,且必须按照 am > i > java 的顺序出现。
match query
先对搜索词进行分词,然后再搜索。
例如
PUT /test/_doc/2
{
"name": "PHP is the best language in the world"
}
PUT /test/_doc/3
{
"name": "hello world! elasticsearch!"
}
GET /test/_search
{
"query": {
"match": {
"name": "php"
}
}
}
心细如你的一定可以发现,输入的 PHP 是大写。但小写也可搜索到。这部分内容和分词有关系,后续会补充,这里先忽略。
Match boolean prefix query
前缀匹配。将搜索词分词,最后一个词,被用作前缀匹配。
GET /test/_search
{
"query": {
"match_bool_prefix": {
"name": {
"query": "search wor",
"operator": "or"
}
}
}
}
search wor,被分词为 search, wor。
要求文档中出现 search 或者 有关键字是以 wor 开头。
operator 还有 and 选项。即文档中必须有 search 关键字, 且有关键字以 wor 开头。
Match phrase query
短语搜索。将搜索词分词,而后根据 slop 定义的词与词的间距,判断词与词之间最多能出现多少单词
GET /test/_search
{
"query": {
"match_phrase": {
"name": {
"query": "hello elasticsearch",
"slop": 2
}
}
}
}
hello elasticsearch 被分词为 hello, elasticsearch。
slop=2 表示匹配的文档, hello, elasticsearch 之间至多只能间隔 2 个词。
Match phrase prefix query
短语搜索和前缀搜索的结合体。
GET /test/_search
{
"query": {
"match_phrase_prefix": {
"name": {
"query": "you sear",
"slop": 2
}
}
}
}
you sear 被分词为 you, sear。
slop=2 表示匹配的文档, you, sear 之间至多只能间隔 2 个词。并且只要以 sear 开头即满足条件。
Combined fields
组合查询。需要注意的是,被搜索的字段,需要有相同的分词器。
PUT /test2/_doc/1
{
"name": "elasticsearch i love it",
"title": "java is very good"
}
PUT /test2/_doc/2
{
"name": "php i love it",
"title": "mysql is very good"
}
PUT /test2/_doc/3
{
"name": "redis i love it",
"title": "rocketMQ is very good"
}
GET /test2/_search
{
"query": {
"combined_fields" : {
"query": "java redis",
"fields": [ "name", "title"],
"operator": "or"
}
}
}
将 java redis 分词为 java、redis, 并在 name、title 中查找。name 或者 title 中有出现,文档就算匹配。
Multi-match query
多字段查询。
GET /test2/_search
{
"query": {
"multi_match": {
"query": "mysql java",
"fields": ["name", "title"]
}
}
}
将 mysql java 分词为 mysql, java。分别在 name, title 中查询,如果 name 或者 title 中有匹配,文档就算匹配。
评分公式为:max(field_scores) * boost
type
type 会影响查询的行为和结果
best_fields
默认 type 为 best_fields。
假设:
| 字段 | 文档 ID | mysql 得分 | java 得分 | 最终得分 |
|---|---|---|---|---|
| name | 1 | 0 | 3 | 0 + 3 |
| title | 1 | 7 | 0 | 7 + 0 |
文档 1 最终得分 max(3, 7) = 7 |
most_fields
GET /test2/_search
{
"query": {
"multi_match": {
"query": "mysql java",
"fields": ["name", "title"],
"type": "most_fields"
}
}
}
与 best_fields 的区别为:算分公式为,sum(field_scores * boost)
| 字段 | 文档 ID | mysql 得分 | java 得分 | 最终得分 |
|---|---|---|---|---|
| name | 1 | 0 | 10 | 0 + 10 |
| title | 1 | 10 | 0 | 10 + 0 |
文档 1 最终得分 10 + 10 |
cross_fields
GET /test2/_search
{
"query": {
"multi_match": {
"query": "php java",
"fields": ["name", "title"],
"type": "cross_fields"
}
}
}
算分公式为, sum(max( term_in_field_scores * boost))。即 每个分词(term) 在不同 field 中得分最大值之和,即为该文档的得分。
与 most_fields, best_fields 主要区别在于,cross_fields 算分是以 term(分词) 为纬度,后两者是以字段为维度。
| 分词(term) | 文档 ID | name 中的得分 | title 中的得分 | 最终得分 |
|---|---|---|---|---|
| php | 1 | 12 | 10 | 12(取最大的) |
| java | 1 | 3 | 10 | 10(取最大的) |
文档 1 最终得分 (12) + (10) |
phrase, phrase_prefix
与 Match boolean prefix query、Match phrase query 类似。只不过是在多个字段中搜索,不过多介绍
tie_breaker
tie_breaker 是一个 0-1 的值。默认为 0.
当设置了 tie_breaker 后,算分公式会变为。
final_score = max_score + tie_breaker * sum_of_other_scores
即,tie_breaker 值越大,其他字段的得分对总得分影响越大。
我们以 cross_fields 举例说明。
GET /test2/_search
{
"query": {
"multi_match": {
"query": "php java",
"fields": ["name", "title"],
"type": "cross_fields"
}
}
}
| 分词(term) | 文档 ID | name 中的得分 | title 中的得分 | 最终得分 | tie_breaker = 0.3 时的得分 |
|---|---|---|---|---|---|
| php | 1 | 12 | 10 | 12(取最大的) | 12 + 10 * 0.3 |
| java | 1 | 3 | 10 | 10(取最大的) | 10 + 3 * 0.3 |
文档 1 最终得分 (12 + 10 * 0.3) + (10 + 3 * 0.3) |
operator
默认为 or。
例如
GET /test2/_search
{
"query": {
"multi_match": {
"query": "mysql java",
"fields": ["name", "title"],
"operator": "or"
}
}
}
表示,(mysql, java) in name or (mysql, java) in title。
即只要有 1个 term 存在于 field 就算匹配。
还有另外一个值 and。
例如
GET /test2/_search
{
"query": {
"multi_match": {
"query": "mysql java",
"fields": ["name", "title"],
"operator": "and"
}
}
}
表示,((mysql) in name and (java) in name) OR ((mysql) in title and (java) in title)。
即 terms 必须同时出现在 field 才算匹配。
boost
我们可以为每个字段单独设置权重。 例如
GET /test2/_search
{
"query": {
"multi_match": {
"query": "mysql java",
"fields": ["name^2", "title^3"],
"operator": "and"
}
}
}
name^2, 表示,在对 name 字段进行算法时,需要将得分 * 2。
{
"query": {
"intervals": {
"name": {
"all_of": {
"ordered": true,
"intervals": [
{ "match": { "query": "am" }},
{ "match": { "query": "i" }},
{ "match": { "query": "java" }}
]
}
}
}
}
}