元数据

元数据:_source 字段

默认情况下,Elasticsearch 用 JSON 字符串来表示文档主体保存在 _source 字段中。像其他保存的字段一样,_source 字段也会在写入硬盘前压缩。这几乎始终是需要的功能,因为:

  • 搜索结果中能得到完整的文档 —— 不需要额外去别的数据源中查询文档
  • 如果缺少 _source 字段,部分 更新 请求不会起作用
  • 当你的映射有变化,而且你需要重新索引数据时,你可以直接在 Elasticsearch 中操作而不需要重新从别的数据源中取回数据。
  • 你可以从 _source 中通过 getsearch 请求取回部分字段,而不是整个文档。
  • 这样更容易排查错误,因为你可以准确的看到每个文档中包含的内容,而不是只能从一堆 ID 中猜测他们的内容。

即便如此,存储 _source 字段还是要占用硬盘空间的。假如上面的理由对你来说不重要,你可以用下面的映射禁用 _source 字段:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "_source": {
                "enabled":  false
            }
        }
    }
}

在搜索请求中你可以通过限定 _source 字段来请求指定字段:

GET /_search
{
    "query":   { "match_all": {}},
    "_source": [ "title", "created" ]
}

这些字段会从 _source 中提取出来,而不是返回整个 _source 字段。

储存字段

除了索引字段的值,你也可以选择 储存 字段的原始值以备日后取回。使用 Lucene 做后端的用户用储存字段 来选择搜索结果的返回值,事实上,_source 字段就是一个储存字段。

在 Elasticsearch 中,单独设置储存字段不是一个好做法。完整的文档已经被保存在 _source 字段中。通常最好的办法会是使用 _source 参数来过滤你需要的字段。

元数据:_all 字段

在【简单搜索】中,我们介绍了 _all 字段:一个所有其他字段值的特殊字符串字段。query_string 在没有指定字段时默认用 _all 字段查询。_all 字段在新应用的探索阶段比较管用,当你还不清楚最终文档的结构时,可以将任何查询用于这个字段,就有机会得到你想要的文档:

GET /_search
{
    "match": {
        "_all": "john smith marketing"
    }
}

随着你应用的发展,搜索需求会变得更加精准。你会越来越少的使用 _all 字段。_all 是一种简单粗暴的搜索方式。通过查询独立的字段,你能更灵活,强大和精准的控制搜索结果,提高相关性。

提示

【相关性算法】考虑的一个最重要的原则是字段的长度:字段越短,就越重要。在较短的 title 字段中的短语会比较长的 content 字段中的短语显得更重要。而字段间的这种差异在 _all 字段中就不会出现

如果你决定不再使用 _all 字段,你可以通过下面的映射禁用它:

PUT /my_index/_mapping/my_type
{
    "my_type": {
        "_all": { "enabled": false }
    }
}

通过 include_in_all 选项可以控制字段是否要被包含在 _all 字段中,默认值是 true。在一个对象上设置 include_in_all 可以修改这个对象所有字段的默认行为。你可能想要保留 _all 字段来查询所有特定的全文字段,例如 title, overview, summarytags。相对于完全禁用 _all 字段,你可以先默认禁用 include_in_all 选项,而选定字段上启用 include_in_all

PUT /my_index/my_type/_mapping
{
    "my_type": {
        "include_in_all": false,
        "properties": {
            "title": {
                "type":           "string",
                "include_in_all": true
            },
            ...
        }
    }
}

谨记 _all 字段仅仅是一个经过分析的 string 字段。它使用默认的分析器来分析它的值,而不管这值本来所在的字段指定的分析器。而且像所有 string 类型字段一样,你可以配置 _all 字段使用的分析器:

PUT /my_index/my_type/_mapping
{
    "my_type": {
        "_all": { "analyzer": "whitespace" }
    }
}

文档 ID

文档唯一标识由四个元数据字段组成:

  • _id:文档的字符串 ID
  • _type:文档的类型名
  • _index:文档所在的索引
  • _uid_type_id 连接成的 type#id

默认情况下,_uid 是被保存(可取回)和索引(可搜索)的。_type 字段被索引但是没有保存,_id_index 字段则既没有索引也没有储存,它们并不是真实存在的。尽管如此,你仍然可以像真实字段一样查询 _id 字段。Elasticsearch 使用 _uid 字段来追溯 _id。虽然你可以修改这些字段的 indexstore 设置,但是基本上不需要这么做。

  • _id 字段有一个你可能用得到的设置:path 设置告诉 Elasticsearch 它需要从文档本身的哪个字段中生成 _id
    PUT /my_index
    {
        "mappings": {
            "my_type": {
                "_id": {
                    "path": "doc_id" <1>
                },
                "properties": {
                    "doc_id": {
                        "type":   "string",
                        "index":  "not_analyzed"
                    }
                }
            }
        }
    }
    
  • <1> 从 doc_id 字段生成 _id
  • 然后,当你索引一个文档时:
    POST /my_index/my_type
    {
        "doc_id": "123"
    }
    
  • _id 值由文档主体的 doc_id 字段生成。
    {
        "_index":   "my_index",
        "_type":    "my_type",
        "_id":      "123", <1>
        "_version": 1,
        "created":  true
    }
    
  • <1> _id 正确的生成了。

警告:虽然这样很方便,但是注意它对 bulk 请求(见【bulk 格式】)有个轻微的性能影响。处理请求的节点将不能仅靠解析元数据行来决定将请求分配给哪一个分片,而需要解析整个文档主体。