elasticsearch 处理空值

朱小虎,University AI 创始人 & Chief Scientist,致力于推进世界人工智能化进程。制定并实施 UAI 中长期增长战略和目标,带领团队快速成长为人工智能领域最专业的力量。
作为行业领导者,他和UAI一起在2014年创建了TASA(中国最早的人工智能社团), DL Center(深度学习知识中心全球价值网络),AI growth(行业智库培训)等,为中国的人工智能人才建设输送了大量的血液和养分。此外,他还参与或者举办过各类国际性的人工智能峰会和活动,产生了巨大的影响力,书写了60万字的人工智能精品技术内容,生产翻译了全球第一本深度学习入门书《神经网络与深度学习》,生产的内容被大量的专业垂直公众号和媒体转载与连载。曾经受邀为国内顶尖大学制定人工智能学习规划和教授人工智能前沿课程,均受学生和老师好评。

源地址
考虑前面的例子,其中文档有一个称为 tags 的字段。这是一个多值字段。文档可以有一个、多个或者没有标签。如果字段没有值,那么它是怎么在倒排索引中存储的呢?

这个问题真诡异,因为答案是,它并没有被存储。让我们看一下上一节提到的倒排索引:

Token DocIDs
open_source 2
search 1,2

如何存储一个在那个数据结构中不存在的字段呢?压根不行!倒排索引是一系列 token 和包含它的文档的列表。如果字段不存在,那也不会保存任何 token,所以在倒排索引中也不会有任何表示。

最终,这意味着 null[][null] 都是等价的。都不存在于倒排索引中!

很显然,真实世界没有这么简单,数据的字段经常会丢失或者包含显式的 null 或者空数组。为了解决这些问题,elasticsearch 有一些工具来处理空值或者丢失的数据。

exists Filter

exists 过滤器是第一件武器。这个过滤器返回特定字段中拥有任何值的文档。让我们使用 tagging 的例子,索引几个样本文档:

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 字段有一个值
  • (2) tags 字段有两个值
  • (3) tags 字段丢失
  • (4) tags 字段设置为 null
  • (5) tag 字段有一个值和一个 null

最终的倒排索引就是:

Token DocIDs
open_source 2
search 1,2,5

我们的目标是找到设置了 tag 的所有文档。不管是 tag 是什么,只要它出现在文档中。在 SQL 中,我们通常可以使用 IS NOT NULL 查询:

SELECT tags
FROM posts
WHERE tags IS NOT NULL

在 elasticsearch 中,我们就使用 exists 过滤器:

GET /my_index/posts/_search
{ 
  "query" : { 
    "filtered" : { 
      "filter" : { 
        "exists" : { "field" : "tags" }}}}}

最后返回三个文档:

"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 值也返回了。因为真实值的 tag 被索引了,所以这个字段存在。所以 null 对过滤器没有影响。

结果很容易理解。任何在 tags 字段中有 term 的文档都作为命中结果返回了。被排除在外的两个文档就是 3 和 4。

missing 过滤器

missing 过滤器本质上是 exists 的逆:它返回对应一个特定的字段没有任何值的文档,就像 SQL:

SELECT tags
FROM posts
WHERE tags IS NULL

让我们用 missing 来替换上面例子中 exists 过滤器:

GET /my_index/posts/_search
{
    "query" : {
        "filtered" : {
            "filter": {
                "missing" : { "field" : "tags" }
            }
        }
    }
}

正如你所期望的,我们拿到了在 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
当指定一个 string、numeric、Boolean 或者日期字段时,你同样能设置 null_value 可以用在任何遇到显式的 null 值的地方。没有一个值的字段显然可以从倒排索引中排除。
选择合适的 null_value,确保下面的事项:

  • 匹配了字段的类型(type)。你不能在一个类型为 date 的字段上用一个 string 的 null_value
  • 不同于字段可能包含的正常值,来避免出令人困惑的出现 null 的真实值

exists/missing on Objects

exists/missing 过滤器同样可以用在内部对象上(inner objects),不仅仅核心类型(core types)。假如有下面的文档

{
   "name" : {
      "first" : "John",
      "last" :  "Smith"
   }
}

你可以检查 name.firstname.last 不仅仅是 name 的存在。然而,在Types and Mappings中,我们提到对象在内部会进行平化展开成一个简单的字段值结构,像这样:

{
   "name.first" : "John",
   "name.last"  : "Smith"
}

所以,我们如何在 name 字段上使用 existsmissing 过滤器,这实际上并不存在于倒排索引中?

其原因就是,这会按照如下的过滤器那样:

{ "exists" : { "field" : "name" }}

实际上是按照:

{
    "bool": {
        "should": [
            { "exists": { "field": { "name.first" }}},
            { "exists": { "field": { "name.last"  }}}
        ]
    }
}

执行的。

这样也意味着如果 firstlast 同时是空,name 命名空间就不会存在。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容