ElasticSearch索引与搜索
发布日期:2021-05-08 23:30:58 浏览次数:18 分类:技术文章

本文共 12317 字,大约阅读时间需要 41 分钟。

在系列的第一篇文章中我们介绍了ElasticSearch的基本概念和操作,本文将继续介绍ElasticSearch查询和索引功能。

目录:

查询

结构化搜索将查询条件以json的形式包含在http请求的body中,通常情况下搜索请求应该使用GET方法但不是所有的客户端和服务端都支持GET请求包含body。 因此,ElasticSearch支持使用GET或POST进行搜索。

本节示例均使用默认analysismapping配置, 此状态下ElasticSearch与普通的文档数据库几乎无异。我们将在下一节中介绍如何进行模糊查询,使它成为真正的搜索引擎。

精确查询

精确查询通常起到过滤的作用,因为不使用分析器解析通常非常迅速。

查询可以使用from和size参数对结果进行分页:

{  "from": 0,  "size": 10,  "query": {    "term": {        "nickname": "easy"    }  }}

term查询

term查询用于进行精确匹配:

POST /blog/user/_search{  "query": {    "term": {        "nickname": "easy"    }  }}response:{    "took": 23,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "failed": 0    },    "hits": {        "total": 2,        "max_score": 0.45532417,        "hits": [            {                "_index": "blog",                "_type": "user",                "_id": "1",                "_score": 0.45532417,                "_source": {                    "nickname": "easy",                    "username": "user1",                    "status": "normal"                }            },            {                "_index": "blog",                "_type": "user",                "_id": "2",                "_score": 0.43648314,                "_source": {                    "nickname": "easy",                    "username": "user2"                    "status": "normal"                }            }        ]    }}

在响应的hits.hits字段中我们可以看到匹配的文档,文档_score字段是采用TF-IDF算法得出匹配程度得分,结果集中的文档按照得分降序排列。

上述查询可以用sql表示为:

SELECT * FROM userWHERE nickname = "easy";

terms查询

terms查询可以视为多个term查询的组合:

POST /blog/user/_search{  "query": {    "terms": {      "nickname": ["easy", "ease"]    }  }}response:{    "took": 18,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "failed": 0    },    "hits": {        "total": 2,        "max_score": 1.0970675,        "hits": [            {                "_index": "blog",                "_type": "user",                "_id": "2",                "_score": 1.5779335,                "_source": {                    "nickname": "ease",                    "status": "normal"                }            },            {                "_index": "blog",                "_type": "user",                "_id": "4",                "_score": 1.0970675,                "_source": {                    "nickname": "easy",                    "content": "simple",                    "status": "normal"                }            }        ]    }}

range查询

range查询用于数值等类型进行过滤,比如示例中过滤粉丝数大于等于2的用户:

GET /user/naive/_search{    "query": {        "range": {            "followerCount": {                 "gte": 2            },            "registerTime": {                "gt": "2017-01-01 00:00:00",                "lt": "2018-01-01 00:00:00"             }        }    }}

示例中的range查询可以用SQL描述为:

SELECT * FROM user_naiveWHERE followerCount >= 2     AND unix_timestamp(registerTime) > unix_timestamp('2017-01-01 00:00:00')    AND unix_timestamp(registerTime) < unix_timestamp('2018-01-01 00:00:00');

可用的范围表达式有:

  • lt: 小于(less than)
  • lte: 小于等于(less than / equals)
  • gt: 大于(greater than)
  • gte: 大于等于(greater than / equals)

可以进行范围查询的字段:

  • 数值
  • 日期
  • 字符串: 按照词典顺序排列

全文查询

用户输入字符串通常无法进行精确查询,全文查询可以使用分析器解析查询字符串然后根据相关度筛选查询结果。

match查询

match查询全文字段(类型为analyzed)时,会使用分析器将查询字符串解析成词条列表,然后进行匹配。

查询字符串"easy simple"会被standard分析器解析为[easy, simple], 这个查询等价于:

