Elasticsearch教程之Mapping

3,224 阅读19分钟

映射是定义文档及其包含的字段如何存储和索引的过程。例如,使用映射来定义:

  • 哪些字符串字段应该被视为全文字段。
  • 哪些字段包含数字、日期或地理位置。
  • 日期值的格式。
  • 自定义规则来控制动态添加字段的映射。

一个映射定义:

  • Meta-fields:元字段用于自定义如何处理文档的相关元数据。 元字段的示例包括文档的_index_id_source字段。
  • Fields or properties:映射包含与文档相关的字段或属性的列表。

在7.0.0之前,映射定义曾经包含类型名称。

字段的数据类型

每个字段都有一个数据类型,可以是以下类型:

  • 简单类型如text, keyword, date, long, double, boolean, ip.
  • 一种支持JSON的分层特性的类型,例如objectnested
  • 或特殊的类型,例如geo_pointgeo_shapecompletion

为不同的目的以不同的方式为同一字段建立索引通常很有用。例如,string字段可以作为全文搜索的text字段建立索引,也可以作为排序或聚合的keyword字段建立索引。或者,您可以使用标准分析器、英语分析器和法语分析器来索引字符串字段。

这是多字段的目的。 大多数数据类型通过fields参数支持多字段。

阻止mapping激增的设置

在索引中定义太多字段的情况可能导致映射激增,从而可能导致内存不足错误和难以恢复的情况。这个问题可能比预期的更普遍。例如,考虑一种情况,其中插入的每个新文档都引入了新字段。 这在动态映射中很常见。每当文档包含新字段时,这些字段最终都会出现在索引的映射中。 不必担心数据量少,但是随着映射的增长,可能会成为问题。以下设置允许您限制可以手动或动态创建的字段映射的数量,以防止不良文档引起映射爆炸:

  • index.mapping.total_fields.limit

索引中字段的最大数目。字段和对象映射以及字段别名都属于此限制。默认值是1000。

该限制已到位,以防止映射和搜索变得太大。 较高的值可能导致性能下降和内存问题,尤其是在负载较高或资源很少的群集中。 如果增加此设置,我们建议您还增加`index.query.bool.max_clause_count`设置,该设置将限制查询中布尔型子句的最大数量。

  • index.mapping.depth.limit

field 的最大深度,以内部对象的数量来衡量。例如,如果所有字段都在根对象级别定义,那么深度为1,如果存在一个对象映射,则深度为2,依此类推。默认值为20。

  • index.mapping.nested_fields.limit 索引中不同嵌套映射的最大数目,默认为50。
  • index.mapping.nested_objects.limit 单个文档中所有嵌套类型中嵌套JSON对象的最大数量,默认为10000。
  • index.mapping.field_name_length.limit 设置字段名称的最大长度。 默认值为Long.MAX_VALUE(无限制)。此设置实际上不能解决映射爆炸问题,但是如果您想限制字段长度,该设置可能仍然有用。通常不需要设置此设置。 除非用户开始添加大量名称很长的字段,否则默认设置是可以的。

动态Mapping

字段和映射类型在使用之前不需要定义。通过动态映射,仅通过索引文档即可自动添加新的字段名称。新字段既可以添加到顶级映射类型,也可以添加到内部对象和嵌套字段.

可以将动态映射规则配置为自定义用于新字段的映射。

显式的映射

您对数据的了解超出了Elasticsearch的猜测,因此尽管动态映射对于入门非常有用,但有时您仍需要指定自己的显式映射。

你可以创建字段映射,当你创建一个索引或者在已经存在的索引中新增字段

  • 创建索引的显示映射

您可以使用create index API创建带有显式映射的新索引。

curl -X PUT "localhost:9200/my-index?pretty" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}
'
  • 在已存映射中新增字段

您可以使用put mapping API向现有索引添加一个或多个新字段。下面的示例添加了employee-id,这是一个keyword字段,其索引映射参数值为false。这意味着存储了employee-id字段的值,但是不能进行索引,也不能用于搜索。

curl -X PUT "localhost:9200/my-index/_mapping?pretty" -H 'Content-Type: application/json' -d'
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}
'

更新字段的mapping

除了支持的映射参数外,您无法更改现有字段的映射或字段类型。 更改现有字段可能会使已经建立索引的数据无效。

如果需要更改字段的映射,请使用正确的映射创建一个新索引,然后将数据重新索引到该索引中。

