Elasticsearch 8.X 可以按照数组下标取数据吗?

news2025/1/22 2:21:11

1、线上环境问题

老师、同学们,有人遇到过这个问题么,索引中有一个 integer 数组字段,然后通过脚本获取数组下标为1的值作为运行时字段,发现返回的值是乱的,并不是下标为1的值, 具体如下:

DELETE my_index
PUT my_index
{
  "mappings": {
    "properties": {
      "price": {
        "type": "integer"
      }
    }
  }
}

POST /my_index/_doc
{
  "price": [
    5,
    6,
    99,
    3
  ]
}

POST /my_index/_doc
{
  "price": [
    22,
    11,
    19,
    -1
  ]
}
GET my_index/_search
{
  "runtime_mappings": {
    "price_a": {
      "type": "double",
      "script": {
        "source": """
        double v = doc['price'][1];
        emit(v);
        """
      }
    }
  },
  "fields": [
    {
      "field": "price_a"
    }
  ]
}

是不是因为doc value存储的结果是乱序的?

结果为:

5f1ef5a8169db5b4d7134a984bf8a2ec.png                                                                                         ——问题来自技术交流群

2、问题分析

2.1 Elasticsearch 数组是如何存取的?

在 Elasticsearch 中,数组并不是一种特殊的数据类型。

当你在JSON文档中有一个数组字段并将其索引到Elasticsearch时,Elasticsearch会将数组中的每个元素当作独立的值进行索引,但它不会存储数组的结构或顺序信息。

例如,假设你有以下文档:

{

  "tags": ["A", "B", "C"]

}

Elasticsearch会像你分别为文档添加了三个标签"A"、"B"和"C"一样对待它。

数组字段(和许多其他字段类型)在 Elasticsearch 中主要是通过Doc Values来存储的。

Doc Values 是一种优化的、磁盘上的、列式数据结构,它们使得对字段的排序和聚合变得非常快速和高效。

但是,列式存储并不保留原始数据的顺序,这就是为什么数组在 Elasticsearch中会丢失其原始顺序的原因。

2.2 访问数组数据

当你在脚本或查询中访问数组字段时,例如 doc['tags'],你实际上得到的是一个值列表。

即使原始数组只有一个值,你也会得到一个值列表。因此,通常需要检查其.size( )并通过.value或具体的索引来访问特定的值。

2.3 数组与嵌套文档类型 Nested

尽管数组不保留顺序,但 Elasticsearch 提供了一种 nested 数据类型,可以让你索引数组中的对象,并保持它们之间的关系。

这对于复杂的对象数组非常有用,但同时也带来了一些复杂性,如使用特定的 nested 查询和聚合。

3、如何获取指定下标的数据?

3.1 方案一、微小改动。

#### 删除索引
DELETE my_index

#### 创建索引
PUT my_index
{
  "mappings": {
    "properties": {
      "price": {
        "type": "integer"
      }
    }
  }
}

#### 导入数据
POST /my_index/_doc
{
  "price": [
    5,
    6,
    99,
    3
  ]
}

POST /my_index/_doc
{
  "price": [
    22,
    11,
    19,
    -1
  ]
}
#### 创建预处理管道
PUT _ingest/pipeline/split_array_pipeline
{
  "description": "Splits the price array into individual fields",
  "processors": [
    {
      "script": {
        "source": """
        if (ctx.containsKey('price') && ctx.price instanceof List && ctx.price.size() > 0) {
          for (int i = 0; i < ctx.price.size(); i++) {
            ctx['price_' + i] = ctx.price[i];
          }
        }
        """
      }
    }
  ]
}

split_array_pipeline 预处理管道的含义:

  • description:

描述该管道的目的。在这个案例中,我们说明这个管道的目的是将price数组分解为单独的字段。

  • processors:

是一个处理器数组,每个处理器都完成一个特定的任务。在这里,我们只有一个script处理器。

在 script 处理器中,我们编写了一个小脚本,检查是否存在一个名为 price 的字段,该字段是否是一个数组,以及数组是否至少有一个元素。如果所有这些条件都满足,脚本会遍历数组并为数组中的每个元素创建一个新字段。新字段的名称将是 price_0、price_1等,其中的数字是数组的索引。

这种预处理管道非常有用,特别是当原始数据格式不适合直接索引到 Elasticsearch 时。通过使用预处理管道,我们可以在索引数据之前对其进行所需的转换或清理。

