意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

java - Elasticsearch 每日一课:存储一个 float 值到 long 字段会怎么样?_个人文章

来源:恒创科技 编辑:恒创科技编辑部
2024-01-30 14:00:59

今天我们要讨论的主题会让很多新手感到困惑,因为本身这个问题就挺难理解的。但如果你对 ES 比较熟悉,今天的内容也可以测试一下你对 ES 数据类型方面的知识是否牢靠。

一、默认的 Numeric 类型

首先,我们创建一个索引:

PUT orders/_doc/1
{
  "order_id": 1
}

如上示例,利用 Dynamic Mapping,系统会为自动为 orders 索引创建一个 Mapping,那么默认的情况下,order_id 这个字段会被推断为啥类型呢?


java - Elasticsearch 每日一课:存储一个 float 值到 long 字段会怎么样?_个人文章

下面是这个 orders 索引的 Mapping:

# 获取 Mapping
GET orders/_mapping

# 结果:
{
  "orders" : {
    "mappings" : {
      "properties" : {
        "order_id" : { "type" : "long" }
      }
    }
  }
}

如上示例,很明显 Dynamic Mapping 推断的 order_id 为 long 类型。

那如果我们对这个 order_id 字段写入一些非整型数字会发生啥情况呢?在 Kibana 中执行以下命令来插入文档:

PUT orders/_doc/2
{
  "order_id": 2
}

PUT orders/_doc/3
{
  "order_id": "3"
}

PUT orders/_doc/4
{
  "order_id": 4.5
}

PUT orders/_doc/5
{
  "order_id": "5.5"
}

PUT orders/_doc/6
{
  "order_id": "六"
}

文档 2 跟文档 1 相似,所以肯定可以插入。但当我们尝试把一个字符串、float,甚至字符串形式的 float 值存储到 long 类型字段时会发生啥呢?可以肯定的是,除了文档 6,其他都可以成功存储,但为啥呢?

二、coerce 对数据处理

默认的情况下,ES 将会用 coerce 来处理不符合规则的数据。Coercion 会尝试转换不符合字段类型的数据,例如:

字符串会被转换到 numbers。Float 会被截断,转换为 integer。

ok,有了 coerce,那我们通过 search 接口获取到的 order_id 会是 1,2,3,4,5 吗?其实不然:

# 获取数据
GET orders/_search
{
  "query": { "match_all": {} }
}

# 结果
{
  "_shards" : {.....},
  "hits" : {
    ......
    "hits" : [
      {
        "_id" : "1",
        "_source" : { "order_id" : 1 }
      },
      {
        "_id" : "2",
        "_source" : { "order_id" : 2 }
      },
      {
        "_id" : "3",
        "_source" : { "order_id" : "3" }
      },
      {
        "_id" : "4",
        "_source" : { "order_id" : 4.5 }
      },
      {
        "_id" : "5",
        "_source" : { "order_id" : "5.5" }
      }
    ]
  }
}

看到上面的结果,你是不是蒙圈了?不是说会数据转换处理的吗?如果此时再次查看 Mapping 的话,发现 order_id 的的确确是 long 类型的。

三、不会被改变的 _source

上述的这个疑问其实很好解答,因为 ES 不会改变 _source 的内容。简单来说就是 _source 的数据其实存储在一个地方,而 order_id 这个字段的索引数据却存储在另外一个地方,它们不是混着(共用)的。简单来说就是,写入数据的时候,会把 _source 里的 order_id 拿出来,经过 coerce 进行处理,然后对处理后的结果进行索引操作。

可以通过对 order_id 字段做聚合操作来验证这个想法:

# 聚合操作
GET orders/_search
{
  "size": 0,
  "aggs": {
    "the_sum": {
      "sum": { "field": "order_id" }
    }
  }
}

# 结果
{
  ......
  "aggregations" : {
    "the_sum" : {
      "value" : 15.0
    }
  }
}

对 order_id 进行聚合操作,最终得出正确结果,可以证明其数据的类型为 long。

四、总结

最后需要注意的是,coerce 以后可能会被删除!!!关于这部分的信息,你可以参考这个 issue。怎么说呢,默认的情况下去改动用户输入的数据,其实不是一个太好的选择,let it crash 不好吗?

另外,从这里例子中我们可以加深对 Mapping 和索引理解。Mapping 其实是描述了索引的设置和各个字段的属性,但并没有太限制用户输入的 _source 是否严格与字段类型相匹配(默认的情况下)。

最后,如果你对 ES 感兴趣,欢迎订阅我的《Elasticsearch 从入门到实践》小册。

上一篇: java - 如何查询已经执行过的流程信息?_个人文章 下一篇: 手机怎么远程登录云服务器?