重命名字段会使在旧字段名称下已建立索引的数据无效。 而是添加一个别名字段以创建备用字段名称。

查看索引的映射

您可以使用get mapping API来查看现有索引的映射。

curl -X GET "localhost:9200/my-index/_mapping?pretty"

API返回以下响应:

{
  "my-index" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "employee-id" : {
          "type" : "keyword",
          "index" : false
        },
        "name" : {
          "type" : "text"
        }
      }
    }
  }
}

查看特定字段的映射

如果您只想查看一个或多个特定字段的映射,则可以使用get字段映射API。

如果您不需要索引的完整映射或索引包含大量字段,这将很有用。

随后的请求将检索employee-id字段的映射。

curl -X GET "localhost:9200/my-index/_mapping/field/employee-id?pretty"

API返回以下响应:

{
  "my-index" : {
    "mappings" : {
      "employee-id" : {
        "full_name" : "employee-id",
        "mapping" : {
          "employee-id" : {
            "type" : "keyword",
            "index" : false
          }
        }
      }
    }
  }
}

删除映射类型

在Elasticsearch 7.0.0或更高版本中创建的索引不再接受_default_映射。在6.x中创建的索引将继续像在Elasticsearch 6.x中一样运行。在7.0的API中不推荐使用类型,其中包括对索引创建,放置映射,获取映射,放置模板,获取模板和获取字段映射API的重大更改。

mapping 类型是什么

从Elasticsearch的第一版开始,每个文档都存储在单个索引中,并分配了单个映射类型。映射类型用于表示要建立索引的文档或实体的类型,例如,twitter索引可能具有user类型和tweet类型。

每种映射类型都可以有自己的字段.因此,user类型可能有一个full_name字段、一个user_name字段和一个email字段.而tweet类型可以具有一个content字段,一个tweeted_at字段以及一个类似于user类型的user_name字段。

每个文档都有一个_type元字段,其中包含类型名称,通过在URL中指定类型名称,可以将搜索限制为一种或多种类型:

GET twitter/user,tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

_type字段与文档的_id组合在一起以生成_uid字段,因此具有相同_id的不同类型的文档可以存在于单个索引中。

映射类型还用于在文档之间建立父子关系,因此question类型的文档可以是answer类型文档的父级。

为什么要移除mapping type?

最初,我们谈到“索引”类似于SQL数据库中的“数据库”,而“类型”等同于“表”。

这是一个不好的类比,导致了错误的假设。 在SQL数据库中,表彼此独立。 一个表中的列与另一表中具有相同名称的列无关。 映射类型的字段不是这种情况。

在Elasticsearch索引中,在不同映射类型中具有相同名称的字段在内部由相同的Lucene字段支持。换句话说,使用上面的示例,user类型中的user_name字段存储在与tweet类型中的user_name字段完全相同的字段中,并且两个user_name字段在这两种类型中必须具有相同的映射(定义)。

例如,当您要删除同一类型的索引中的日期字段和另一类型的布尔值字段时,这可能会导致挫败感。

最重要的是,存储在同一索引中具有很少或没有相同字段的不同实体会导致数据稀疏并干扰Lucene有效压缩文档的能力。

基于这些原因,我们决定将映射类型的概念从Elasticsearch中移除。

Mapping type的替代品

第一种方法是为每个文档类型建立一个索引。而不是将tweetuser存储在单个twitter索引中.您可以在tweets索引中存储tweet,在user索引中存储用户。索引彼此完全独立,因此索引之间的字段类型不会发生冲突。

这种方法有两个好处:

  • 数据更可能是密集的,因此可以从Lucene中使用的压缩技术中受益。
  • 在全文搜索中用于评分的术语统计信息更可能是准确的,因为同一索引中的所有文档都代表一个实体。

每个索引的大小可以根据将包含的文档数量进行适当调整:您可以为user使用较少数量的主分片,并为tweet使用较大数量的主分片。

当然,一个集群中可以存在多少个主要分片是有限制的,因此您可能不想只花几千个文档就浪费整个分片。在这种情况下,您可以实现自己的自定义type字段,该字段的工作方式与旧_type相似.

让我们以上面的user/tweet示例为例。 最初,工作流看起来像这样:

