Elasticsearch Synthetic _source

21 阅读9分钟

_source 字段包含索引时传入的原始 JSON 文档体。_source 字段本身不被索引(因此不可搜索),但会被存储,以便在执行获取请求(如 getsearch)时返回。

如果磁盘使用很重要,可以考虑以下选项:

  • 使用 synthetic _source,在检索时重建源内容,而不是存储在磁盘上。这样可以减少磁盘使用,但会导致 GetSearch 查询中访问 _source 变慢。
  • 完全禁用 _source 字段。这样可以减少磁盘使用,但会禁用依赖 _source 的功能。

什么是 synthetic _source?

当文档被索引时,有些字段,比如需要生成 doc_valuesstored fileds,来自 _source 的字段值会根据数据类型复制到独立的列表 doc_values 中(磁盘上的不同数据结构,用于模式匹配),这样可以独立搜索这些值。当在这些小列表中找到所需值后,返回原始文档。由于只搜索了小列表,而不是整个文档的所有字段值,搜索所需的时间会减少。虽然这种处理方式提升了速度,但会在小列表和原始文档中存储重复的数据。

Synthetic _source 是一种索引配置模式,可以改变文档在摄取时的处理方式,以节省存储空间并避免数据重复。它会创建独立的列表,但不会保留原始的原始文档。相反,在找到值后,会使用小列表中的数据重建 _source 内容。由于没有存储原始文档,仅在磁盘上存储 “列表”,可以节省大量存储空间。

`

1.  PUT idx
2.  {
3.    "settings": {
4.      "index": {
5.        "mapping": {
6.          "source": {
7.            "mode": "synthetic"
8.          }
9.        }
10.      }
11.    }
12.  }

`AI写代码

需要注意的是,由于 _source 值是在文档被检索时即时重建的,因此需要额外时间来完成重建。这会为用户节省存储空间,但会降低搜索速度。虽然这种即时重建通常比直接保存源文档并在查询时加载更慢,但它节省了大量存储空间。通过在不需要时不加载 _source 字段,可以避免额外的延迟。

支持的字段

Synthetic _source 支持所有字段类型。根据实现细节,不同字段类型在使用 synthetic _source 时具有不同属性。

大多数字段类型使用现有数据构建 synthetic _source,最常见的是 doc_valuesstored fields。对于这些字段类型,不需要额外空间来存储 _source 字段内容。由于 doc_values 的存储布局,生成的 _source 字段相比原始文档会有修改

对于其他所有字段类型,字段的原始值会按原样存储,方式与非 synthetic 模式下的 _source 字段相同。这种情况下不会有修改,_source 中的字段数据与原始文档相同。同样,使用 ignore_malformedignore_above 的字段的格式错误值也需要按原样存储。这种方式存储效率较低,因为为 _source 重建所需的数据除了索引字段所需的其他数据(如 doc_values)外,还会额外存储。

Synthetic _source 限制

某些字段类型有额外限制,这些限制记录在字段类型文档的 synthetic _source 部分。

Synthetic _source 不支持仅存储源的快照仓库。要存储使用 synthetic _source 的索引,请选择其他类型的仓库。

Synthetic _source 修改

启用 synthetic _source 时,检索到的文档相比原始 JSON 会有一些修改。

数组被移动到叶子字段

Synthetic _source 中的数组会被移动到叶子字段。例如:

由于 _source 值是通过 “doc values” 列表中的值重建的,因此原始 JSON 会被做一些修改。例如,数组会被移到叶子节点。

`

1.  PUT idx/_doc/1
2.  {
3.    "foo": [
4.      {
5.        "bar": 1
6.      },
7.      {
8.        "bar": 2
9.      }
10.    ]
11.  }

`AI写代码

将变为:

`

1.  {
2.    "foo": {
3.      "bar": [1, 2]
4.    }
5.  }

`AI写代码

这可能导致某些数组消失:

`

1.  PUT idx/_doc/1
2.  {
3.    "foo": [
4.      {
5.        "bar": 1
6.      },
7.      {
8.        "baz": 2
9.      }
10.    ]
11.  }

`AI写代码

将变为:

`

1.  {
2.    "foo": {
3.      "bar": 1,
4.      "baz": 2
5.    }
6.  }

`AI写代码

字段名称与映射一致

Synthetic _source 使用映射中字段的原始名称。当与动态映射一起使用时,字段名中带点(.)的字段默认被解释为多个对象,而在禁用子对象的对象中,字段名中的点会被保留。例如:

`

1.  PUT idx/_doc/1
2.  {
3.    "foo.bar.baz": 1
4.  }

`AI写代码

将变为:

`

1.  {
2.    "foo": {
3.      "bar": {
4.        "baz": 1
5.      }
6.    }
7.  }

`AI写代码

如何将索引配置为 synthetic _source 模式

测试代码:在此测试中,将 synthetic _source 模式下的索引与标准索引进行对比。