{    "terms": {        "nickname": ["easy", "simple"]    }}
POST /blog/user/_search{  "query": {    "match": {        "nickname": "easy simple"    }  }}response:{    "took": 19,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "failed": 0    },    "hits": {        "total": 3,        "max_score": 1.1382749,        "hits": [            {                "_index": "blog",                "_type": "user",                "_id": "3",                "_score": 1.1382749,                "_source": {                    "nickname": "simple",                    "status": "normal"                }            },            {                "_index": "blog",                "_type": "user",                "_id": "1",                "_score": 1.049597,                "_source": {                    "nickname": "easy",                    "status": "normal"                }            }        ]    }}

若以eas sim为关键词进行term查询不会匹配到任何文档。

multi_match查询

multi_match用于对多个字段进行同一个查询:

{  "query": {    "multi_match": {        "analyzer": "keyword",        "query": "easy",        "fields": [            "nickname.ngram",            "username.ngram"        ]    }  }}

上述查询等同于对nickname.ngramusername.ngram两个字段进行match查询,对结果进行综合排序。

排序方式有:

  • best_fields
  • most_fields
  • cross_fields

script查询

script查询可以使用脚本描述查询条件,进行复杂的查询。

{  "query": {      "script": {        "script": {          "source": "doc['count'].value > params.limit",          "lang": "painless",          "params": {              "limit": 1          }        }      }}

我们通常使用ElasticSearch提供的编写脚本,也可以使用Groovy等脚本语言。

组合查询

组合查询用于将多个查询组合起来,以表达更复杂的查询条件或排序规则。

组合查询都是可以自由嵌套的,如我们可以bool查询中使用另一个bool查询或dis_max作为must子查询。

bool查询

Bool查询用于组合多个条件查询相关度最高的文档, 下面展示了一个Bool查询请求:

POST /user/naive/_search{  "query": {    "bool": {      "must": {          "match": {            "nickname": "easy"          }      },      "must_not": {        "match": {          "nickname": "hard"        }      },      "should": {        "match": {          "nickname": "simple"        }      },      "filter": [        {          "term": {            "status": "normal"          }        }      ]    }  }}response:{    "took": 20,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "failed": 0    },    "hits": {        "total": 3,        "max_score": 1.3847495,        "hits": [            {                "_index": "blog",                "_type": "user",                "_id": "4",                "_score": 1.3847495,                "_source": {                    "nickname": "easy",                    "content": "simple",                    "status": "normal"                }            },            {                "_index": "blog",                "_type": "user",                "_id": "5",                "_score": 0.45532417,                "_source": {                    "nickname": "easy",                    "content": "bitter",                    "status": "normal"                }            }        ]    }}

上述bool查询的目标为:

  • must条件必须满足, 即nickname字段必须与词条easy匹配,字段的匹配程度会影响得分
  • must_not条件必须不满足, 即nickname字段不能与词条hard匹配
  • should条件不做要求, 但满足should条件的文档会获得更高的相关度评分_score。 当should查询中包含多个字段时, 会将各字段得分的和作为总分。所以查询到两个nickname与easy匹配的文档,但是contentsimple的字段获得了更高的评分。
  • filter条件必须满足,但是匹配程度不会影响得分。

filter子查询一般使用精确查询,因为过滤器不进行相关度计算且使用, 其执行速度非常快。

dis_max查询

上文已经提到bool查询的should查询会将各字段得分之和作为总分,然而在实际应用中通常一个字段高度匹配的文档可能比拥有多个字段低匹配更符合用户的期望。

dismax查询同样用于组合多个查询,但是按照匹配程度最高的字段确定总分:

{  "query": {    "dis_max": {      "queries": [      	{      		"match": {        		"nickname": "easy"        	}        },      	{      		"match": {        		"content": "easy"        	}        }      ]    }  }}

function_score 查询

function_score可以更加自由地控制评分过程:

{  "query": {    "function_score": {      "filter": {         "term": { "uid": 1 }      },      "functions": [         {          "filter": { "term": { "features": "a" }},           "weight": 1        },        {          "filter": { "term": { "features": "b" }},           "weight": 2         }      ],      "score_mode": "sum",     }  }}