PUT twitter
{
  "mappings": {
    "user": {
      "properties": {
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" }
      }
    },
    "tweet": {
      "properties": {
        "content": { "type": "text" },
        "user_name": { "type": "keyword" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

PUT twitter/user/kimchy
{
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/tweet/1
{
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

您可以通过添加自定义类型字段来实现相同的目的,如下所示:

PUT twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": { "type": "keyword" }, 
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" },
        "content": { "type": "text" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

PUT twitter/_doc/user-kimchy
{
  "type": "user", 
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/_doc/tweet-1
{
  "type": "tweet", 
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "user_name": "kimchy"
        }
      },
      "filter": {
        "match": {
          "type": "tweet" 
        }
      }
    }
  }
}

父/子没有mapping type

以前,通过将一种映射类型作为父级,而将一种或多种其他映射类型作为子级来表示父子关系。没有类型,我们将无法再使用此语法。除了表示文档之间关系的方式已更改为使用新的连接字段外,父子功能将继续像以前一样工作。

将多类型索引migrate 到单类型索引

Reindex API可用于将多类型索引转换为单类型索引。 以下示例可在Elasticsearch 5.6或Elasticsearch 6.x中使用。 在6.x中,无需指定index.mapping.single_type,因为这是默认设置。

第一个示例将我们的twitter索引分为tweets索引和users索引:

PUT users
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT tweets
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "user"
  },
  "dest": {
    "index": "users",
    "type": "_doc"
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet"
  },
  "dest": {
    "index": "tweets",
    "type": "_doc"
  }
}

下一个示例添加一个自定义type字段并将其设置为原始_type的值。 如果有任何不同类型的文档具有冲突的ID,它也会将类型添加到_id中:

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}


POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

7.0中的无类型接口

在Elasticsearch 7.0中,每个API将支持无类型请求,指定类型将产生弃用警告。

索引创建,索引模板和映射API支持新的include_type_name URL参数,该参数指定请求和响应中的映射定义是否应包含类型名称。 在6.8版中,该参数默认为true,以匹配7.0之前在映射中使用类型名称的行为。 它在7.0版中默认为false,在8.0版中将被删除。

应该在6.8中明确设置以准备升级到7.0。 为了避免6.8中的弃用警告,可以将参数设置为true或false。 在7.0中,完全设置include_type_name将导致弃用警告。

将此选项设置为false,请参见与Elasticsearch进行交互的一些示例:

curl -X PUT "localhost:9200/index?include_type_name=false&pretty" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": { 
      "foo": {
        "type": "keyword"
      }
    }
  }
}
'
curl -X PUT "localhost:9200/index/_mappings?include_type_name=false&pretty" -H 'Content-Type: application/json' -d'
{
  "properties": { 
    "bar": {
      "type": "text"
    }
  }
}
'

curl -X GET "localhost:9200/index/_mappings?include_type_name=false&pretty"
{
  "index": {
    "mappings": {
      "properties": { 
        "foo": {
          "type": "keyword"
        },
        "bar": {
          "type": "text"
        }
      }
    }
  }
}

在7.0中,必须使用{index}/_ doc路径调用索引API,以自动生成_id和具有明确ID的{index}/_doc/{id}

curl -X PUT "localhost:9200/index/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "foo": "baz"
}
'

{
  "_index": "index",
  "_id": "1",
  "_type": "_doc",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

同样,getdelete API使用路径{index}/_doc/{id}

curl -X GET "localhost:9200/index/_doc/1?pretty"

对于同时包含类型和终结点名称的API路径(如_update),在7.0中,终结点将紧随索引名称之后:

curl -X POST "localhost:9200/index/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
    "doc" : {
        "foo" : "qux"
    }
}
'
curl -X GET "localhost:9200/index/_source/1?pretty"

类型也不应再出现在请求正文中。 以下批量索引编制示例在URL和单个批量命令中都省略了该类型:

curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d'
{ "index" : { "_index" : "index", "_id" : "3" } }
{ "foo" : "baz" }
{ "index" : { "_index" : "index", "_id" : "4" } }
{ "foo" : "qux" }
'

调用诸如_search_msearch_explain之类的搜索API时,URL中不应包含类型。 此外,_type字段不应在查询,聚合或脚本中使用。

文档和搜索API将继续在响应中返回_type键,以避免中断响应解析。 但是,该键被认为已弃用,不应再被引用。 在8.0版中,类型将从响应中完全删除。

请注意,当使用不推荐使用的类型化API时,索引的映射类型将照常返回,但无类型API会在响应中返回虚拟类型_doc。 例如,即使映射具有像my_type这样的自定义类型名称,以下无类型的get调用也将始终返回_doc作为类型:

