_source 字段包含索引时传入的原始 JSON 文档体。_source 字段本身不被索引(因此不可搜索),但会被存储,以便在执行获取请求(如 get 或 search)时返回。
如果磁盘使用很重要,可以考虑以下选项:
- 使用 synthetic _source,在检索时重建源内容,而不是存储在磁盘上。这样可以减少磁盘使用,但会导致 Get 和 Search 查询中访问 _source 变慢。
- 完全禁用 _source 字段。这样可以减少磁盘使用,但会禁用依赖 _source 的功能。
什么是 synthetic _source?
当文档被索引时,有些字段,比如需要生成 doc_values 或stored 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_values 和 stored fields。对于这些字段类型,不需要额外空间来存储 _source 字段内容。由于 doc_values 的存储布局,生成的 _source 字段相比原始文档会有修改。
对于其他所有字段类型,字段的原始值会按原样存储,方式与非 synthetic 模式下的 _source 字段相同。这种情况下不会有修改,_source 中的字段数据与原始文档相同。同样,使用 ignore_malformed 或 ignore_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. }
`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