`

1.  PUT index
2.  {
3.    "settings": {
4.      "index": {
5.        "mapping": {
6.          "source": {
7.            "mode": "synthetic"
8.          }
9.        }
10.      }
11.    }
12.  }

`AI写代码

测试

标准索引使用 multi-field 来说明如何通过全文搜索和聚合检索文档,并在 _source 内容中包含已禁用字段的值。

`

1.  PUT test_standard
2.  {
3.    "mappings": {
4.      "properties": {
5.        "disabled_field": {
6.          "enabled": false
7.        },
8.        "multi_field": {
9.          "type": "text",
10.          "fields": {
11.            "keyword": {
12.              "type": "keyword"
13.            }
14.          }
15.        }
16.      }
17.    }
18.  }

`AI写代码

让我们导入一些示例文档:

`

1.  PUT test_standard/_doc/1
2.  {
3.    "multi_field": "Host_01",
4.    "disabled_field" : "Required for storage 01"
5.  }

7.  PUT test_standard/_doc/2
8.  {
9.    "multi_field": "Host_02",
10.    "disabled_field" : "Required for storage 02"
11.  }

13.  PUT test_standard/_doc/3
14.  {
15.    "multi_field": "Host_03",
16.    "disabled_field" : "Required for storage 03"
17.  }

`AI写代码

全文搜索会检索带有 _source 内容的文档:

`

1.  GET test_standard/_search
2.  {
3.    "query": {
4.      "match": {
5.        "multi_field": "host_01"
6.      }
7.    }
8.  }

`AI写代码

结果:文档通过对已分析的字段进行全文搜索被检索到。返回的结果包含 _source 中的所有值,包括已被禁用的字段:

`

1.  {
2.    "took": 17,
3.    "timed_out": false,
4.    "_shards": {
5.      "total": 1,
6.      "successful": 1,
7.      "skipped": 0,
8.      "failed": 0
9.    },
10.    "hits": {
11.      "total": {
12.        "value": 1,
13.        "relation": "eq"
14.      },
15.      "max_score": 0.9808291,
16.      "hits": [
17.        {
18.          "_index": "test_standard",
19.          "_id": "1",
20.          "_score": 0.9808291,
21.          "_source": {
22.            "multi_field": "Host_01",
23.            "disabled_field": "Required for storage 01"
24.          }
25.        }
26.      ]
27.    }
28.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)AI写代码

这里,synthetic _source 模式下的索引使用 multi-fields 来说明 “text” 数据类型如何用于 “doc values” 列表,以及禁用字段中的值如何不可用。

`

1.  PUT test_synthetic
2.  {
3.    "settings": {
4.      "index": {
5.        "mapping": {
6.          "source": {
7.            "mode": "synthetic"
8.          }
9.        }
10.      }
11.    },
12.    "mappings": {
13.      "properties": {
14.        "keyword_field": {
15.          "type": "keyword"
16.        },
17.        "multi_field": {
18.          "type": "text",
19.          "fields": {
20.            "keyword": {
21.              "type": "keyword"
22.            }
23.          }
24.        },
25.        "text_field": {
26.          "type": "text"
27.        },
28.        "disabled_field": {
29.          "enabled": false
30.        },
31.        "skill_array_field": {
32.          "properties": {
33.            "language": {
34.              "type": "text"
35.            },
36.            "level": {
37.              "type": "text"
38.            }
39.          }
40.        }
41.      }
42.    }
43.  }

`AI写代码

让我们导入一些示例文档:

`

1.  PUT test_synthetic/_doc/1
2.  {
3.    "keyword_field": "Host_01",
4.    "disabled_field": "Required for storage 01",
5.    "multi_field": "Some info about computer 1",
6.    "text_field": "This is a text field 1",
7.    "skills_array_field": [
8.      {
9.        "language": "ruby",
10.        "level": "expert"
11.      },
12.      {
13.        "language": "javascript",
14.        "level": "beginner"
15.      }
16.    ],
17.    "foo": [
18.      {
19.        "bar": 1
20.      },
21.      {
22.        "bar": 2
23.      }
24.    ],
25.    "foo1.bar.baz": 1
26.  }

28.  PUT test_synthetic/_doc/2
29.  {
30.    "keyword_field": "Host_02",
31.    "disabled_field": "Required for storage 02",
32.    "multi_field": "Some info about computer 2",
33.    "text_field": "This is a text field 2",
34.    "skills_array_field": [
35.      {
36.        "language": "C",
37.        "level": "guru"
38.      },
39.      {
40.        "language": "javascript",
41.        "level": "beginner"
42.      }
43.    ],
44.    "foo": [
45.      {
46.        "bar": 1
47.      },
48.      {
49.        "bar": 2
50.      }
51.    ],
52.    "foo1.bar.baz": 2
53.  }