curl -X PUT "localhost:9200/index/my_type/1?pretty" -H 'Content-Type: application/json' -d'
{
  "foo": "baz"
}
'
curl -X GET "localhost:9200/index/_doc/1?pretty"


{
    "_index" : "index",
    "_type" : "_doc",
    "_id" : "1",
    "_version" : 1,
    "_seq_no" : 0,
    "_primary_term" : 1,
    "found": true,
    "_source" : {
        "foo" : "baz"
    }
}

建议通过将include_type_name设置为false来重新添加索引模板,使索引模板无类型。 在后台,无类型的模板在创建索引时将使用伪类型_doc

如果将无类型的模板与类型化的索引创建调用一起使用,或者将有类型的模板与无类型的索引创建调用一起使用,则仍将应用该模板,但是索引创建调用将确定是否应该有一个类型。例如,在下面的示例中,尽管index-1-01与无类型的模板匹配,但index-1-01将具有类型;尽管index-2-01与定义类型的模板匹配,但index-2-01将是无类型的。 index-1-01index-2-01都将从它们匹配的模板中继承foo字段.

curl -X PUT "localhost:9200/_template/template1?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns":[ "index-1-*" ],
  "mappings": {
    "properties": {
      "foo": {
        "type": "keyword"
      }
    }
  }
}
'
curl -X PUT "localhost:9200/_template/template2?include_type_name=true&pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns":[ "index-2-*" ],
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "keyword"
        }
      }
    }
  }
}
'
curl -X PUT "localhost:9200/index-1-01?include_type_name=true&pretty" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "type": {
      "properties": {
        "bar": {
          "type": "long"
        }
      }
    }
  }
}
'
curl -X PUT "localhost:9200/index-2-01?pretty" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": {
      "bar": {
        "type": "long"
      }
    }
  }
}
'

在创建隐式索引的情况下,由于文档在尚不存在的索引中建立索引,因此始终优先使用模板。 由于无类型索引调用对类型索引起作用,因此通常这不是问题。

混合版本的集群

在由6.8和7.0节点组成的集群中,应在诸如索引创建之类的索引API中指定参数include_type_name。 这是因为参数在6.8和7.0之间具有不同的默认值,因此相同的映射定义对两个节点版本均无效。

无类型文档API(例如bulkupdate)仅从7.0开始可用,不适用于6.8节点。 对于执行文档查找的无类型查询,例如terms,也是如此。

字段类型

Elasticsearch为文档中的字段支持多种不同的数据类型:

  • 核心数据类型:
    • 字符串型 : text 和keyword
    • 数值型:long, integer, short, byte, double, float, half_float, scaled_float
    • 日期:date
    • 日期纳秒:date_nanos
    • 布尔类型:boolean
    • 二进制型:binary
    • 范围型:integer_range, float_range, long_range, double_range, date_range
  • 复杂类型:
    • 对象:单个JSON对象
    • 嵌套:JSON对象数组
  • 地理类型:
    • Geo-point:纬度/经度
    • Geo-shape:多边形等复杂形状
  • 特殊类型:
    • IP:
    • Completion:完成以提供自动完成建议
    • Token count:token_count用来计算字符串中令牌的数量
    • mapper-murmur3:在索引时计算值的哈希并将其存储在索引中
    • mapper-annotated-text:索引包含特殊标记的文本(通常用于标识命名实体)
    • Percolator:接受来自query-dsl的查询
    • Join:为同一索引内的文档定义父/子关系
    • Rank feature:记录数字功能以提高查询时的点击率。
    • Rank features:记录数字功能以提高查询时的点击率。
    • Dense vector:记录浮点值的密集向量。
    • Sparse vector:记录浮点值的稀疏向量。
    • Search-as-you-type:为查询优化的文本字段,以实现按需输入的完成
    • Alias:为现有字段定义别名。
    • Flattened:允许将整个JSON对象索引为单个字段。
    • Shape:任意笛卡尔几何的形状。
    • Histogram:百分位数聚合的预聚合数值的直方图。
  • 数组类型: 在Elasticsearch中,数组不需要专用的字段数据类型。 默认情况下,任何字段都可以包含零个或多个值,但是,数组中的所有值都必须具有相同的数据类型。
  • 多字段类型:为不同的目的以不同的方式为同一字段建立索引通常很有用。 例如,可以将字符串字段映射为用于全文搜索的文本字段,并映射为用于排序或聚合的关键字字段。 另外,您可以使用标准分析仪,英语分析仪和法语分析仪为文本字段建立索引。这是多字段的目的。 大多数数据类型通过fields参数支持多字段。

