ElasticSearch 2 (13)
来源:互联网 发布:985 211 知乎 编辑:程序博客网 时间:2024/05/29 11:01
原文:http://www.cnblogs.com/richaaaard/p/5241461.html
摘要
结构化查询指的是查询那些具有内在结构的数据,比如日期、时间、数字都是结构化的。它们都有精确的格式,我们可以对这些数据进行逻辑操作,比较常见的操作包括比较时间区间,或者获取两个数字间的较大值。
文本也可以是结构化的。比如彩笔可以有红、绿、蓝颜色集合,一个博客可以有关键字标签 分布式 和 搜索 。 电商网站上的商品都有UPC(Universal Product Codes)或者其他需要严格结构化格式的唯一标识。
在结构化查询中,我们得到的结果通常是 是 或 非 ,要么是处于集合中的,要么是集合之外的。结构化查询通常不需要操心文件之间的相关性或者准确的相关程度,对于结果来说,要么 包含 ,要么 排除 。
这在逻辑上是说得通的,对于一个数字来说,我们不能说它比其他数字更适合在某一集合中。确切的结果只能是:要么在范围中 ,要么反之。同理,对于一个结构化文本,一个值要么相等,要么不等。在结构化查询中,没有更相似 这种概念
版本
elasticsearch版本: elasticsearch-2.x
内容
精确查询
当进行精确查询时,过滤器filter是十分重要的,因为它们效率非常高,过滤器不计算相关性(直接跳过了整个记分阶段)而且很容易进行缓存。我们会在本片文章的后面介绍缓存为filter带来的性能优势。现在需要记住的只是:尽可能多的使用filter。
过滤数字
我们首先看 term filter,它最常用,可以用来处理数字,布尔值,日期和文本。
例如我们有一些产品:
POST /my_store/products/_bulk{ "index": { "_id": 1 }}{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }{ "index": { "_id": 2 }}{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }{ "index": { "_id": 3 }}{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }{ "index": { "_id": 4 }}{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }
我们想要做的是要查询具有某个价格的所有产品,如果对于SQL熟悉,那么它的表达式是:
SELECT documentFROM productsWHERE price = 20
在ElasticSearch查询DSL里,我们使用 term 达到相同的目的:
{ "term" : { "price" : 20 }}
但是在ElasticSearch里,term 不能单独使用,search API期望的是一个 query 而不是 filter,所以,我们需要把 term 放在一个filter query里进行使用:
GET /my_store/products/_search{ "query" : { "filtered" : { #1 "query" : { "match_all" : {} #2 }, "filter" : { "term" : { #3 "price" : 20 } } } }}
- #1 filtered 查询同时接受一个 query 和 filter
- #2 match_all 会返回所有匹配的文件,这是个默认行为
- #3 term 过滤我们之前说到的,需要注意的是这里 term块 是处于 filter 之内的
执行结果正如我们期望一样,它只会返回文档2,这里我们称为命中hit。
"hits" : [ { "_index" : "my_store", "_type" : "products", "_id" : "2", "_score" : 1.0, #1 "_source" : { "price" : 20, "productID" : "KDKE-B-9947-#kL5" } }]
- \1 之前我们说到filter不会进行记分或相关性计算,这里的分数来自于我们查询时使用的关键字 match_all ,它会同等对待所有的文件,并对所有的结果都给以1的记分。
过滤文本
term 同样可以用来过滤文本,如果我们想要查询某个具体UPC id的产品,SQL语句会是下面这样:
SELECT productFROM productsWHERE productID = "XHDK-A-1293-#fJ3"
转换成DSL,同样使用 term 来查询:
GET /my_store/products/_search{ "query" : { "filtered" : { "filter" : { "term" : { "productID" : "XHDK-A-1293-#fJ3" } } } }}
但这里有个小问题,我们没有如预期得到想要的结果!为什么呢?问题并不出在 term 查询上,问题出在数据索引的方式。如果使用 analyze API(Test Analyzers),我们可以看到这里的UPC码以及被拆分成多个小的token:
GET /my_store/_analyze?field=productIDXHDK-A-1293-#fJ3
结果
{ "tokens" : [ { "token" : "xhdk", "start_offset" : 0, "end_offset" : 4, "type" : "<ALPHANUM>", "position" : 1 }, { "token" : "a", "start_offset" : 5, "end_offset" : 6, "type" : "<ALPHANUM>", "position" : 2 }, { "token" : "1293", "start_offset" : 7, "end_offset" : 11, "type" : "<NUM>", "position" : 3 }, { "token" : "fj3", "start_offset" : 13, "end_offset" : 16, "type" : "<ALPHANUM>", "position" : 4 } ]}
这里有些几点需要注意的:
- 这个UPC我们有4个不同的token而不是1个
- 所有token的字母都变成了小写
- 我们都掉了短横线(-)和哈希符(#)
所以,当我们用 term 去过滤值 XHDK-A-1293-#fJ3 的时候,找不到任何文件,因为这个token不在我们的反向索引(inverted index)之中,正如上面呈现的,索引里面有4个token。
显然,这种对于id码或其他任何精确值的处理方式不是我们想要的。
为了避免这种问题,我们需要告诉ElasticSearch这个字段具有精确值,需要被设置成 not_analyzed 。 我们可以在定制化字段mapping中找到相关内容。为了修正这个问题,我们需要首先删除老的index,然后再创建一个新的
DELETE /my_store #1PUT /my_store #2{ "mappings" : { "products" : { "properties" : { "productID" : { "type" : "string", "index" : "not_analyzed" #3 } } } }}
- #1 删除索引是必须的,因为我们不能更新已存在的mapping(Immutable)。
- #2 在索引被删除后,我们可以创建自定义的mapping。
- #3 我们在这里告诉ElasticSearch,我们不想对 productID 做任何分析。
然后我们就可以对文件重索引了:
POST /my_store/products/_bulk{ "index": { "_id": 1 }}{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }{ "index": { "_id": 2 }}{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }{ "index": { "_id": 3 }}{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }{ "index": { "_id": 4 }}{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }
此时,如果我们再次搜索就会得到我们想要的结果。
过滤器的内部操作
在内部,当ElasticSearch运行时,会执行多个操作:
找到匹配的文件
term 过滤器在反向索引表中查找 XHDK-A-1293-#fJ3 然后返回有这个 term 的所有文件,这个例子中,只有1个文件满足。
创建位集合(bitset)
filter会创建一个包含有0和1的 bitset ,这个数组描述了哪个文档有这个 term 。对于匹配的文件标志位为1,在我们的这个例子中,位集合的值为 [1,0,0,0]。
缓存位集合
最后,bitset会存在于内存之中,因为我们可以用这个值来直接跳过步骤1和2,这使得filter处理更快,性能更好。
当执行 filtered 查询时,filter 在 query 之前执行,所以在filter产生的bitset会传给 query,query 会依据bitset的内容,直接排除掉已被filter过滤掉的文件,这是提高处理性能的一种方式,更少的文档意味着更小的相应时间。
组合过滤器
上面的两个例子都是单个filter的使用方式,在实际中,我们很多情况下会同时会对多个值或字段使用filter。例如,在ElasticSearch中,如何标识下面这个SQL?
SELECT productFROM productsWHERE (price = 20 OR productID = "XHDK-A-1293-#fJ3") AND (price != 30)
在这种情况下,我们需要 bool filter。这是一个复合过滤器(compound filter)可以接收多个参数,然后将他们组合成布尔组合(Boolean combination)。
布尔过滤器(Bool Filter)
bool filter包括三部分:
{ "bool" : { "must" : [], "should" : [], "must_not" : [], }}
must
所有的语句必须匹配,与 AND 等价。
must_not
所有的语句都不能匹配,与 NOT 等价。
should
至少有一个语句匹配,与 OR 等价。
需要注意的是:bool filter的每个部分都是可选的(例如,我们可以只有一个 must 语句),而且每个部分内部可以只有一个filter,或者一组(array)filter。
用ElasticSearch的DSL实现我们上面SQL里的查询:
GET /my_store/products/_search{ "query" : { "filtered" : { #1 "filter" : { "bool" : { "should" : [ { "term" : {"price" : 20}}, #2 { "term" : {"productID" : "XHDK-A-1293-#fJ3"}} #3 ], "must_not" : { "term" : {"price" : 30} #4 } } } } }}
- #1 注意,我们仍然需要一个 filtered 查询将所有的东西包裹起来。
- #2 这两个在 should 条件块里面的 term 是 bool filter的子过滤器,
- #3 should 条件块里面,其一需要满足
- #4 如果一个产品的价格是 30,那么它会自动被排除,因为它处于 must_not 条件块里面。
我们搜索的结果返回了2个hits,两个文件各满足其中一个条件:
"hits" : [ { "_id" : "1", "_score" : 1.0, "_source" : { "price" : 10, "productID" : "XHDK-A-1293-#fJ3" } }, { "_id" : "2", "_score" : 1.0, "_source" : { "price" : 20, "productID" : "KDKE-B-9947-#kL5" } }]
嵌套布尔过滤器(Nesting Boolean Filters)
尽管 bool 是一个复合的过滤器,可以接受多个子过滤器,需要注意的是 bool 过滤器本身仍然是一个过滤器(filter)。这意味着我们可以将一个bool过滤器置于另外一个bool过滤器内部,这为我们提供了复杂布尔逻辑的处理能力:
对于一个SQL语句:
SELECT documentFROM productsWHERE productID = "KDKE-B-9947-#kL5" OR ( productID = "JODL-X-1937-#pV7" AND price = 30 )
我们将其转换成一个嵌套的 bool 过滤器:
GET /my_store/products/_search{ "query" : { "filtered" : { "filter" : { "bool" : { "should" : [ { "term" : {"productID" : "KDKE-B-9947-#kL5"}}, #1 { "bool" : { #2 "must" : [ { "term" : {"productID" : "JODL-X-1937-#pV7"}}, #3 { "term" : {"price" : 30}} #4 ] }} ] } } } }}
- #1 因为 term 和 bool 过滤器是兄弟关系,他们都处于 should 过滤器内部,
- #2 命中返回的文件中,需要至少满足其中一个filter的条件。
- #3 这两个 term 兄弟关系的条件同时处于 must 语句之中,所以
- #4 命中返回的文件,必须同时满足这两个条件。
得到的结果有两个文件,他们各满足 should 中的一个条件:
"hits" : [ { "_id" : "2", "_score" : 1.0, "_source" : { "price" : 20, "productID" : "KDKE-B-9947-#kL5" #1 } }, { "_id" : "3", "_score" : 1.0, "_source" : { "price" : 30, #2 "productID" : "JODL-X-1937-#pV7" #3 } }]
- #1 这个 productID 匹配 bool 过滤器 should 里的第一个 term
- #2 这两个字段匹配 bool 过滤器 should 里嵌套的 bool 过滤器
这只是一个简单的例子,但足以呈现 Boolean filter 可以用来构建复杂逻辑条件的能力。
多值精确查询
term 过滤器对于查找单个值非常有用,但是在很多时候我们想要进行多值查询。如果我们想要找到价格为 $20 或 $30 的产品文件该怎么办呢?
不需要使用多个 term 过滤器,我们只需要为 term 加上 s 告诉ElasticSearch就行,terms 只是 term 过滤器的复数形式(以英语单词做比)。
我们要做的只是要将 price 的值改为数组:
{ "terms" : { "price" : [20, 30] }}
完整的形式和 term 过滤器一样,我们只需要将其置入 filtered 查询块中:
GET /my_store/products/_search{ "query" : { "filtered" : { "filter" : { "terms" : { "price" : [20, 30] } } } }}
运行结果返回第二、三、四个文档
"hits" : [ { "_id" : "2", "_score" : 1.0, "_source" : { "price" : 20, "productID" : "KDKE-B-9947-#kL5" } }, { "_id" : "3", "_score" : 1.0, "_source" : { "price" : 30, "productID" : "JODL-X-1937-#pV7" } }, { "_id": "4", "_score": 1.0, "_source": { "price": 30, "productID": "QQPX-R-3956-#aD8" } }]
包含,但不是相等
需要了解的是 term 和 terms 是包含操作,而非等值判断,如何理解这句话呢?
如果我们有一个term过滤器
{ "term" : { "tags" : "search" } }
它会与以下两个文件匹配:
{ "tags" : ["search"] }{ "tags" : ["search", "open_source"] } #1
- #1 尽管第二个文件包含除 search 之外的其他词,它也会被匹配到。
回想 term 过滤器是如何工作的?ElasticSearch会在反向索引表中查找相应的term,然后创建一个bitset。在我们的例子中,反向索引表如下:
------------------------------------------- Token | DocIDs------------------------------------------- open_source | 2------------------------------------------- search | 1,2-------------------------------------------
这里 term 过滤器直接在反向索引表中找到 search 相关的文档ID,这里即为文件1、文件2,所以两个文件都会作为结果返回。
注意:
由于反向索引表自身的特性,整个字段是否相等比较难以计算,如果确定一个文件只包含我们想要查找的词呢?首先我们需要在反向索引表中找到相关的记录,然后再扫描记录,看他们是否包含其他的词,可以想象这样做的代价是非常高的。正因如此,term和terms是must contain 操作,而非 must equal。
Equals Exactly
如果一定期望得到我们上面说的那种行为 must equal,最好的方式是添加另一个字段,这个字段用来存储比较字段词个数,同样以上面提到的两个文件为例:
{ "tags" : ["search"], "tag_count" : 1 }{ "tags" : ["search", "open_source"], "tag_count" : 2 }
我们增加了tag_count以满足我们的要求,这个我们可以通过 bool 来确保查询满足我们的要求:
GET /my_index/my_type/_search{ "query": { "filtered" : { "filter" : { "bool" : { "must" : [ { "term" : { "tags" : "search" } }, { "term" : { "tag_count" : 1 } } ] } } } }}
范围查询
到目前为止,我们只讲到了数字的精确查询。在实际中,按照数字的范围进行查找也非常普遍,例如,我们想要找到价格大于 $20 而且小于 $40 的产品。
在SQL语句中,这句话可以表示成:
SELECT documentFROM productsWHERE price BETWEEN 20 AND 40
在ElasticSearch中,我们对应有:
"range" : { "price" : { "gt" : 20, "lt" : 40 }}
range 过滤器同时提供包含和排除两种范围表达式,可以组合使用一下选项:
- gt: > greater than
- lt: < less than
- gte: >= greater than or equal to
- lte: <= less than or equal to
这里有一个完整的例子
GET /my_store/products/_search{ "query" : { "filtered" : { "filter" : { "range" : { "price" : { "gte" : 20, "lt" : 40 } } } } }}
如果需要一边无界(例如>20),将lt部分删除即可:
"range" : { "price" : { "gt" : 20 }}
时间范围
range同样可以应用到时间字段上:
"range" : { "timestamp" : { "gt" : "2014-01-01 00:00:00", "lt" : "2014-01-07 00:00:00" }}
当使用range处理时间字段时,range 过滤器支持时间计算(date math)操作,例如,我们可以查找时间戳在过去一小时内的所有文件:
"range" : { "timestamp" : { "gt" : "now-1h" }}
这个过滤器会时刻查找过去一个小时内的所有文件,这样我们也实现了通过移动的时间窗过滤文件的功能。
时间计算还可以指定某一具体时间,只要在某一时间后面加上一个pipe (||)就能实现
"range" : { "timestamp" : { "gt" : "2014-01-01 00:00:00", "lt" : "2014-01-01 00:00:00||+1M" }}
上面所要查找的是2014年1月1日加上1月的时间。
Date本身是日历相关的,所以它自己知道每个月具体的日期,也知道一年有多少天(闰年),具体的内容可以在时间格式相关的文档中找到。
字符串的范围
range同样可以应用到字符串字段,字符串范围可以按照 lexicographically 来,也可以根据alphabetically来,例如下面一串字符串是根据lexicographically来排序的:
5, 50, 6, B, C, a, ab, abb, abc, b
在反向索引表中的词就是根据lexicographically的顺序来排列的,这也是为什么字符串可以使用这个顺序来确定范围。
查找自 a 开始,以 b (不包括)结束的所有词:
"range" : { "title" : { "gte" : "a", "lt" : "b" }}
注意Cardinality:
数字和日期的索引方式使他们可以高效的进行range查询,但是对于字符串来说,ElasticSearch只是简单的比较每个反向索引表中的每个词,看他们是否处于范围之中,但是这比时间和数字的范围查找要慢许多。
字符串范围查找在 low cardinality (即具有少数唯一值)的时候可以正常使用,但是唯一值越多,对于字符串的范围查询会越慢。
处理Null
回想我们之前的一个例子,有字段名为 tags 的一组文件,这个字段有多个值,一个文件可能有一个tag(标签),多个tag,也有可能没有tag,如果一个字段没有任何值,那么它在反向索引中是如何存储的呢?
这是个具有欺骗性的问题,因为答案是,什么都不存。让我们回头看看之前那个反向索引表:
------------------------------------------- Token | DocIDs------------------------------------------- open_source | 2------------------------------------------- search | 1,2-------------------------------------------
那么如何存储一个数据结构中不存在的字段呢?这样可似乎我们做不到,一个反向索引表只是一个简单的token以及包含它的文件列表,如果一个字段不存在,那么它也不会有任何token,也就是说它不会在反向索引表中存在。
这就意味着,null,[](空数组)和 [null] 是等价的。它们都不在反向索引表中。
但是世界并不简单,有很多情况字段没有数据,或者有显式的 null 或者空数组。为了解决这个问题,ElasticSearch提供了一些工具。
存在过滤器(exists Filter)
第一个武器是 exists 过滤器,让我们以下面这些文档为例:
POST /my_index/posts/_bulk{ "index": { "_id": "1" }}{ "tags" : ["search"] } #1{ "index": { "_id": "2" }}{ "tags" : ["search", "open_source"] } #2{ "index": { "_id": "3" }}{ "other_field" : "some data" } #3{ "index": { "_id": "4" }}{ "tags" : null } #4{ "index": { "_id": "5" }}{ "tags" : ["search", null] } #5
- #1 tags字段有1个值
- #2 tags字段有2个值
- #3 The tags field is missing altogether.
- #4 The tags field is set to null.
- #5 The tags field has one value and a null.
上面的文件集合对应的反向索引表是这样:
------------------------------------------- Token | DocIDs------------------------------------------- open_source | 2------------------------------------------- search | 1,2,5-------------------------------------------
我们的目的是找到那些设置过tag的文件,并不关心tag具体是什么,只要它存在于文档中即可,在SQL里,我们会使用 IS NOT NULL 进行查询。
SELECT tagsFROM postsWHERE tags IS NOT NULL
在ElasticSearch中,我们使用 exists 过滤器:
GET /my_index/posts/_search{ "query" : { "filtered" : { "filter" : { "exists" : { "field" : "tags" } } } }}
这个查询返回3个文件
"hits" : [ { "_id" : "1", "_score" : 1.0, "_source" : { "tags" : ["search"] } }, { "_id" : "5", "_score" : 1.0, "_source" : { "tags" : ["search", null] } #1 }, { "_id" : "2", "_score" : 1.0, "_source" : { "tags" : ["search", "open source"] } }]
- #1 尽管文件5有null,但它也会被返回。字段因为有真实值而存在,null对过滤不会产生任何影响。
结果显而易见,只要含有字段tags文件都会返回,只有两个文件3、4被排除在外。
缺失过滤器(missing Filter)
missing 过滤器本质上与 exists 相反,它返回某个字段没有值的文件,如果用类似SQL表示
SELECT tagsFROM postsWHERE tags IS NULL
我们将前面例子里面的 exists 换成 missing
GET /my_index/posts/_search{ "query" : { "filtered" : { "filter": { "missing" : { "field" : "tags" } } } }}
按照我们期望的那样,3、4两个文件会返回
"hits" : [ { "_id" : "3", "_score" : 1.0, "_source" : { "other_field" : "some data" } }, { "_id" : "4", "_score" : 1.0, "_source" : { "tags" : null } }]
当null是null
有时候我们需要区分一个字段是没有值,还是一个字段被显式的设置成了null。我们看到之前的系统默认行为是无法做到的;数据丢失了。不过幸运的是,我们可以选择将显试的 null 替换成一个我们定义的占位符。
同样,在字符串,数字,布尔值或时间为 null 的时候,我们可以为之设置 null_value,对于没有任何值的字段还是会被排除在反向索引表之外。
当我们选择合适的 null_value 的时候,我们需要保证以下几点:
- 它会匹配字段类型,我们不能为一个时间字段设置一个字符串类型的 null_value。
- 它必须与一般平常的值不一样,这样可以避免把真实值当成 null 的情况。
对象上的存在或缺失(exists/missing on Objects)
exists 和 missing 除了过滤核心类型外,还可以过滤一个对象的内部字段。下面这个文件:
{ "name" : { "first" : "John", "last" : "Smith" }}
我们可以直接检查 name.first 和 name.last 的存在性,也可以只检查 name 的存在性,正如在类型与映射中说的,上面这个对象的结构在内部会扁平化存储,类似下面这样:
{ "name.first" : "John", "name.last" : "Smith"}
那我们如何去用 exists 和 missing 过滤 name 字段呢?它并不在反向索引表中真实存在,
原因是当我们执行下面这个过滤的时候:
{ "exists" : { "field" : "name" }}
实际上执行的是:
{ "bool": { "should": [ { "exists": { "field": { "name.first" }}}, { "exists": { "field": { "name.last" }}} ] }}
这也就意味着,如果 first 和 last 都是空的情况下,name 的命名空间也不存在
关于缓存
在前面 过滤器的内部操作 中我们以及简单介绍过滤器是如何计算的。他们的内部实际上是用一个bitset记录与过滤器匹配的文件。ElasticSearch把这些内容缓存起来,以备将来使用。一旦缓存成功,如果重复使用相同的过滤器,这些bitset可以被复用,而不需要重新计算整个过滤器。
这些bitset缓存是非常智能的,他们可以做到增量更新,当我们索引新文件时,只需要将新文档的计算结果加入到现有的bitset中,而不是对整个缓存一遍又一遍的重新计算。过滤器是实时的,我们不需要担心缓存失效的问题。
独立的过滤器缓存
每个过滤器是独立计算并独立缓存的,与他们具体的使用场景无关,如果两个完全不同的查询使用了相同的过滤器,相同的缓存bitset会被复用。同样,如果一个查询在多个地方使用到了相同的过滤器,bitset只会计算一次然后被重复使用。
让我们看看下面这个例子,它查询了需要满足以下条件的email:
- 在收件箱中而且没有被读过
- 不在收件箱中但是被标注了重要
示例:
"bool": { "should": [ { "bool": { "must": [ { "term": { "folder": "inbox" }}, #1 { "term": { "read": false }} ] }}, { "bool": { "must_not": { "term": { "folder": "inbox" } #2 }, "must": { "term": { "important": true } } }} ]}
- #1 #2两个过滤器是相同的,所以也会使用同一bitset。
尽管一个inbox语句是 must,另一个是 must_not ,但是他们两个是一样的,这意味着第一个语句执行之后,这个过滤器的bitset会被缓存起来,供第二个使用。当这个查询再次执行时,这个过滤器已经被缓存,所以两个语句都会使用已缓存的bitset。
这点与DSL查询结合得很好。它可以被移动到任何地方,也可以在同一查询中的多个位置反复使用。这不仅仅能方便开发者,而且对性能有直接的好处。
缓存控制
多数叶子过滤器(leaf filters)是被缓存的。叶子过滤器是指那些直接处理字段的term过滤器,但是不会缓存复合过滤器,如bool过滤器。
注意:
叶子过滤器会要访问磁盘上的反向索引表,所以我们有理由将他们缓存起来,但是组合过滤器运用快速的位逻辑将内部语句的bitset合并起来,所以即使每次计算效率也很高。
对于某些叶子过滤器,默认状态下不会缓存,因为缓存它们没有任何意义,比如:
脚本过滤(Script filters)
因为对于ElasticSearch来说脚本的含义是含糊的。
地理位置过滤(GEO filters)
因为地理位置的信息通常是和用户相关的,所以每次过滤的结果都会不太一样,对它做缓存意义不大。
日期范围(Date ranges)
时间范围用了 now 的。每次过滤的时候 now 都会返回一个最新的时间,所以旧的过滤器不会被复用,所以也不需要缓存。但是,当如果我们将 now 与rounding一起使用表示最近的一天时(now/d),它也会缓存。
有时默认的缓存策略并不正确。可能我们需要反复使用一个非常复杂的bool查询,或者我们对时间字段有一个过滤器但永远不会复用。默认的缓存策略可以几乎在所有filter上进行覆盖重写,只要设置标志位 *_cache* 就行:
{ "range" : { "timestamp" : { "gt" : "2014-01-02 16:15:14" #1 }, "_cache": false #2 }}
- #1 我们通常情况下不会再次使用这个时间戳
- #2 关闭这个过滤器的缓存功能
过滤顺序
在 bool 过滤器中,过滤器的顺序对性能是非常重要的,更具体的过滤器需要放在次具体的过滤器前面,这样可以帮更早的排除更多的文件。
如果语句A可以匹配10,000,000个文件,语句B只能匹配100个,那么语句B需要放在语句A的前面。
缓存的过滤器非常快,他们需要放在不能缓存的过滤器前,如果我们对1小时内的数据非常感兴趣:
GET /logs/2014-01/_search{ "query" : { "filtered" : { "filter" : { "range" : { "timestamp" : { "gt" : "now-1h" } } } } }}
因为使用了now,ElasticSearch不会缓存这个过滤器,这意味着我们每次查询时都需要检查一个月的日志数据。
我们可以将这个查询与一个缓存过滤器结合,让它变得更高效,我们可以通过增加昨天凌晨的时间点,将大量日志排除:
"bool": { "must": [ { "range" : { "timestamp" : { "gt" : "now-1h/d" #1 } }}, { "range" : { "timestamp" : { "gt" : "now-1h" #2 } }} ]}
- #1 这个过滤器会被缓存,因为它用到了now字段,并将其截断到凌晨
- #2 这个过滤器不会被缓存,因为它没有用到now时间截取
now-1h/d 这个句子将时间置为凌晨,把今日之前所有的文件都排除掉了,这意味着bitset每天只会被执行一次,这次发生在 昨日凌晨(midnight-last-night) 这个时间发生变化的时候。由于第一个过滤器可以帮我们过滤掉之前的大量文件,第二个过滤器只会从剩下的文件中过滤出最近一小时的文件。
语句的顺序非常重要,这个方法只在 since-midnight 语句置于 last-hour 之前有效。如果顺序相反,那么 last-hour 语句就需要过滤整月的文件,而非当天的文件。
参考
elastic.co: Structured Search
- ElasticSearch 2 (13)
- Elasticsearch 2
- Elasticsearch集群入门2
- Elasticsearch学习笔记(2)
- Elasticsearch java API (2)
- Elasticsearch(2)安装
- ElasticSearch 5.2.2 安装
- ElasticSearch 2 (15)
- ElasticSearch 2.4.2
- ElasticSearch 2 (12)
- ElasticSearch 2 (12)
- elasticsearch 编程api 2
- ElasticSearch
- Elasticsearch
- Elasticsearch
- ElasticSearch
- elasticSearch
- elasticsearch
- java安装与配置
- jsp页面错误不跳转到指定错误页面的解决方法
- Ubuntu系统上安装Nginx服务器的简单方法
- RxSwift(3.4.1)- Time Operators
- MySQL
- ElasticSearch 2 (13)
- redis数据类型及持久化方案
- maven -- 创建新的项目,该项目需要依赖上一发布的项目
- 【GDOI 2017 day2】小学生语文题
- JAVA Date类、SimpleDateFormat抽象类、Calendar类
- PHPMailer Exploit Remote Code Exec CVE-2016-10033 Vuln
- 组合数之卢卡斯定理
- 分子式
- 直接插入排序