【Elastic Engineering】Elasticsearch:mapping 定制

作者:刘晓国


Elasticsearch 依据我们提供的数据,根据自己的估计,并自动生成相应的 mapping。这在很多的时候是非常有用的。 它可以帮我们自动省很多的手动操作,而且在大多数的情况下,Elasticsearch 帮我们自动生成的 mapping 也是有效的。一般来讲,如果我们想自己定义自己的 mapping 的话,如下的步骤将是可取的,也是推荐的方法:


1.把自己的一个数据输入到 Elasticsearch 中

2.得到上面输入数据的 mapping,并在此基础上进行调整,从而得出适合自己数据的 mapping


在下面我们以几个例子来展示几个我们需要调整的地方。


调整数据类型


我们首先在我们的 Kibana 中输入如下的一个命令:

PUT myindex/_doc/1
{
  "status_code": 404
}

上面创建一个叫做 myindex 的索引,它里面含有一个我们通常见到的 HTTP 请求的 status_code。在这里我们的文档的值为 404。我们通过如下的命令来显示这个 myindex 的mapping。请记住这个 mapping 是在第一次数据输入时,根据我们输入的数据猜出来的:

{
  "myindex" : {
    "mappings" : {
      "properties" : {
        "status_code" : {
          "type" : "long"
        }
      }
    }
  }
}

从上面的结果中,我们可以看出来 type 为 long,意味着这将是一个 64 bit 的数据长度。我们知道 HTTP 的 status_code,通常就是小于 1000 的数据。这样一个 64 bit 的数据显然浪费存储空间。如果我们的数据是很少,这个可能并不算是什么,但是如果我们有海量的数据,那么这个存储空间的浪费将是很大的。为此,我们可以把这个数据类型修改为 short类型的, 也就是 16 bit:

PUT myindex1
{
  "mappings": {
    "properties": {
      "status_code": {
        "type": "short"
      }
    }
  }
}

在上面,我们创建的 myindex1 的 mapping 里定义 status_code 为 short 类型。


如果你之前看过我的另外一篇文章 “开始使用Elasticsearch (2)”,我们在那里也调整一个 geo_point 的数据类型。比如:

PUT twitter/_doc/1
{
  "user": "zhangsan",
  "location": {
    "lat": "39.970718",
    "lon": "116.325747"
  }
}

我们看看 Elasticsearch 帮我们生产的数据类型:

GET twitter/_mapping
{
  "twitter" : {
    "mappings" : {
      "properties" : {
        "location" : {
          "properties" : {
            "lat" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "lon" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        },
        "user" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

显然,在上面我们可以看到 lat 及 lon 都被估算为 text 类型,而且是一个 multi-field 的数据类型,这显然不是我们所需要的。另外,我们的 user 类型,我们并不想它是一个 text 类型的。那么我们可以作如下的调整:

PUT twitter1
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      },
      "user": {
        "type": "keyword"
      }
    }
  }
}

在上面的 twitter1 中,我们可以看出来调整后的 location 类型为 geo_point 的数据类型,而 user 也变成了我们所希望的 keyword 类型。


动态的 mapping 并不总是优化的


针对一个浮点数来说:

PUT my_index/_doc/1
{
  "price": 1.99
}

我们可以得到它的 mapping:

GET my_index/_mapping
{
  "my_index" : {
    "mappings" : {
      "properties" : {
        "price" : {
          "type" : "float"
        }
      }
    }
  }
}

从上面我们可以看出来,price 的数据类型是一个 float 类型。对于大多数的情况来说,这个应该没有问题。但是在实际的应用中,我们可以把这个 float 数据类型转换为 scaled float 数据类型。Scaled float 由 long 数据类型来支持。long 数据类型在 Lucene 里可以被更加有效地压缩,从而节省存储的空间。在使用 scaled float 数据类型时,你必须使用scaling_factor 来配置它的精度:

PUT my_index1/_doc/1
{
  "mappings": {
    "properties": {
      "price": {
        "type": "scaled_float",
        "scaling_factor": 100
      }
    }
  }
}

在上面我们定义 price 类型为 scaled_float 数据类型,并定义 scaling_factor 为 100。这样我们的数据,比如 1.99 刚好可以在乘以 100 变为 199,从而成为一个整型数。


经过这样的改造后,我们可以试试重新在 my_index1 里输入一个文档:

PUT my_index1/_doc/1
{
  "price": 1.99
}

我们通过如下的方法来查询这个数据:

GET my_index1/_doc/1

返回的结果是:

{
  "_index" : "my_index1",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "price" : 1.99
  }
}

从上面我们可以看出来,尽管我们修改了我们的数据类型,但是我们返回的数据还是对的。


上一篇:MyEclipse安装后需要进行的配置


下一篇:【Elastic Engineering】Kibana:如何在 Linux,MacOS 及 Windows 上安装 Elastic 栈中的 Kibana