处理 Null 值
我们的目标是找到那些被设置过标签字段的文档,并不关心标签的具体内容。只要它存在于文档中即可,用 SQL 的话就是用 IS NOT NULL
非空进行查询:
SELECT tags
FROM posts
WHERE tags IS NOT NULL
在 Elasticsearch 中,使用 exists
查询的方式如下:
GET /my_index/posts/_search
{
"query" : {
"constant_score" : {
"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 值,但它仍会被命中返回。字段之所以存在,是因为标签有实际值( search )可以被索引,所以 null 对过滤不会产生任何影响。
缺失查询
这个 missing
查询本质上与 exists
恰好相反: 它返回某个特定 _无_ 值字段的文档,与以下 SQL 表达的意思类似:
SELECT tags
FROM posts
WHERE tags IS NULL
我们将前面例子中 exists
查询换成 missing
查询:
GET /my_index/posts/_search
{
"query" : {
"constant_score" : {
"filter": {
"missing" : { "field" : "tags" }
}
}
}
}
按照期望的那样,我们得到 3 和 4 两个文档(这两个文档的 tags
字段没有实际值):
"hits" : [
{
"_id" : "3",
"_score" : 1.0,
"_source" : { "other_field" : "some data" }
},
{
"_id" : "4",
"_score" : 1.0,
"_source" : { "tags" : null }
}
]
当 null 的意思是 null
有时候我们需要区分一个字段是没有值,还是它已被显式的设置成了 null
。在之前例子中,我们看到的默认的行为是无法做到这点的;数据被丢失了。不过幸运的是,我们可以选择将显式的 null
值替换成我们指定 占位符(placeholder) 。
在为字符串(string)、数字(numeric)、布尔值(Boolean)或日期(date)字段指定映射时,同样可以为之设置 null_value
空值,用以处理显式 null
值的情况。不过即使如此,还是会将一个没有值的字段从倒排索引中排除。
当选择合适的 null_value
空值的时候,需要保证以下几点:
- 它会匹配字段的类型,我们不能为一个
date
日期字段设置字符串类型的null_value
。 - 它必须与普通值不一样,这可以避免把实际值当成
null
空的情况。
对象上的存在与缺失
不仅可以过滤核心类型, exists
and missing
查询 还可以处理一个对象的内部字段。以下面文档为例:
{
"name" : {
"first" : "John",
"last" : "Smith"
}
}
我们不仅可以检查 name.first
和 name.last
的存在性,也可以检查 name
,不过在 映射 中,如上对象的内部是个扁平的字段与值(field-value)的简单键值结构,类似下面这样:
{
"name.first" : "John",
"name.last" : "Smith"
}
那么我们如何用 exists
或 missing
查询 name
字段呢? name
字段并不真实存在于倒排索引中。
原因是当我们执行下面这个过滤的时候:
{
"exists" : { "field" : "name" }
}
实际执行的是:
{
"bool": {
"should": [
{ "exists": { "field": "name.first" }},
{ "exists": { "field": "name.last" }}
]
}
}
这也就意味着,如果 first
和 last
都是空,那么 name
这个命名空间才会被认为不存在。