一致性随机

function_score可以对结果进行随机排序:

{  "query": {    "random_score": {      "seed": 2    },    "bost_mode": "replace",    "query": {      "dis_max": {        "queries": [          {            "multi_match": {              "analyzer": "keyword",              "query": "finley",              "boost": 1.2,              "fields": [                "username.ngram"              ]            }          },          {            "multi_match": {              "analyzer": "keyword",              "query": "finley",              "boost": 1,              "fields": [                "nickname.ngram"              ]            }          }        ]      }    }  }}

只要随机种子random_score.seed不变结果的排序就不会变化,这种方式被称为一致性随机评分。

一致性随机评分机制可以为每个用户生成一个独有的排序,并支持翻页浏览。

排序

ElasticSearch的搜索结果默认按照_score进行降序排列,_score是根据TF-IDF算法得出文档与查询调价匹配度。

在一些情况下我们希望自定义排序方式, 比如按创建时间排列。

POST /blog/user/_search{    "query" : {        "term" : {             "uid" : 1         }    },    "sort": {         "date": {             "order": "desc"         }    }}

script 排序

{    "query" : {        "term" : {             "uid" : 1         }    },    "sort": {         "script_score": {             "source": "_score / (params.current - doc['create_at'].value)"            "params": {                "current": 1534606168            }         }    }}

索引管理

ElasticSearch中的Index是最高级的逻辑的结构, 类似于MySQL中的数据库(schema),可以配置独立的搜索策略和存储策略。

ElasticSearch通过倒排索引进行搜索,所谓倒排索引是指对文档进行分析提取关键词,然后建立关键词到文档的索引,当我们搜索关键词时就可以找到它所关联的文档。

我们可以通过在Index中配置分析器和映射来设置倒排索引的策略。

分析器是通用的从文档提取关键词的方法,即将文档中某个字段映射为关键字的方法。例如:过滤停用词,分词,添加同义词等。

映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词。

分析器

这个拆分的过程即是分析的过程, 分析执行的操作包括不限于: 字符过滤, 分词, 停用词过滤, 添加同义词.

ES提供了很多内置分析器:

  • standard: 默认分析器, 根据Unicode定义删除标点并将词条小写
  • simple: 根据空格分词并将词条小写
  • whitespace: 根据空格分词但不将词条小写
  • english: 过滤英文停用词并将词条还原为词根, 详情参考.
  • : 滑动窗口分析器,取文本中所有子串作为关键词。 比如对easy进行处理可以得到关键词:e, a, s, y, ea, as, sy, eas, asy, easy
  • edge-ngram: 边缘固定滑动窗口分析器,取文本所有从头开始的子串作为关键词。 比如对easy进行处理可以得到关键词:e, ea, eas, easy。常用于联想搜索,根据用户输入前几个字符进行搜索。

此外, 也可以通过配置字符过滤器(char_filter), 词过滤器(filter), 分词器(tokenizer)的方式来自定义分析器。

这里展示基于ngram的分析器定义:

PUT /blog{	"settings": {		"analysis": {			"filter": {				"grams_filter": {					"type": "ngram",					"min_gram": 1,					"max_gram": 5				}				},            "analyzer": {                "gram_analyzer": {                    "type": "custom",                    "tokenizer": "standard",                    "filter": [                        "lowercase",                        "grams_filter"                    ]                }            }		}	}	},	mappings: {...}}

自定义分析器的更多信息可以查阅官方文档:

类型和映射

映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词:

PUT /blog{  "settings": { ... },  "mappings": {		"user": {			"properties": {				"nickname": {					"type": "string",					"analyzer": "gram_analyzer",					"fields": {						"keyword:": {							"type": "keyword"						}					}				},				"status": {					"type": "text",					"fields": {						"keyword:": {							"type": "keyword"						}					}				}			}		}	}}