POST my_index/_update_by_query?pipeline=split_array_pipeline
{
  "query": {
    "match_all": {}
  }
}


GET my_index/_search
{
  "runtime_mappings": {
    "price_a": {
      "type": "double",
      "script": {
        "source": """
        if (doc['price_0'].size() > 0) {
          double v = doc['price_0'].value;
          emit(v);
        }
        """
      }
    }
  },
  "fields": [
    {
      "field": "price_a"
    }
  ]
}

运行时字段(Runtime Fields)。运行时字段是 7.12 版本后引入的功能,允许你定义临时字段,这些字段的值是在查询时通过脚本计算的,而不是在索引时预先存储的。

如上代码中:

  • 我们定义了一个名为 price_a 的新运行时字段。字段类型为 double。

  • 我们提供了一个Painless 脚本,用于计算此字段的值。

脚本解读:

  • if (doc['price_0'].size() > 0):

这检查price_0字段是否存在并且有值。在Elasticsearch的脚本中,doc['field_name']表示获取该字段的值,.size()方法用于检查该字段是否有值(在某些文档中,该字段可能不存在或为空)。

  • double v = doc['price_0'].value;:

如果上面的条件为真,这行代码会从price_0字段中取出值,并将其转换为double类型。

  • emit(v);:

这是Painless脚本的关键指令。它将指定的值输出为运行时字段price_a的值。

执行结果如下,结果已经达到预期。

fda64f8f1db9ef87286e902581cda187.png

3.2 方案二:Nested 实现

Nested 嵌套数据类型,咱们之前文章多次讲过,不明白的同学可以翻看一下历史文章。

### 定义索引
PUT /my_nested_index
{
  "mappings": {
    "properties": {
      "prices": {
        "type": "nested",
        "properties": {
          "value": {
            "type": "integer"
          }
        }
      }
    }
  }
}

### 导入数据
POST /my_nested_index/_doc
{
  "prices": [
    {"value": 5},
    {"value": 6},
    {"value": 99},
    {"value": 3}
  ]
}

POST /my_nested_index/_doc
{
  "prices": [
    {"value": 22},
    {"value": 11},
    {"value": 19},
    {"value": -1}
  ]
}
#### 执行检索
GET my_nested_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "prices",
            "query": {
              "exists": {
                "field": "prices.value"
              }
            },
            "inner_hits": {
              "size": 1
            }
          }
        }
      ]
    }
  }
}

如果你想只返回 inner_hits 下的第一个数据结果,你可以使用size参数。通过设置size为 1,你可以限制inner_hits返回的结果数量。

返回结果:

24306f80c2394ec29945d334d23a6c37.png

4、小结

当我们使用 Elasticsearch 处理数组数据时,很容易误解其实际行为。本文详细探讨了Elasticsearch如何处理和存储数组,并提供了几种获取数组中特定位置元素的方法。

首先,我们必须理解 Elasticsearch 不是以传统的方式存储数组,而是将每个元素视为独立的值。因此,我们不能简单地通过下标直接访问数组中的某个特定元素。

有几种方法可以解决这个问题:

使用预处理管道:通过创建一个预处理管道来分解数组并为每个元素生成一个新字段。这种方法非常直观,允许我们轻松访问任何特定位置的元素。

使用 Nested 数据类型:对于需要保留其元素间关系的复杂数组,Nested数据类型是一个非常有效的选择。这使我们能够对数组中的每个对象执行更复杂的查询,并且能够保留它们之间的关系。

这两种方法都有其优点和缺点。选择哪一种方法取决于你的具体需求和数据结构。预处理管道方案适用于那些希望保持数据的简单性并能够直接访问数组元素的场景。而 Nested 数据类型则适用于那些需要在数组对象之间维护关系的更复杂的场景。

在任何情况下,理解你的数据结构和 Elasticsearch 如何处理它是至关重要的。希望通过这篇文章,你对Elasticsearch的数组处理有了更深入的理解,并能够更有效地解决与数组相关的问题。

最后,不管你选择哪种方法,都要确保经常测试和验证数据的完整性和准确性。这样,你就可以确保在生产环境中得到预期的结果,避免因为数据结构的误解而产生的潜在问题。

