1.结构化搜索
结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作。比较常见的操作包括比较数字或时间的范围,或判定两个值的大小。
文本也可以是结构化的。如彩色笔可以有离散的颜色集合: 红(red)
、 绿(green)
、 蓝(blue)
。一个博客可能被标记了关键词 分布式(distributed)
和 搜索(search)
。电商网站上的商品都有 UPCs(通用产品码 Universal Product Codes)或其他的唯一标识,它们都需要遵从严格规定的、结构化的格式。
在结构化查询中,我们得到的结果 总是 非是即否,要么存于集合之中,要么存在集合之外。结构化查询不关心文件的相关度或评分;它简单的对文档包括或排除处理。
这在逻辑上是能说通的,因为一个数字不能比其他数字 更 适合存于某个相同范围。结果只能是:存于范围之中,抑或反之。同样,对于结构化文本来说,一个值要么相等,要么不等。没有 更似 这种概念。
a.精确值查找
我们首先来看最为常用的 term
查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。
//使用filter查询,不需要评分机制:constant_score,指定固定的分数:boost
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"price": 20
}
},
"boost": 1.2
}
}
}
term 查询文本
如本部分开始处提到过的一样 ,使用 term
查询匹配字符串和匹配数字一样容易
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"product_id": "b"
}
},
"boost": 1.2
}
}
}
b.组合过滤器
布尔过滤器
must
文档必须匹配这些条件才能被包含进来.must_not
文档必须不匹配这些条件才能被包含进来.
should
当文档满足此条件时,会增加其_score
值,类似于or
语句filter
必须匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
//不参与计分的bool
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"should":[
{"term":{"price":20}},
{"term":{"product_id":"c"}}
],
"must_not":{
"term":{"price" : 30}
}
}
},
"boost": 1.2
}
}
}
//部分参与计分的bool
GET products/_search
{
"query": {
"bool": {
"filter": [
{"term": {
"product_id": {
"value": "d"
}
}}
],
"should":[
{"term":{"price":20}},
{"term":{"product_id":"c"}}
],
"must_not":{
"term":{"price" : 30}
}
}
}
}
//全部参与计分
//部分参与计分的bool
GET products/_search
{
"query": {
"bool": {
"must": [
{"term": {
"product_id": {
"value": "d"
}
}}
],
"should":[
{"term":{"price":20}},
{"term":{"product_id":"c"}}
],
"must_not":{
"term":{"price" : 30}
}
}
}
}
嵌套布尔过滤器
尽管 bool
是一个复合的过滤器,可以接受多个子过滤器,需要注意的是 bool
过滤器本身仍然还只是一个过滤器。 这意味着我们可以将一个 bool
过滤器置于其他 bool
过滤器内部,这为我们提供了对任意复杂布尔逻辑进行处理的能力。
GET products/_search
{
"query": {
"bool": {
"filter": [
{"term": {
"product_id": {
"value": "b"
}
}},
{
"bool":{
"should":[
{"term":{"price":20}},
{"term":{"product_id":"c"}}
]
}
}
],
"must_not":{
"term":{"price" : 30}
}
}
}
}
c.查找多个精确值
term
和 terms
是 包含(contains) 操作,而非 等值(equals) (判断)
//同一个字段查询多个值
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"terms": {
"product_id": ["a","b"]
}
},
"boost": 1.2
}
}
}
d.范围
range
查询可同时提供包含(inclusive)和不包含(exclusive)这两种范围表达式,可供组合的选项如下:
gt
:>
大于(greater than)lt
:<
小于(less than)
gte
:>=
大于或等于(greater than or equal to)lte
:<=
小于或等于(less than or equal to)
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"price": {
"gte":30,
"lte": 40
}
}
},
"boost": 1.2
}
}
}
日期范围
当使用它处理日期字段时, range
查询支持对 日期计算(date math) 进行操作,比方说,如果我们想查找时间戳在过去一小时内的所有文档:
"range" : {
"timestamp" : {
"gt" : "now-1h"
}
}
这个过滤器会一直查找时间戳在过去一个小时内的所有文档,让过滤器作为一个时间 滑动窗口(sliding window) 来过滤文档。
日期计算还可以被应用到某个具体的时间,并非只能是一个像 now 这样的占位符。只要在某个日期后加上一个双管符号 (||
) 并紧跟一个日期数学表达式就能做到:
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-01 00:00:00||+1M"
}
}
早于 2014 年 1 月 1 日加 1 月(2014 年 2 月 1 日 零时)
日期计算是 日历相关(calendar aware) 的,所以它不仅知道每月的具体天数,还知道某年的总天数(闰年)等信息。更详细的内容可以参考: 时间格式参考文档 。
字符串范围
range
查询同样可以处理字符串字段,字符串范围可采用 字典顺序(lexicographically) 或字母顺序(alphabetically)。例如,下面这些字符串是采用字典序(lexicographically)排序的:
- 5, 50, 6, B, C, a, ab, abb, abc, b
在倒排索引中的词项就是采取字典顺序(lexicographically)排列的,这也是字符串范围可以使用这个顺序来确定的原因。
注意基数
数字和日期字段的索引方式使高效地范围计算成为可能。但字符串却并非如此,要想对其使用范围过滤,Elasticsearch 实际上是在为范围内的每个词项都执行 term
过滤器,这会比日期或数字的范围过滤慢许多。
字符串范围在过滤 低基数(low cardinality) 字段(即只有少量唯一词项)时可以正常工作,但是唯一词项越多,字符串范围的计算会越慢。
e.处理null值
最终,这也就意味着,null
, []
(空数组)和 [null]
所有这些都是等价的,它们无法存于倒排索引中
在 Elasticsearch 中,使用 exists
查询的方式 进行判断null
//获取字段product_id中有值的文档
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"exists": {
"field": "product_id"
}
},
"boost": 1.2
}
}
}
缺失查询
这个 missing
查询本质上与 exists
恰好相反:它返回某个特定 无 值字段的文档(最新版本已经不使用这个关键字)
//查询缺少字段product_id的文档
GET products/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"must_not":{
"exists":{
"field":"product_id"
}
}
}
},
"boost": 1.2
}
}
}