55.  PUT test_synthetic/_doc/3
56.  {
57.    "keyword_field": "Host_03",
58.    "disabled_field": "Required for storage 03",
59.    "multi_field": "Some info about computer 3",
60.    "text_field": "This is a text field 3",
61.    "skills_array_field": [
62.      {
63.        "language": "golang",
64.        "level": "beginner"
65.      }
66.    ],
67.    "foo": [
68.      {
69.        "bar": 1
70.      },
71.      {
72.        "bar": 2
73.      }
74.    ],
75.    "foo1.bar.baz": 3
76.  }

`AI写代码

搜索 “keyword” 数据类型时需要精确匹配。另外,禁用字段中的值也不再可用。

`

1.  GET test_synthetic/_search
2.  {
3.    "query": {
4.      "match": {
5.        "keyword_field": "Host_01"
6.      }
7.    }
8.  }

`AI写代码

响应**:**

`

1.  {
2.    "took": 1,
3.    "timed_out": false,
4.    "_shards": {
5.      "total": 1,
6.      "successful": 1,
7.      "skipped": 0,
8.      "failed": 0
9.    },
10.    "hits": {
11.      "total": {
12.        "value": 1,
13.        "relation": "eq"
14.      },
15.      "max_score": 0.9808291,
16.      "hits": [
17.        {
18.          "_index": "test_synthetic",
19.          "_id": "1",
20.          "_score": 0.9808291,
21.          "_source": {
22.            "keyword_field": "Host_01",
23.            "disabled_field": "Required for storage 01",
24.            "multi_field": "Some info about computer 1",
25.            "text_field": "This is a text field 1",
26.            "skills_array_field": [
27.              {
28.                "language": "ruby",
29.                "level": "expert"
30.              },
31.              {
32.                "language": "javascript",
33.                "level": "beginner"
34.              }
35.            ],
36.            "foo": [
37.              {
38.                "bar": 1
39.              },
40.              {
41.                "bar": 2
42.              }
43.            ],
44.            "foo1.bar.baz": 1
45.          }
46.        }
47.      ]
48.    }
49.  }

`AI写代码

我们再做一次搜索:

`

1.  GET test_synthetic/_search
2.  {
3.    "query": {
4.      "match": {
5.        "multi_field": "info"
6.      }
7.    }
8.  }

`AI写代码

响应是:

`

1.  {
2.    "took": 1,
3.    "timed_out": false,
4.    "_shards": {
5.      "total": 1,
6.      "successful": 1,
7.      "skipped": 0,
8.      "failed": 0
9.    },
10.    "hits": {
11.      "total": {
12.        "value": 3,
13.        "relation": "eq"
14.      },
15.      "max_score": 0.13353139,
16.      "hits": [
17.        {
18.          "_index": "test_synthetic",
19.          "_id": "2",
20.          "_score": 0.13353139,
21.          "_source": {
22.            "keyword_field": "Host_02",
23.            "disabled_field": "Required for storage 02",
24.            "multi_field": "Some info about computer 2",
25.            "text_field": "This is a text field 2",
26.            "skills_array_field": [
27.              {
28.                "language": "C",
29.                "level": "guru"
30.              },
31.              {
32.                "language": "javascript",
33.                "level": "beginner"
34.              }
35.            ],
36.            "foo": [
37.              {
38.                "bar": 1
39.              },
40.              {
41.                "bar": 2
42.              }
43.            ],
44.            "foo1.bar.baz": 2
45.          }
46.        },
47.        {
48.          "_index": "test_synthetic",
49.          "_id": "3",
50.          "_score": 0.13353139,
51.          "_source": {
52.            "keyword_field": "Host_03",
53.            "disabled_field": "Required for storage 03",
54.            "multi_field": "Some info about computer 3",
55.            "text_field": "This is a text field 3",
56.            "skills_array_field": [
57.              {
58.                "language": "golang",
59.                "level": "beginner"
60.              }
61.            ],
62.            "foo": [
63.              {
64.                "bar": 1
65.              },
66.              {
67.                "bar": 2
68.              }
69.            ],
70.            "foo1.bar.baz": 3
71.          }
72.        },
73.        {
74.          "_index": "test_synthetic",
75.          "_id": "1",
76.          "_score": 0.13353139,
77.          "_source": {
78.            "keyword_field": "Host_01",
79.            "disabled_field": "Required for storage 01",
80.            "multi_field": "Some info about computer 1",
81.            "text_field": "This is a text field 1",
82.            "skills_array_field": [
83.              {
84.                "language": "ruby",
85.                "level": "expert"
86.              },
87.              {
88.                "language": "javascript",
89.                "level": "beginner"
90.              }
91.            ],
92.            "foo": [
93.              {
94.                "bar": 1
95.              },
96.              {
97.                "bar": 2
98.              }
99.            ],
100.            "foo1.bar.baz": 1
101.          }
102.        }
103.      ]
104.    }
105.  }

`AI写代码

更多阅读,请参考官方文档:_source field | Elastic Documentation