下面详细介绍各个字段类型:

别名数据类型

别名映射为索引中的字段定义另一个名称。别名可用于替代搜索请求中的目标字段,以及选择其他api(如字段功能)。


PUT trips
{
  "mappings": {
    "properties": {
      "distance": {
        "type": "long"
      },
      "route_length_miles": {
        "type": "alias",
        "path": "distance" 
      },
      "transit_mode": {
        "type": "keyword"
      }
    }
  }
}

GET _search
{
  "query": {
    "range" : {
      "route_length_miles" : {
        "gte" : 39
      }
    }
  }
}

{
  "took" : 37,
  "timed_out" : false,
  "_shards" : {
    "total" : 15,
    "successful" : 15,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

搜索请求的几乎所有组件都接受字段别名。 特别是,别名可用于查询,聚合和排序字段,以及在请求docvalue_fieldsstored_fields,建议和突出显示时使用。访问字段值时,脚本还支持别名。

在搜索请求的某些部分以及请求字段功能时,可以提供字段通配符模式。在这些情况下,通配符模式除了具体字段外还将匹配字段别名:

curl -X GET "localhost:9200/trips/_field_caps?fields=route_*,transit_mode&pretty"

对别名的目标有一些限制:

  • 目标必须是具体字段,而不是对象或其他字段别名。
  • 在创建别名时,目标字段必须存在。
  • 如果定义了嵌套对象,则字段别名必须与其目标具有相同的嵌套作用域。

此外,字段别名只能有一个目标。 这意味着不可能使用字段别名在单个子句中查询多个目标字段。可以通过映射更新将别名更改为引用新目标。 一个已知的限制是,如果任何存储的渗滤器查询包含字段别名,它们仍将引用其原始目标。

不支持的APIs: 不支持写入字段别名:尝试在索引或更新请求中使用别名将导致失败。 同样,别名不能用作copy_to的目标或在多字段中。

由于文档源中不存在别名,因此在执行源筛选时不能使用别名。 例如,以下请求将为_source返回空结果:

curl -X GET "localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query" : {
    "match_all": {}
  },
  "_source": "route_length_miles"
}
'

当前,只有搜索和字段功能API会接受并解析字段别名。其他接受字段名的api(如术语向量)不能与字段别名一起使用。最后,一些查询(例如termsgeoshapemore_like_this)允许从索引文档中获取查询信息。 由于在获取文档时不支持字段别名,因此查询中指定查找路径的部分无法通过其别名引用字段。

数组

在Elasticsearch中,没有专用的数组数据类型。 默认情况下,任何字段都可以包含零个或多个值,但是,数组中的所有值都必须具有相同的数据类型。 例如:

  • 字符串数组:["one", "two"]
  • 整数数组:[1,2]
  • 数组:[1,[2,3]]等效于[1,2,3]
  • 对象数组:[{“ name”:“ Mary”,“ age”:12},{“ name”:“ John”,“ age”:10}]

对象数组无法按预期工作:无法独立于数组中的其他对象查询每个对象。如果需要这样做,那么应该使用嵌套数据类型而不是对象数据类型。

动态添加字段时,数组中的第一个值确定字段类型,所有后续值必须具有相同的数据类型,或者至少必须能够将后续值强制转换为相同的数据类型。

不支持混合使用数据类型的数组:[10,"some string"]

数组可以包含空值,这些空值要么被配置的null_value替换,要么被完全跳过。空数组[]被视为缺少字段-没有值的字段。

无需预先配置即可在文档中使用数组,即开即用地支持它们:

curl -X PUT "localhost:9200/my_index/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
  "message": "some arrays in this document...",
  "tags":  [ "elasticsearch", "wow" ], 
  "lists": [ 
    {
      "name": "prog_list",
      "description": "programming list"
    },
    {
      "name": "cool_list",
      "description": "cool stuff list"
    }
  ]
}
'
curl -X PUT "localhost:9200/my_index/_doc/2?pretty" -H 'Content-Type: application/json' -d'
{
  "message": "no arrays in this document...",
  "tags":  "elasticsearch",
  "lists": {
    "name": "prog_list",
    "description": "programming list"
  }
}
'
curl -X GET "localhost:9200/my_index/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "tags": "elasticsearch" 
    }
  }
}
'