上述JSON中user项定义了一个根对象, 它通常与文档对应。根对象下可以包含下列几个字段:

  • properties: 定义文档中可能包含字段和它们的映射
  • : 控制如何动态处理新字段
  • 其它设置项

文档中每一个字段都有3个配置项:

  • type: 指定字段类型, 如:text, long, doubledate.
  • index: 指定字段索引的类型:
    • no: 不可被搜索
    • not_analyzed: 必须精确匹配
    • analyzed: 使用分析器建立倒排索引
  • analyzer: 该字段使用的默认分析器
  • fields: 字段的属性, 可以配置独立的type, index和analyzer。用于将一个字段映射为不同类型适应不同用途。

管理索引

在分析器及映射两节中展示了创建索引所需的PUT请求片段,将一节中PUT请求的settings字段, 用一节中的settings字段替换即可得到完整创建索引请求。

发送DELETE请求可以删除索引:

  • DELETE /user: 删除user索引
  • DELET /user1,user2: 删除user1和user2两个suoyin
  • DELETE /user*: 根据通配符删除索引
  • DELET /_all, DELETE /*: 删除所有索引

GET /_cat/indices可以列出ElasticSearch上的所有索引。

GET /blog?pretty列出索引blog的所有信息。

中文搜索

若需要进行中文搜索我们需要使用中文分析器,它的核心是中文分词器(tokenizer)。

这里我们使用插件进行中文搜索。

PUT /post/Post{    "settings": {        "analysis": {            "analyzer": {                "cn_smart": {                    "type": "custom",                    "tokenizer": "ik_smart",                    "filter": [                        {                            "type": "length",                            "min": "3"                        }                    ]                },                "max_word": {                    "type": "custom",                    "tokenizer": "ik_max_word",                    "filter": [                        {                            "type": "length",                            "min": "3"                        }                    ]                }            }        }    },    "mappings": {		"user": {			"properties": {				"content": {					"type": "text",                    "analyzer": "cn_smart",                    "fields": {                        "max_word": {                            "type": "text",                            "analyzer": "max_word"                        }                    }                }            }        }    }}

使用match进行查询:

{    "query" : {         "match" : {             "content" : "搜索"         }    }}
{    "query" : {         "match" : {             "content.max_word" : "搜索"         }    }}

ik_smart会进行标准的中文分词, ik_max_word则会试图穷尽所有分词

上一篇:Shell与脚本
下一篇:Docker快速指南

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月26日 21时52分07秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

2025版最新大模型学习路线,零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新大模型开发流程(非常详细)零基础入门到精通,收藏这一篇就够了 2025-03-30
(干货)数据分析案例--以上海二手房为例 2025-03-30
(大部分安卓手机通用)一加OnePlus Ace3扬声器优化教程 外放直接媲美苹果 2025-03-30
2025版最新大模型微调方法(非常详细)零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新大模型算法岗位薪资指南,零基础入门到精通,收藏这一篇就够了 2025-03-30
2025版最新大语言模型的指令微调,零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新小白学习大模型:什么是大模型?零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新常用黑客工具之【Nmap 教程基础】零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新开发一款大模型需要经过哪些步骤?开发一款大模型的完整流程,收藏这篇就够了 2025-03-30
$.inArray函数判断数组中的是否包含字符串 2025-03-30
2025版最新渗透测试和黑客工具列表,零基础入门到精通,收藏这一篇就够了 2025-03-30
2025版最新网络安全入门书籍整理大全,零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新网络安全知识入门及学习流程(非常详细)零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新网络安全等级保护测评指南,零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新运维怎么转行网络安全?零基础入门到精通,收藏这篇就够了 2025-03-30
2025版最新黑客学习网站(非常详细),零基础入门到精通,看这一篇就够了 2025-03-30
2025版网络工程11个高含金量证书(非常详细)零基础入门到精通,收藏这篇就够了 2025-03-30
2025自学成为黑客必读的5本书籍,带你从小白进阶成大佬 2025-03-30
20万高薪专业-网络安全(非常详细)零基础入门到精通,收藏这一篇就够了 2025-03-30