国外校园网站网站建设发展历程,制作一个静态网站的步骤,企业网站建设一条龙,百度查重软件Elasticsearch 是一个很棒的工具#xff0c;可以从各种来源收集日志和指标。 它为我们提供了许多默认处理#xff0c;以便提供最佳用户体验。 但是#xff0c;在某些情况下#xff0c;默认处理可能不是最佳的#xff08;尤其是在生产环境中#xff09;#xff1b; 因此可以从各种来源收集日志和指标。 它为我们提供了许多默认处理以便提供最佳用户体验。 但是在某些情况下默认处理可能不是最佳的尤其是在生产环境中 因此今天我们将深入探讨避免 “映射污染” 的方法。 什么是“映射”为什么会有污染
与任何数据存储解决方案一样必须有一个模式schema等效设置来说明如何处理数据字段例如存储和处理/分析 此设置在 Elasticsearch 下称为 “映射mapping”。
与大多数不同如果未指定Elasticsearch 会为模式设置提供默认处理。 举个例子当我们创建一个文档并引入一个全新的数据索引时Elasticsearch 会尝试为我们猜测正确的字段映射。 是的~这是一个猜测 因此在大多数情况下我们会毫无问题地摄取该文档并自动完成。 干杯~但是等等……
猜测的映射可能未优化例如任何整数字段都将映射到占用 64 位内存的数据类型 “long”但是如果你的整数字段范围仅从 0~5 ......可能是 “byte” 或 “short” 就足够了只需要 8 到 16 位内存有些字段对我们来说毫无意义因此我们应该在摄取之前排除它们以免引入映射污染如何避免映射污染 —— Painless 脚本处理器方法
我们可以使用脚本处理器创建一个摄取管道以在最终摄取发生之前删除目标字段。 用例删除名称长度超过 15 个字符的字段。
也许我们的数据与随机生成的 UUID超过 15 个字符长下的一些元数据一起出现并且不知何故我们永远不需要这些元数据。 因此为避免映射污染我们需要在一开始就排除此类字段。 以下是检查文档中可能字段长度的示例
# pipeline to script and loop around all fields in a
# context
POST _ingest/pipeline/_simulate
{pipeline: {processors: [{script: {source: boolean flag false;java.util.Set keys ctx.keySet();for (String key : keys) {if (key.length()15) {flag true;}}ctx[has_long_fields] flag;}}]},docs: [{_source: {very_very_long_field: balabalabalabalabala}},{_source: {age: 12}}]
}
上面的命令的响应为
{docs: [{doc: {_index: _index,_id: _id,_version: -3,_source: {very_very_long_field: balabalabalabalabala,has_long_fields: true},_ingest: {timestamp: 2023-03-01T06:40:53.22560943Z}}},{doc: {_index: _index,_id: _id,_version: -3,_source: {has_long_fields: false,age: 12},_ingest: {timestamp: 2023-03-01T06:40:53.225907596Z}}}]
}
我们可以清楚地看到 2 个测试文档的结果第一个包含一个字段 very_very_long_field结果为 true而第二个只包含一个 age 字段结果为 false。
这里的技巧是 ctx.keySet() 方法。 此方法返回一个 Set 接口其中包含文档下可用的所有 “field-names”。 获得 Set 后我们可以开始迭代它并应用我们的匹配逻辑。 一个棘手的事情是......这个集合还包含元数据字段如 _index 和 _id因此当我们应用一些字段匹配逻辑时也要注意这些字段。 下一个示例将说明如何从我们的文档上下文中删除相应的字段
# remove long fields...
POST _ingest/pipeline/_simulate
{pipeline: {processors: [{script: {source: boolean flag false;java.util.Set keys ctx.keySet();java.util.List fields new java.util.ArrayList();for (String key : keys) {if (!key.startsWith(_) key.length() 10) {fields.add(key); }}// look through and delete those long field(s)if (fields.size() 0) {for (String field: fields) {ctx.remove(field);}flag true;}ctx[has_removed_long_fields] flag; }}]},docs:[{_source: {very_very_long_field: balabalabalabalabala,another_long_field: wakkakakakaka,age: 13,name: Felis}},{_source: {desc: dkjfdkjfkdjfkdfjk,address: wakkakakakaka,age: 13,name: Felis}}]
}
上面命令的返回值为
{docs: [{doc: {_index: _index,_id: _id,_version: -3,_source: {name: Felis,age: 13,has_removed_long_fields: true},_ingest: {timestamp: 2023-03-01T06:45:46.164088718Z}}},{doc: {_index: _index,_id: _id,_version: -3,_source: {name: Felis,address: wakkakakakaka,age: 13,desc: dkjfdkjfkdjfkdfjk,has_removed_long_fields: false},_ingest: {timestamp: 2023-03-01T06:45:46.164113593Z}}}]
}
魔法是 ctx.remove(“fieldname”)。 很简单不是吗 另请注意我们对字段匹配逻辑 !key.startsWith(“_”) key.length()10 应用了更精确的规则因此不会考虑所有元字段例如 _index。
还引入了一个 ArrayList 来存储目标字段名称。 你可能会问为什么我们不在循环期间直接从文档上下文中删除该字段 原因是如果我们尝试这样做则会爆发一个异常描述对文档上下文的并发修改。 因此我们需要延迟删除过程并且此 ArrayList 会跟踪这些字段名称。
最后还有另一种情况我们的文档可能涉及多个级别/层次结构。 以下示例说明如何确定字段是 “leaf” 字段还是 “branch” 字段
# remove the inner field
POST _ingest/pipeline/_simulate
{pipeline: {processors: [{script: {source: java.util.Set keys ctx.keySet();java.util.ArrayList fields new java.util.ArrayList();for (String key : keys) {// access the value; check the typeif (key.startsWith(_)) {continue;}Object value ctx[key];if (value ! null) {// it is a MAP (equivalent to the object structure of a json field)if (value instanceof java.util.Map) {// inner fields loopjava.util.Map innerObj (java.util.Map) value;for (String innerKey: innerObj.keySet()) {if (innerKey.length() 10) {//Debug.explain(a long field innerKey);fields.add(key.innerKey);}}} else {if (key.length() 10) {fields.add(key);}}}}if (fields.size()0) {for (String field:fields) {// is it an inner field?int idx field.indexOf(.);if (idx ! -1) {ctx[field.substring(0, idx)].remove(field.substring(idx1));} else {ctx.remove(field); }}}}}]},docs: [{_source: {age: 13,very_very_very_long_field: to be removed,outer: {name: Felix,very_very_long_field: dkjfdkjfkdjfkdfjk}}}]
}
上面的响应为
{docs: [{doc: {_index: _index,_id: _id,_version: -3,_source: {outer: {name: Felix},age: 13},_ingest: {timestamp: 2023-03-01T06:54:51.610568095Z}}}]
}
一个很长的代码……为了检查该字段是 “leaf” —— 普通字段还是 “branch” —— 另一层字段例如对象 我们需要检查字段值的类型 java.util.Map 的值实例。 instanceof 方法有助于验证提供的值是否与特定的 Java 类类型匹配。 接下来我们需要再次迭代 Set of inner-object fields 以应用我们的匹配规则。 使用 ArrayList 的相同技术将应用于跟踪目标字段名称以便在稍后阶段删除。
最后通过 ctx.remove(“fieldname”) 删除字段。 但是这次我们还需要检查这个字段是 leaf 还是 branch 字段。 对于 branch 字段它将以 outer-object-name.inner-field-name 的格式出现。 我们需要先提取 outer-object-name 并在删除 inner-field-name 之前访问其上下文 - ctx[field.substring(0, idx)].remove(field.substring (idx1))
举个例子outer.very_very_long_field
idx(“.”分隔符所在的 index) 5field.substring(0, idx) outerfield.substring(idx1) very_very_long_field因此…… ctx[field.substring(0, idx)].remove(field.substring(idx1)) ctx[“outer”].remove(“very_very_long_field”)
干得好 ~ 这是避免贴图污染的 “Painless” 脚本方法。 如何避免映射污染 —— 索引的动态设置方法
有时我们可能不介意引入一个映射污染 然而~我们不希望那些无意义的字段是可搜索或可聚合的。 只是我们让那些无意义的字段充当虚拟对象你可以看到它们在 _source 字段下可用但永远无法对它们应用任何操作。 如果是这样的话……我们可以更改索引的动态设置。
PUT test_dynamic
{mappings: {dynamic: false,properties: {name: {type: text},address: {dynamic: true,properties: {street: {type: keyword}}},work: {dynamic: strict,properties: {department: {type: keyword},post: {type: keyword}}}}}
}
我们通过如下的方法来摄入一些数据
# all good, everything matches the mapping
POST test_dynamic/_doc
{name: peter parker,address: {street: 20 Ingram Street,state: NYC},work: {department: daily bugle}
}# added a non-searchable age and a searchable field address.post_code
POST test_dynamic/_doc
{age: 45,name: Edward Elijah,address: {post_code: 344013}
}
很显然在上面age 不在之前的映射中定义。由于我们在映射中设置 dynamic 为 falseage 这个字段将不能被用于搜索
dynamicdoc indexed?fields searchablefields indexed?mapping updated?trueYesYesYesYesruntimeYesYesNo NofalseYesNoNoNostrictNo
GET test_dynamic/_search
{query: {match: {age: 45}}
}
上面搜索的结果为空。有关动态映射的文章请详细阅读文章 “ElasticsearchDynamic mapping”。
我们接着进行如下的搜索
GET test_dynamic/_search
{query: {match: {address.post_code: 344013}}
}
上面的搜索显示的是一个文档 hits: [{_index: test_dynamic,_id: Z2X8m4YBRPmzDW_iGJOb,_score: 0.2876821,_source: {age: 45,name: Edward Elijah,address: {post_code: 344013}}}]
我们执行如下的命令
# exception as work.salary is forbidden
POST test_dynamic/_doc
{age: 45,name: Prince Tomas,address: {post_code: 344013},work: {salary: 10000000}
}
上面命令返回的结果为
{error: {root_cause: [{type: strict_dynamic_mapping_exception,reason: mapping set to strict, dynamic introduction of [salary] within [work] is not allowed}],type: strict_dynamic_mapping_exception,reason: mapping set to strict, dynamic introduction of [salary] within [work] is not allowed},status: 400
}
这是因为 work 字段的属性为 strict。我们不可以为这个字段添加任何新的属性。 如何避免映射污染 —— 通过预处理方法删除字段
这里讨论的最后一种方法是在传递给 Elasticsearch 之前删除无意义的字段......以及如何 嗯……自己写程序对文档进行预处理~:)))))
这确实是一种有效的方法但可能并不适合所有人 因为需要一些编程知识。 有时如果我们在传递给 Elasticsearch 之前对文档进行预处理它可能会更加灵活因为我们可以完全控制文档的修改由于编程语言的功能。