推荐阅读

  1. 全网首发!从 0 到 1 Elasticsearch 8.X 通关视频

  2. 重磅 | 死磕 Elasticsearch 8.X 方法论认知清单

  3. 如何系统的学习 Elasticsearch ?

  4. 2023,做点事

  5. 干货 | Elasticsearch Nested类型深入详解

  6. Elasticsearch Nested 选型,先看这一篇!

  7. 干货 | Elasticsearch Nested 数组大小求解,一网打尽!

  8. Elasticsearch 有没有数组类型?有哪些坑?

  9. 干货 | 拆解一个 Elasticsearch Nested 类型复杂查询问题

0ccc5009684511c3c757f579c97343d5.jpeg

更短时间更快习得更多干货!

和全球 近2000+ Elastic 爱好者一起精进!

be1df05a9d799ce1f17ad7161cce62ed.gif

一个人可以走得很快,但一群人走得更远!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/966257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

36岁男子自称被裁,曾是前500强公司市场总监,最后接受做外买

(点击即可收听) 最近有一个新闻,上了头条,36岁男子自称被裁,是前500强公司市场总监,一年没找到工作,以前不信年龄是道坎 以为自己的经验和履历,能在找到一份相匹配的工作不成问题 但是现实却很残酷,一年后,仍然没有找到对口的工作,即使降低了薪资标准,可依旧找不到 都说疫情前难…

逻辑回归Logistic

回归 概念 假设现在有一些数据点&#xff0c;我们用一条直线对这些点进行拟合&#xff08;这条直线称为最佳拟合直线&#xff09;&#xff0c;这个拟合的过程就叫做回归。进而可以得到对这些点的拟合直线方程。 最后结果用sigmoid函数输出 因此&#xff0c;为了实现 Logisti…

3D视觉测量:形位公差 面对面垂直度(附源码)

文章目录 0. 测试效果1. 基本内容2. 实现方法2. 代码实现3. 参考文章目录:3D视觉测量目录微信:dhlddxB站: Non-Stop_0. 测试效果 1. 基本内容 垂直度用于描述被测要素相对基准(面/轴)的垂直(90)程度。是一个表面或轴线与一个基准面(通常是垂直于图纸纸面的一个面)之间的…

Leetcode 16.07 最大数值

编写一个方法&#xff0c;找出两个数字a和b中最大的那一个。不得使用if-else或其他比较运算符。 示例&#xff1a; 输入&#xff1a; a 1, b 2 输出&#xff1a; 2 我的答案&#xff1a; 为了找出两个数中的较大者&#xff0c;而不使用比较或条件语句&#xff0c;我们可以…

Linux常用命令——cupsdisable命令

在线Linux命令查询工具 cupsdisable 停止指定的打印机 补充说明 cupsdisable命令用于停止指定的打印机。 语法 cupsdisable(选项)(参数)选项 -E&#xff1a;当连接到服务器时强制使用加密&#xff1b; -U&#xff1a;指定连接服务器时使用的用户名&#xff1b; -u&#…

【无公网IP内网穿透】异地远程访问本地SQL Server数据库

目录 1.前言 2.本地安装和设置SQL Server 2.1 SQL Server下载 2.2 SQL Server本地连接测试 2.3 Cpolar内网穿透的下载和安装 2.3 Cpolar内网穿透的注册 3.本地网页发布 3.1 Cpolar云端设置 3.2 Cpolar本地设置 4.公网访问测试 5.结语 1.前言 数据库的重要性相信大家…

jdk-8u371-linux-x64.tar.gz jdk-8u371-windows-x64.exe 【jdk-8u371】 全平台下载

jdk-8u371 全平台下载 jdk-8u371-windows-x64.exejdk-8u371-linux-x64.rpmjdk-8u371-linux-x64.tar.gzjdk-8u371-macosx-x64.dmgjdk-8u371-linux-aarch64.tar.gz 下载地址 迅雷云盘 链接&#xff1a;https://pan.xunlei.com/s/VNdLL3FtCnh45nIBHulh_MDjA1?pwdw4s6 百度…

uni-app之android离线打包

一 AndroidStudio创建项目 1.1&#xff0c;上一节演示了uni-app云打包&#xff0c;下面演示怎样androidStudio离线打包。在AndroidStudio里面新建空项目 1.2&#xff0c;下载uni-app离线SDK&#xff0c;离线SDK主要用于App本地离线打包及扩展原生能力&#xff0c;SDK下载链接h…

