-
结构化搜索:非是即否的搜索,要么存于集合之中,要么存在集合之外。结构化查询不关心文件的相关度或评分;只是对文档包括或排除处理。
-
bitset 过滤器会创建一个 bitset (一个包含 0 和 1 的数组),匹配文档的标志位是 1 。例如:
[1,0,0,0]
bitset 可以复用 任何 已使用过的相同过滤器,而无需再次计算整个过滤器。
这些 bitsets 缓存是“智能”的:它们以增量方式更新。当我们索引新文档时,只需将那些新文档加入已有 bitset,而不是对整个缓存一遍又一遍的重复计算。
==这里要搞清楚缓存的是什么==,缓存是类似这种语句的查询
{ "term": { "folder": "inbox" }}
Elasticsearch 会基于使用频次自动缓存查询。如果一个非评分查询在最近的 256 词查询中被使用过(次数取决于查询类型),那么这个查询就会作为缓存的候选。但是,并不是所有的片段都能保证缓存 bitset 。只有那些文档数量超过 10,000 (或超过总文档数量的 3% )才会缓存 bitset 。因为小的片段可以很快的进行搜索和合并,这里缓存的意义不大。
一旦缓存了,非评分计算的 bitset 会一直驻留在缓存中直到它被剔除。剔除规则是基于 LRU 的:一旦缓存满了,最近最少使用的过滤器会被剔除。
-
全文搜索 怎样在全文字段中搜索到最相关的文档。全文搜索两个最重要的方面是:
相关性(Relevance)
和分析(Analysis)
一个简单的搜索
搜索姓氏为Smith的员工
GET /employee/doc/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
},
"from": 0,
"size": 2
}
from
显示应该跳过的初始结果数量,默认是0。
size
显示应该返回的结果数,默认是10。
查询语句的结构
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
最重要的查询
- match_all 匹配所有文档
{"match_all": {}}
- match 可用的标准查询,
基于全文的查询
如果在一个精确值字段上上使用它, 1 假如数字、日期、布尔或者一个 keyword 字符串字段,那么它将会精确匹配给定的值;2 如果要查询一个( analyzed )已分析的全文字段,它们会先将++查询字符串++传递到一个合适的分析器,然后生成一个供查询的词项列表。
match 查询还可以接受 operator 操作符作为输入参数,默认情况下该操作符是 or。
{
"query": {
"match": {
"title": {
"query": "BROWN DOG!",
"operator": "and"
}
}
}
}
match 查询支持 minimum_should_match 最小匹配参数, 可以指定必须匹配的词项数用来表示一个文档是否相关。可以将其设置为具体数字或一个百分数。参数 minimum_should_match 的设置非常灵活,可以根据用户输入词项的数目应用不同的规则。具体看这里
{
"query": {
"match": {
"title": {
"query": "quick brown dog",
"minimum_should_match": "75%"
}
}
}
}
- multi_match 可以在多个字段上执行相同的 match 查询。字段名称可以用模糊匹配的方式给出,例如{"fields": "*_title"}。
multi_match 多匹配查询的类型有多种,其中的三种: best_fields(默认) 、 most_fields 和 cross_fields (最佳字段、多数字段、跨字段)。
{
"multi_match": {
"query": "full text search",
"type": "best_fields",
"fields": [ "title", "body" ]
}
}
- range 找出那些落在指定区间内的数字或者时间。可用操作符如下: gt, gte, lt, lte
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
当使用它处理日期字段时, range 查询支持对 日期计算(date math) 进行操作,比方说,如果我们想查找时间戳在过去一小时内的所有文档:
"range" : {
"timestamp" : {
"gt" : "now-1h"
}
}
还可以被应用到某个具体的时间,并非只能是一个像 now 这样的占位符。只要在某个日期后加上一个双管符号 (||) 并紧跟一个日期数学表达式就能做到:
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-01 00:00:00||+1M"
}
}
详细的日期格式参考https://www.elastic.co/guide/en/elasticsearch/reference/6.1/mapping-date-format.html
- term 被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者keyword的字符串。它与match的区别是:term查询对于输入的文本不分析,查询那些精确匹配的值(包括在大小写、重音、空格等方面的差异),所以它将给定的值进行精确查询。
- terms 允许指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件。
一定要了解 term 和 terms 是 包含(contains) 操作,而非 等值(equals) (判断)。
- exists & missing exists 查询和 missing 查询被用于查找那些指定字段中有值 (exists) 或无值 (missing) 的文档。相当于SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 。
null, [] (空数组)和 [null] 所有这些都是等价的,它们无法存于倒排索引中。
将多条件组合查询
可以用 bool
查询来将多查询组合在一起,成为想要的布尔查询,它接收以下参数:
- must 文档 必须 匹配这些条件才能被包含进来。
- must_not 文档 必须不 匹配这些条件才能被包含进来。
- should 如果满足这些语句中的任意语句,将增加
_score
,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
所有 must 语句必须匹配,所有 must_not 语句都必须不匹配,但有多少 should 语句应该匹配呢? 默认情况下,没有 should 语句是必须匹配的,只有一个例外:那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。
- filter 必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
下面是一个组合查询的示例, must,must_not,should的参数可以接收一个数组或一个对象(当只有一个条件时)。
这个查询的意思是查找除名字是john外的姓氏为Smith,年龄大于30的员工,如果员工的兴趣里有music项,这个员工所在的文档的评分会更高。
GET /employee/doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"last_name": "smith"
}
}
],
"must_not": {
"match": {
"first_name": "john"
}
},
"should": [
{
"match": {
"interests": "music"
}
}
],
"filter": {
"range": {
"age": {
"gt": 30
}
}
}
}
}
}
排序与相关性
默认情况下,返回的结果是按照 相关性_score 进行排序的——最相关的文档排在最前。
- 指定字段行排序
{
...
"sort":{
"age":{
"order": "desc"
}
}
}
字段排序默认为升序排序,所以如果是按字段升序排序的话,可以用下面的方式简单的指定一个字段
"sort": "age"
- 多字段排序,可以按如下语法执行
{
...
"sort": [
{
"age": {
"order": "asc"
}
},
{
"last_name.keyword": {
"order": "desc"
}
}
]
}
排序条件的顺序是很重要的。结果首先按第一个条件排序,仅当结果集的第一个 sort 值完全相同时才会按照第二个条件进行排序,以此类推。
- 多值字段排序
一种情形是字段有多个值的排序, 需要记住这些值并没有固有的顺序;一个多值的字段仅仅是多个值的包装,这时应该选择哪个进行排序呢?
对于数字或日期或keyword,你可以将多值字段减为单值,这可以通过使用 min 、 max 、 avg 或是 sum 排序模式 。
{
...
"sort": [
{
"age": {
"order": "asc"
}
},
{
"interests.keyword": {
"order": "asc",
"mode": "max"
}
}
]
}
scroll游标查询
可以用来有效地执行大批量的文档查询,而又不用付出深度分页那种代价。 类似传统数据库中的cursor。 游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。
启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。
GET /old_index/_search?scroll=1m
{
"query": { "match_all": {}},
"sort" : ["_doc"],
"size": 1000
}
这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串 ((("scroll_id"))) 。 现在我们能传递字段 _scroll_id 到 _search/scroll 查询接口获取下一批结果:
GET /_search/scroll
{
"scroll": "1m",
"scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
}
尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。 当查询的时候, 字段 size作用于单个分片,所以每个批次实际返回的文档数量最大为
size * number_of_primary_shards
。
使用过滤器
GET /employee/doc/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"last_name.keyword": "Smith"
}
},
"boost": 1.2
}
},
"sort": [
{
"age": {
"order": "asc"
}
},
{
"interests.keyword": {
"order": "asc",
"mode": "max"
}
}
]
}
几种查询策略
<span id="best_fields"></span>
- 最佳字段
当搜索词语具体概念的时候,比如 “brown fox” ,词组比各自独立的单词更有意义。像 title 和 body 这样的字段,尽管它们之间是相关的,但同时又彼此相互竞争。文档在 相同字段 中包含的词越多越好,评分也来自于 最匹配字段 。 <span id="most_fields"></span>
- 多数字段
为了对相关度进行微调,常用的一个技术就是将相同的数据索引到不同的字段,它们各自具有独立的分析链。
主字段可能包括它们的词源、同义词以及 变音词 或口音词,被用来匹配尽可能多的文档。
相同的文本被索引到其他字段,以提供更精确的匹配。一个字段可以包括未经词干提取过的原词,另一个字段包括其他词源、口音,还有一个字段可以提供 词语相似性 信息的瓦片词(shingles)。
其他字段是作为匹配每个文档时提高相关度评分的 信号 , 匹配字段越多 则越好。
<span id="cross_fields"></span>
- 混合字段
对于某些实体,我们需要在多个字段中确定其信息,单个字段都只能作为整体的一部分:
Person: first_name 和 last_name (人:名和姓) Book: title 、 author 和 description (书:标题、作者、描述) Address: street 、 city 、 country 和 postcode (地址:街道、市、国家和邮政编码) 在这种情况下,我们希望在 任何 这些列出的字段中找到尽可能多的词,这有如在一个大字段中进行搜索,这个大字段包括了所有列出的字段。
短语匹配match_phrase
- slop参数
- 邻近度
- 结果集重新评分 rescore
- shingles: unigram bigram trigram four-gram five-gram ...
shingles 不仅比短语查询更灵活, 而且性能也更好。
其他几种查询
- prefix 前缀匹配
- wildcard 通配符
- regexp 正则
- fuzzy 模糊
- ids 通过给定文档id匹配
边界n-grams(edge n-grams)与自动完成
所谓的边界 n-gram 是相对于shingles来说,它会固定词语开始的一边,以单词 quick 为例,它的边界 n-gram 的结果为:
- q
- qu
- qui
- quic
- quick
在索引时配置token过滤器和分析器
PUT /my_index
{
"settings": {
"number_of_shards": 1,
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
}
}
然后将分析器应用到指定字段,注意此时分别指定了索引时和查询时的分析器
PUT /my_index/_mapping/my_type
{
"my_type": {
"properties": {
"name": {
"type": "string",
"index_analyzer": "autocomplete",
"search_analyzer": "standard"
}
}
}
}
更多内容查看https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-suggesters-completion.html