Elasticsearch 数值类型也能存String 类型,有点意思

| 2019-05-17

一、前言|
   最近经常遇到遇到某个客户问数值类型的字段也能存字符串,或者说已经将字段类型设置成了float,但是实际存储的仍然是字符串,该如何解决,今天花点时间我们来梳理整个流程。
 
二、实际演练
1,定义一个索引mapping,并指定类型为float.单精度浮点型
PUT nginxindex
{
  "mappings": {
    "properties": {
      "price":{
        "type": "float"
      }
    }
  }
}
2,写入几个文档,看看效果
POST nginxindex/_doc
{
  "price":4.68        //数字类型
}

POST nginxindex/_doc
{
  "price": "4.69"      //字符串类型
}

POST nginxindex/_doc
{
  "price": "free for charge"    //字符串类型
}

3,对比结果

第一个,正常写入,有返回,

第二个,正常写入,有返回,

第三个,无法写入,报错提示。提示如下图所示

                                                                                                        第三个报错
这个报错大概就是无法解析字符串内容到floa类型的type.这个比较容易理解,但是第二个文档那个字符串类型数字又能写入,这又是为何?那么这也是本节要关注的内容,也是前面客户需要解决的问题:
 
 
三、问题原因
用户在存储字符串形式的数字,无论是那种数字类型,都能默认识别并存储,那么最终客户在终端搜索的时候会出现很多返回结果数量不一致的问题。这个是什么原因呢?我们看一下官网,翻译如下:
 
   数据并不总是干净的。根据它的生成方式,一个数字可能在JSON体中呈现为一个真正的JSON数字,例如。5,但它也可能呈现为一个字符串,例如。“5”。或者,一个应该是整数的数字可以呈现为浮点,例如5.0,甚至“5.0”。
 
需要配置强制程序来清理脏值,以适应字段的数据类型。具体参考如下链接:
 
https://www.elastic.co/guide/en/elasticsearch/reference/current/coerce.html#coerce
 
解决方案:就是在索引的mapping字段属性定义里,加一个coerce参数,并将其值设置为false.默认为true.
四、再次实践
PUT nginxnewindex
{
  "mappings": {
    "properties": {
      "price":{
        "type": "float",
        "coerce": false        //定义mapping
      }
    }
  }
}

POST nginxnewindex/_doc        //写入几个文档
{
  "price":4.68
}

POST nginxnewindex/_doc      //写入报错,
{
  "price": "4.69"
}

结果会发现,第三个文档,写入报错,截图如下:

  字段解析错误

错误提示也是说你定义一个float类型的字段,但是写入的是字符串。
 
那么这样的话,用户就能第一时间发现写入报错信息,及时扭转前端写入格式,以防后续影响业务了。那么这就是这个严格匹配参数的作用。
 
五、实际生产环境中,如何平滑解决用户字段类型错误?
  mapping字段类型一旦定义,就不能再修改。那么实际用户生产环境,新数据可以通过修改新索引mapping参数解决。那么对于存量的索引数据,如何实现平滑更改呢。没错,大家想到的是reindex.再造索引。还是以本文前面报错的索引为列,如何实现字符串类型的float转换为纯float数字类型。
 

 

先看源索引的文档类型,price是字符串类型,后面要实现更改为float.

1,先创建一个目标索引,指定mapping参数
PUT nginxnewindex2
{
  "mappings": {
    "properties": {
      "price":{
        "type": "float",
        "coerce": false
      }
    }
  }
}
2, 执行reindex拷贝,这里有问题,注意
POST _reindex
{
  "source": {
    "index": "nginxindex"
  },
  "dest": {
    "index": "nginxnewindex2"
  }
}

返回报错如下:

                                                                                         reindex索引报错
这个错误大家都很明白了,就是写入的是字符串,但是实际目标存储的字段类型是数字类型,解析失败。reindex也中断。
 
因此,reindex在某些场景下是有限制的。两个索引mapping不一致的问题,会导致索引同步失败。
 
那么有没有一种办法,将存量索引的字段类型进行更改,然后再拷贝到目标索引呢? 答案是有的,这里要用到pipeline,管道预处理。就是在reindex拷贝之前,将源索引的字段类型先进行更改,然后再写入目标索引。
 
六、Reindex+pipeline预处理方式实现存量索引字段类型的更改并拷贝
需求:实现reindex的时候,转化源索引格式到float.
 
第一步,创建一个processors
PUT _ingest/pipeline/my-pipeline-id
{
  "description": "converts the content of the price field to an float",
  "processors" : [
    {
      "convert" : {
        "field" : "price",
        "type": "float"
      }
    }
  ]
}
第二步:创建一个目标索引
PUT nginxnewindex2
{
  "mappings": {
    "properties": {
      "price":{
        "type": "float",
        "coerce": false
      }
    }
  }
}
第三步:执行reindex+pipeline 
POST _reindex
{
  "source": {
    "index": "nginxindex"    //源索引,要改造的索引
  },
  "dest": {
    "index": "nginxnewindex2",
    "pipeline": "my-pipeline-id"
  }
}
返回如下:没有报错,执行成功。

                                                                                                                  返回成功
那么我们看看目标拷贝的索引的mapping,看看新的字段类型是否变成强float了。如下:更改成功。
 
那么至此,彻底解决客户这个问题。问题整理完毕。以后两个字段类型不一样需要转格式,大家也可以这么操作。
 
七、总结
     本文从一个实际生产案列出发,分析剖解解决了一个字段精确值的问题,以及平滑解决存量索引字段类型转换的问题,因该方法比较经典,特梳理整理了一下,希望能帮到有需求的人们。

编辑:航网科技 来源:腾讯云 本文版权归原作者所有 转载请注明出处

在线客服

微信扫一扫咨询客服


全国免费服务热线
0755-36300002

返回顶部