2023开学礼新疆理工学院图书馆藏八一新书《乡村振兴战略下传统村落文化旅游设计》许少辉新财经理工

2023开学礼新疆理工学院图书馆藏八一新书《乡村振兴战略下传统村落文化旅游设计》许少辉新财经理工

GraalVM Community Edition 22.3.3

https://github.com/graalvm/graalvm-ce-builds/releases/ 按需下载 https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.3/graalvm-ce-java11-windows-amd64-22.3.3.ziphttps://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.3/nat…

山西电力市场日前价格预测【2023-09-03】

日前价格预测 预测明日&#xff08;2023-09-03&#xff09;山西电力市场全天平均日前电价为316.78元/MWh。其中&#xff0c;最高日前电价为405.32元/MWh&#xff0c;预计出现在19: 15。最低日前电价为249.46元/MWh&#xff0c;预计出现在12: 15。 价差方向预测 1&#xff1a; 实…

公司文件数据防泄密软件——「天锐绿盾透明加密防泄密系统」

天锐绿盾透明加密防泄密系统是一款利用驱动层透明加密技术实现电子文件安全加密的防护产品。该系统集成了密码学、访问控制和审计跟踪等技术手段&#xff0c;对企事业单位电子文件的存储、访问、传播和处理过程进行全方位防护&#xff0c;从源头上保障数据安全和使用安全。 PC访…

springboot web开发登录拦截器

在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。 应用场景 日志记录&#xff0c;可以记录请求信息的日志&#xff0c;以便进行信息监控、信息统计等。权限检查&#xff1a;如登陆检测&#xff…

.NET之后,再无大创新

回想起来&#xff0c;2001年发布的.NET已经是距离最近的一次软件开发技术的整体创新了&#xff0c;后续的新技术就没有在各个端都这么成功的了。.NET是Windows平台下软件开发技术的巨大变革。在此之前&#xff0c;有VB、C&#xff08;MFC&#xff09;、JSP&#xff0c;在此之后…

cms系统稳定性压力测试出现TPS抖动和毛刺的性能bug【杭州多测师_王sir】

一、并发线程数100&#xff0c;分10个阶梯&#xff0c;60秒加载时间&#xff0c;运行1小时进行压测&#xff0c;到10分钟就出现如下 二、通过jstat -gcutil 16689 1000进行监控

go-zero jwt 鉴权快速实战

前面我们分享了 go-zero 的快速实战以及日志组件的剖析&#xff0c;本次我们来实战使用 go-zero jwt 鉴权 本次文章主要是分享关于 go-zero 中 jwt 的使用方式&#xff0c;会以一个 demo 的方式来进行实战&#xff0c;对于使用 goctl 工具以及安装细节就不在赘述&#xff0c;有…

计算机图形软件(三)6-5 一个完整的OpenGL程序、OpenGL的出错处理

一个完整的OpenGL程序 给出构成一个完整程序的所有部分之前还是有一些任务需要完成。对于显示窗口&#xff0c;我们可以选择背景颜色。我们需要组织一个过程来包含创建显示图形所必需的OpenGL 函数。 要像图3.2 那样使用 RCB 颜色值将显示窗口的背景颜色设定为白色可以使用 Ope…

报错处理:Disk space full

报错环境&#xff1a; Linux 具体报错&#xff1a; No space left on device&#xff0c;磁盘空间已满 排错思路&#xff1a; 当磁盘空间耗尽时&#xff0c;会出现磁盘空间已满的错误。这可能是由于磁盘上的文件过多或者某个文件系统占用了过多磁盘空间。 解决方法&#xff1a;…

Opencv图像暗通道调优

基于雾天退化模型的去雾算法&#xff0c;Opencv图像暗通道调优&#xff0c;&#xff08;清华版代码&#xff09;对普通相片也有较好的调优效果&#xff0c;相片更通透。 结合代码实际运行效果、算法理论模型、实际代码。我个人理解&#xff0c;实际效果是对图像的三个颜色通道…

解决Clipping input data to the valid range for imshow with RGB data([0..1] ...)

解决 Clipping input data to the valid range for imshow with RGB data [0..1] for floats or [0..255] for integers 1. 问题描述2. 解决方法 1. 问题描述 在使用 python 显示 npz 文件中的图片时&#xff0c;使用如下代码&#xff1a; import numpy as np import matplot…