在 Elasticsearch 中处理复杂数据类型如数组时,提供了极高的灵活性,但同时也带来了一定的复杂性,尤其在使用脚本进行高级查询和数据操作时。
举例来自微信群的企业级实战问题如下:
本博客旨在通过一系列实战案例,展示如何有效地在 Elasticsearch 中使用脚本来处理数组类型的字段。
在现代数据环境中,数据往往以复杂和非结构化的形式存在,数组字段的处理尤为常见。
Elasticsearch 通过其强大的 Painless 脚本语言支持,提供了一种灵活的方式来操作这些数据。然而,脚本的使用可能会让用户在错误调试和性能优化上遇到挑战。
Painless 是 Elasticsearch 专为快速、安全、可管理性强而设计的脚本语言。用于执行复杂的数据处理任务,如计算、数据转换、条件逻辑等。
更多关于 Painless 的官方文档可以参见:
https://www.elastic.co/guide/en/elasticsearch/painless/current/index.html
在这个例子中,我们将创建一个名为 vehicles 的索引,该索引用于存储关于车辆的信息,包括发行日期、在线日期范围、所有者、公司 ID、货物类型、车辆长度(数组形式)和车辆类型。
PUT /vehicles
{
"mappings": {
"properties": {
"issue_date": {"type": "date"},
"online_date": {
"properties": {
"gte": {"type": "date"},
"lte": {"type": "date"}
}
},
"owner": {"type": "integer"},
"company_id": {"type": "integer"},
"goods_type": {"type": "integer"},
"car_length": {"type": "integer"}, // 注意,这里定义为单个整数,我们需要修改以支持数组
"car_type": {"type": "keyword"}
}
}
}
注意:在原定义中,car_length 被定义为整数,但因为它是一个数组,我们需要确保在真实环境中将其定义为 "type": "integer" 且在设置时需要标识为数组可以接受多个整数。
数据插入时,使用 _bulk API 可以提高数据插入的效率,特别适合处理大批量数据。示例如下:
POST /vehicles/_bulk
{ "index": {} }
{ "issue_date": "2024-07-07T21:44:56Z", "owner": 6347, "company_id": 2513, "goods_type": 21, "car_length": [16, 18], "car_type": ["轿车", "货车", "大挂车"] }
在这个数据中,car_length 是一个数组,包含两个整数值 [16, 18]。
我们一个个实操如下:
在 Elasticsearch 中,可以使用 Painless 脚本语言来处理更复杂的查询。例如,若要访问 car_length 数组的第一个元素,我们可以在查询中添加一个脚本字段:
POST /vehicles/_search
{
"script_fields": {
"first_car_length": {
"script": {
"lang": "painless",
"source": "if (doc['car_length'].size() > 0) { return doc['car_length'][0]; } else { return 'none'; }"
}
}
}
}
这个脚本检查 car_length 是否非空,如果非空,则返回第一个元素;否则返回 'none'。
在 Elasticsearch 中处理数组类型字段的脚本操作可以变得相当复杂,尤其是当涉及到数据的实际业务逻辑时。
以下是一些进阶的示例,演示如何使用 Elasticsearch 的 Painless 脚本语言来执行数组字段的常规操作,从基本到高级。
获取数组长度是数组操作中最基础的功能之一,可以用来判断数组是否为空,或者用在更复杂的脚本逻辑中。
POST /vehicles/_search
{
"script_fields": {
"car_length_count": {
"script": {
"lang": "painless",
"source": "doc['car_length'].size()"
}
}
}
}
3.2.3 求和操作:计算数组元素总和
计算数组中所有元素的总和是处理数组类型数据时的常见需求,特别是在统计和分析数据时。
POST /vehicles/_search
{
"script_fields": {
"car_length_sum": {
"script": {
"lang": "painless",
"source": "int sum = 0; for (int length : doc['car_length']) { sum += length; } return sum;"
}
}
}
}
3.2.4 最大/最小值:寻找数组中的极值
在数组中找到最大或最小的元素,这对于数据分析和决策制定是非常有用的。
POST /vehicles/_search
{
"script_fields": {
"max_car_length": {
"script": {
"lang": "painless",
"source": "int max = Integer.MIN_VALUE; for (int val : doc['car_length']) { if (val > max) { max = val; } } return max;"
}
},
"min_car_length": {
"script": {
"lang": "painless",
"source": "int min = Integer.MAX_VALUE; for (int val : doc['car_length']) { if (val < min) { min = val; } } return min;"
}
}
}
}
3.2.5 过滤操作:基于条件筛选数组元素
根据特定条件筛选数组中的元素,这在处理满足特定标准的数据项时特别有用。
POST /vehicles/_search
{
"script_fields": {
"filtered_lengths": {
"script": {
"lang": "painless",
"source": "doc['car_length'].stream().filter(length -> length > 15).collect(Collectors.toList())"
}
}
}
}
还有一种简单的写法:
POST /vehicles/_search
{
"script_fields": {
"filtered_lengths": {
"script": {
"lang": "painless",
"source": """
List filtered = new ArrayList();
for (int length : doc['car_length']) {
if (length > 17) {
filtered.add(length);
}
}
return filtered;
"""
}
}
}
}
创建一个新的 ArrayList,名为 filtered,用于存储过滤后的结果。通过 for 循环遍历 car_length 数组中的每个元素。在循环体内部,对每个元素使用 if 条件语句来检查是否大于 15。如果条件为真,就将该元素添加到 filtered 列表中。脚本最终返回 filtered 列表,该列表包含所有大于 15 的 car_length 值。
这个方法对于执行数组的过滤操作是非常有效的,并且在执行上比使用 Stream API 更为简洁和高效,特别是在 Elasticsearch 的 Painless 环境中。
计算数组的平均值是统计数据的一种常见需求,对于理解数据的整体趋势非常重要。
POST /vehicles/_search
{
"script_fields": {
"average_car_length": {
"script": {
"lang": "painless",
"source": "double sum = 0; for (int length : doc['car_length']) { sum += length; } return sum / doc['car_length'].size();"
}
}
}
}
3.2.7 复杂业务逻辑:计算条件权重总和
在某些业务场景下,我们可能需要根据数组中的每个元素计算加权总和,其中权重可能由另一个字段或复杂的业务规则确定。
POST /vehicles/_search
{
"script_fields": {
"weighted_sum": {
"script": {
"lang": "painless",
"source": "double sum = 0; for (int i = 0; i < doc['car_length'].size(); i++) { sum += doc['car_length'][i] * (i + 1); } return sum;"
}
}
}
}
在这个示例中,我们使用每个元素的索引位置作为权重,这只是一个简单的例子,实际应用中权重可能更复杂。
这些示例覆盖了从基本到复杂的多种操作,每种操作都可以根据具体的业务需求进行调整和扩展。在使用 Elasticsearch 进行数据处理时,合理运用 Painless 脚本可以极大地增强查询的灵活性和功能。
在使用脚本进行数组操作时,应考虑性能和资源消耗。适当的优化策略,如使用缓存、限制操作大小、选择合适的索引和数据模型,写入前的 ingest 预处理都是确保良好性能的关键。
合理利用 Elasticsearch 的 Painless 脚本功能可以显著提高数据处理的灵活性和效率,但需要根据具体业务需求进行调整。
希望本文能帮助您更有效地利用 Elasticsearch 处理和分析数据。
[1] 官方 Elasticsearch 文档:深入理解 Elasticsearch 的架构和原理
https://elastic.co/guide/en/elasticsearch/reference/current/index.html
[2] Elasticsearch: The Definitive Guide:提供 Elasticsearch 使用方法和最佳实践的详尽介绍。(版本虽老,但经得起时间的检验)
https://www.elastic.co/guide/en/elasticsearch/guide/master/index.html
[3] Elasticsearch 数组脚本细节
https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-operators-array.html
[4] Elasticsearch 有没有数组类型?有哪些坑?
本文分享自 铭毅天下Elasticsearch 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!