Elasticsearch:如何使用 Elasticsearch 进行排序

虽然你在唱这首歌时可能会想象圣诞老人,但欧洲民间传说,尤其是阿尔卑斯地区的民间传说,有两个传奇人物圣尼古拉斯和坎普斯。 象征着慷慨和善良的圣尼古拉斯,在 12 月 6 日 为乖巧的孩子们带来礼物和欢乐! 相比之下,坎普斯是一种有角且具有威胁性的生物,它可以在前一天晚上对行为不端的孩子发出警告。 他们共同创造了独特而持久的传统,鼓励节日期间的慷慨和良好行为。

当然,他们正在使用 Elasticsearch 来追踪行为不端和行为良好的孩子。 但他们如何确定访问的优先顺序呢? 本文深入探讨了 Elasticsearch 的一些更有趣的排序选项 - 有关所有选项,请参阅官方文档。 虽然该示例使用圣尼古拉斯和坎普斯主题,但这些概念适用于许多其他场景。

示例数据集

ChatGPT 可以使用提示为我们生成映射和数据集:

generate an elasticsearch mapping and bulk query with 10 christmas themed characters (like the grinch) containing the fields: name, date of birth, geolocation, timezone, behavior (can be good, bad, or mixed), and points (an array of mixed +1 or -1 values)

mappings

PUT /christmas_characters
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "date_of_birth": { "type": "date" },
      "geolocation": { "type": "geo_point" },
      "timezone": { "type": "keyword" },
      "behavior": { "type": "keyword" },
      "points": { "type": "integer" }
    }
  }
}

Bulk Query

POST /christmas_characters/_bulk
{ "index": {} }
{ "name": "The Grinch", "date_of_birth": "1966-12-01", "geolocation": {"lat": 48.8566, "lon": 2.3522}, "timezone": "UTC", "behavior": "bad", "points": [ -1, -1, -1, -1, -1 ] }
{ "index": {} }
{ "name": "Santa Claus", "date_of_birth": "0000-12-25", "geolocation": {"lat": 90, "lon": 0}, "timezone": "UTC", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Ebenezer Scrooge", "date_of_birth": "1803-12-19", "geolocation": {"lat": 51.509865, "lon": -0.118092}, "timezone": "GMT", "behavior": "mixed", "points": [ -1, 1, -1, 1, -1 ] }
{ "index": {} }
{ "name": "Buddy the Elf", "date_of_birth": "1973-12-25", "geolocation": {"lat": 40.7128, "lon": -74.0060}, "timezone": "EST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Rudolph the Red-Nosed Reindeer", "date_of_birth": "1939-12-01", "geolocation": {"lat": 61.016, "lon": -149.737}, "timezone": "AKST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Jack Frost", "date_of_birth": "Unknown", "geolocation": {"lat": 44.9778, "lon": -93.2650}, "timezone": "CST", "behavior": "mixed", "points": [ -1, 1, -1, 1, -1 ] }
{ "index": {} }
{ "name": "Cindy Lou Who", "date_of_birth": "1998-12-25", "geolocation": {"lat": 41.8781, "lon": -87.6298}, "timezone": "CST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "The Nutcracker", "date_of_birth": "1816-12-18", "geolocation": {"lat": 55.7558, "lon": 37.6176}, "timezone": "MSK", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Frosty the Snowman", "date_of_birth": "1969-12-07", "geolocation": {"lat": 34.0522, "lon": -118.2437}, "timezone": "PST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Scrooge's Nephew Fred", "date_of_birth": "Unknown", "geolocation": {"lat": 51.509865, "lon": -0.118092}, "timezone": "GMT", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }

从上面的输出中我们可以看到有两个文档中的 date_of_birth 字段值为 "Unknow"。我们需要对它进行修正。修正后的文档为:

POST /christmas_characters/_bulk
{ "index": {} }
{ "name": "The Grinch", "date_of_birth": "1966-12-01", "geolocation": {"lat": 48.8566, "lon": 2.3522}, "timezone": "UTC", "behavior": "bad", "points": [ -1, -1, -1, -1, -1 ] }
{ "index": {} }
{ "name": "Santa Claus", "date_of_birth": "0000-12-25", "geolocation": {"lat": 90, "lon": 0}, "timezone": "UTC", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Ebenezer Scrooge", "date_of_birth": "1803-12-19", "geolocation": {"lat": 51.509865, "lon": -0.118092}, "timezone": "GMT", "behavior": "mixed", "points": [ -1, 1, -1, 1, -1 ] }
{ "index": {} }
{ "name": "Buddy the Elf", "date_of_birth": "1973-12-25", "geolocation": {"lat": 40.7128, "lon": -74.0060}, "timezone": "EST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Rudolph the Red-Nosed Reindeer", "date_of_birth": "1939-12-01", "geolocation": {"lat": 61.016, "lon": -149.737}, "timezone": "AKST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Jack Frost", "date_of_birth": "1539-11-01", "geolocation": {"lat": 44.9778, "lon": -93.2650}, "timezone": "CST", "behavior": "mixed", "points": [ -1, 1, -1, 1, -1 ] }
{ "index": {} }
{ "name": "Cindy Lou Who", "date_of_birth": "1998-12-25", "geolocation": {"lat": 41.8781, "lon": -87.6298}, "timezone": "CST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "The Nutcracker", "date_of_birth": "1816-12-18", "geolocation": {"lat": 55.7558, "lon": 37.6176}, "timezone": "MSK", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Frosty the Snowman", "date_of_birth": "1969-12-07", "geolocation": {"lat": 34.0522, "lon": -118.2437}, "timezone": "PST", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }
{ "index": {} }
{ "name": "Scrooge's Nephew Fred", "date_of_birth": "1970-05-07", "geolocation": {"lat": 51.509865, "lon": -0.118092}, "timezone": "GMT", "behavior": "good", "points": [ 1, 1, 1, 1, 1 ] }

 再次运行上面的命令,我们可以得到输入正确的 Elasticsearch 索引。在下面,我们针对这个数据集来进行排序。

针对 visits 来进行排序

让我们看看如何对圣尼古拉斯和坎普斯的来访进行排序,看看你是否值得一份礼物或一块煤炭 —— 这是坎普斯送给行为不端的孩子的传统礼物。

根据年龄 age

或许年纪越小,等待的耐心就越少。 或者你需要早点睡觉。 因此,让我们使用 match_all 来匹配所有文档,并按 date_of_birth 字段降序排序。

GET /christmas_characters/_search?filter_path=**.hits
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "date_of_birth": {
        "order": "desc"
      }
    }
  ]
}

上面显示的结果为:

为了能够得到更为精简的搜索结果,我们可以改写上面的搜索为:

GET /christmas_characters/_search?filter_path=**.hits
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "date_of_birth": {
        "order": "desc"
      }
    }
  ],
  "_source": false,
  "fields": [
    "name",
    "date_of_birth"
  ]
}

在上面,我们仅显示 name 及 date_of_birth:

安装 Points 及 age 来进行排序

也许你想从表现最好的人开始,由具有良好 (1) 和不良 (-1) 行为的点数组表示。 这里,我们可以按照数组的值的总和进行排序,如果多个总和的值相等,则再次添加基于年龄的辅助排序条件。

GET /christmas_characters/_search?filter_path=**.hits
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "points": {
        "order": "desc",
        "mode": "sum"
      },
      "date_of_birth": {
        "order": "desc"
      }
    }
  ],
  "_source": false,
  "fields": [
    "name",
    "points",
    "date_of_birth"
  ]
}

按照远近来进行排名

出于实际原因,按邻近程度排序可能是最简单的。 据说圣尼古拉斯住在北极 —— 北纬 90 度和东经 0 度,作为他 “家” 的象征性地理点:

GET /christmas_characters/_search?filter_path=**.hits
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_geo_distance": {
        "geolocation": [
          0,
          90
        ],
        "order": "asc",
        "unit": "km",
        "distance_type": "arc"
      }
    }
  ],
  "_source": false,
  "fields": [
    "name"
  ]
}

注意 geolocation 中经度和纬度的顺序(我第一次尝试时总是会出错),然后我们希望根据更精确但较慢的 arc(而不是 plane)距离。从上面的结果中可以看出来,Santa Claus 是离搜索距离最近的文档。

通过脚本

为了获得最大的灵活性,Elasticsearch 的脚本语言 Painless 为您提供了你想要的所有选项。 例如,如果你按属性 “good”、“mixed”、“bad”(按此顺序)排序,则没有任何现有字段可以让你这样做。 但使用脚本,你可以为每个属性分配一个数值(在查询时),然后基于该值进行排序。 并再次添加年龄决胜条件。你可以通过学习 “Elastic:开发者上手指南” 中的 “Painless 编程” 来了解更多的关于 Painless 的编程。

GET /christmas_characters/_search?filter_path=**.hits
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_script": {
        "type": "number",
        "script": {
          "lang": "painless",
          "source": """
            if(doc['behavior'].value == 'good'){
              return 1;
            } else if(doc['behavior'].value == 'mixed'){
              return 2;
            } else {
              return 3;
            }
          """
        },
        "order": "asc"
      }
    },
    {
      "date_of_birth": {
        "order": "desc"
      }
    }
  ]
}

在上面,我们使用 Painless 脚本来计算一个 script field。它是一个 number 类型的数据。具体它的名字是什么,我们无需知道它的名字。我们可以在 sort 里对它进行排名。

不过,只有在必要时才这样做 —— 按脚本排序比按索引字段排序要慢,而且使用 Painless 常常给人与它的名字相反的感觉。 如果你想经常这样排序,请在摄取时显式添加该字段。

使用 runtime field 来进行排序

你可以再次使用 Painless 对(查询时)运行时字段执行与上一个示例相同的操作 - 尽管此示例按时区排序,以便每个人都可以在晚上进行访问。 此代码片段还引入了 missing 的概念,通常使用魔术值 _first 或 _last,但它也可以是静态值,如本例所示。

GET /christmas_characters/_search?filter_path=**.hits
{
  "query": {
    "match_all": {}
  },
  "runtime_mappings": {
    "numeric_timezone": {
      "type": "double",
      "script": {
        "source": """
          if(doc['timezone'].value == 'GMT'){
            emit(-5);
          } else if(doc['timezone'].value == 'UTC' || doc['timezone'].value == 'Europe/London'){
            emit(0);
          } else if(doc['timezone'].value == 'CST'){
            emit(5.5)
          } else if(doc['timezone'].value == 'EST'){
            emit(4)
          } else if(doc['timezone'].value == 'AKST'){
            emit(3)
          } else if(doc['timezone'].value == 'PST'){
            emit(1)
          } else if(doc['timezone'].value == 'MSK'){
            emit(-2)
          }
        """
      }
    }
  },
  "sort": [
    {
      "numeric_timezone": {
        "order": "desc",
        "missing": -0.1
      }
    },
    {
      "date_of_birth": {
        "order": "desc"
      }
    }
  ]
}

使用 ES|QL

在结束之前,Elasticsearch 中有一种新的查询语言:Elasticsearch 查询语言 (ES|QL)。它有一个新端点 (_query)、一种新的且希望更紧凑的语法来编写查询,以及不同的输出选项。

注意:你需要至少安装 Elastic Stack 8.11.0 及以上的版本才可以体验这个功能!

编写与第一个 Painless 示例类似的查询如下所示 — 在 EVAL 中使用 CASE 语句。 这里不讨论太多细节,这是一种将结果传递到下一个语句的过程语言。

POST _query?format=txt
{
  "query": """
    FROM christmas_characters
    | EVAL numeric_behavior = CASE(
        behavior == "good", 1,
        behavior == "mixed", 2,
        3
      )
    | SORT numeric_behavior ASC, date_of_birth DESC
    | KEEP name, behavior, numeric_behavior, date_of_birth
    | LIMIT 10
  """
}

(可配置的)输出格式可以比漂亮打印的 JSON 更加简洁。

这就是第二个 Painless 查询在 ES|QL 中的样子 —— 这个有点棘手,因为它需要转换 TO_DOUBLE() 并且结果有点长。 不过,它应该仍然比在 Painless 中写这个更容易理解。

POST _query?format=txt
{
  "query": """
    FROM christmas_characters
    | EVAL numeric_timezone = CASE(
        timezone == "GMT", TO_DOUBLE(-5.0),
        timezone == "UTC", TO_DOUBLE(0.0),
        timezone == "CST", TO_DOUBLE(5.5),
        timezone == "EST", TO_DOUBLE(4.0),
        timezone == "AKST", TO_DOUBLE(3.0),
        timezone == "PST", TO_DOUBLE(1.0),
        timezone == "MSK", TO_DOUBLE(-2.0),        
        TO_DOUBLE(-1.0)
      )
    | SORT numeric_timezone DESC, date_of_birth DESC
    | KEEP name, behavior, numeric_timezone, timezone
    | LIMIT 10
  """
}

结论

现在所有的分类都完成了,他们就去送礼物了。

更多关于排序的文章,请阅读

  • Elasticsearch:对搜索结果排序 - Sort
  • Elasticsearch:在 Elasticsearch 中按距离有效地对地理点进行排序

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法挨揍日记】day45——474. 一和零、879. 盈利计划

474. 一和零 474. 一和零 题目描述: 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。 解…

鸿蒙开发入门

一、开发准备 1.1 开发环境搭建 鸿蒙开发文档华为账号注册DevEco Studio 下载 二、快速入门 三、ArkUI 3.1 Image 3.2 Text 3.3 TextInput 3.4 Button 3.5 循环控制 3.6 List 3.7 自定义 3.8 状态管理 3.8.1 State 装饰器 3.8.2 Prop、Link 装饰器 // 父组件 State str: …

【每日论文阅读】生成模型篇

联邦多视图合成用于元宇宙 标题: Federated Multi-View Synthesizing for Metaverse 作者: Yiyu Guo; Zhijin Qin; Xiaoming Tao; Geoffrey Ye Li 摘要: 元宇宙有望提供沉浸式娱乐、教育和商务应用。然而,虚拟现实(VR)在无线网络上的传输是…

【Java】SpringBoot整合xxl-job学习使用详解

文章目录 介绍作用如何使用下载项目中央仓库地址环境调度中心初始化“调度数据库”配置部署“调度中心”部署项目调度中心集群(可选)其他:Docker 镜像方式搭建调度中心配置部署“执行器项目” 执行器maven依赖执行器配置执行器组件配置执行器…

人工智能_机器学习088_DBSCAN聚类案例_KMeans聚类算法效果展示---人工智能工作笔记0128

然后我们先来看一下上一节的代码首先导包 import numpy as np 导入数学计算包 import matplotlib.pyplot as plt 导入画图包 from sklearn.cluster import KMeans,DBSCAN 导入算法 from sklearn import datasets 导入数据集包 然后我们再去创建数据 X,y = datasets.make_c…

MyBatis学习一:快速入门

前言 公司要求没办法,前端也要了解一下后端知识,这里记录一下自己的学习 学习教程:黑马mybatis教程全套视频教程,2天Mybatis框架从入门到精通 文档: https://mybatis.net.cn/index.html MyBatis 快速入门&#xf…

纠删码ReedSolomon

随着大数据技术的发展,HDFS作为Hadoop的核心模块之一得到了广泛的应用。为了数据的可靠性,HDFS通过多副本机制来保证。在HDFS中的每一份数据都有两个副本,1TB的原始数据需要占用3TB的磁盘空间,存储利用率只有1/3。而且系统中大部分…

初识Linux下进程

🌎初识进程 初识进程 简单认识一下进程 如何管理进程 进程属性信息 内核运行队列 查看进程 通过系统调用获取进程标识符       父子进程       查看运行中的进程 总结 前言: 我们在电脑上点开的一个个应用,其实就是一个个进程&am…

【输入npm install express出现的报错】

目录 输入:npm install express,出现如下的报错 分析原因 方法1:用管理员的身份进行安装 方法2:更改文件夹的权限 输入:npm install express,出现如下的报错 分析原因: npm在执行安装过程中…

HTML5和JS实现明媚月色效果

HTML5和JS实现明媚月色效果 先给出效果图&#xff1a; 源码如下&#xff1a; <!DOCTYPE html> <html> <head><title>明媚月光效果</title><style>body {margin: 0;overflow: hidden;background-color: #000; /* 添加一个深色背景以便看到…

vscode中增加参数的一个方法

1 在settings.json 文件中增加参数 2. 在参数中配置 这里也是ok的

VMware 虚拟机 ubuntu 20.04 硬盘扩容方法

前言 最近由于需要编译 【RK3568】的 Linux SDK&#xff0c;发现 虚拟机默认的 200G 空间不足了&#xff0c;因此想增加这个 200G 空间的限制&#xff0c;通过网络上查找了一些方法&#xff0c;加上自己亲自验证&#xff0c;确认 硬盘扩容 正常&#xff0c;方法也比较的容易&a…

MySQL--安装与配置与向日葵的基本操作使用

一.MySQL介绍 1.1 MySQL简介 MySQL是一个开源的关系型数据库管理系统&#xff0c;最早由瑞典MySQL AB公司开发。这个数据库系统有着高可靠性、高性能和易用性的特点&#xff0c;在互联网上得到了广泛的应用。MySQL支持SQL语言&#xff0c;可以运行在多种操作系统上&#xff0c…

draw流程图工具导入云原生(CNCF)相关控件

目录 1、通过draw导入xml文件&#xff0c;获取云原生相关的空间 2、引用自己的资源链接&#xff1a; 1、通过draw导入xml文件&#xff0c;获取云原生相关的空间 导入资源图库&#xff0c;资源放在下方&#xff0c;大家可以下载&#xff1a; 2、引用自己的资源链接&#xff1a;…

C#中汉字转区位码

目录 一、关于区位码 1.区位码定义 2.算法 二、实例 三、生成效果 四、程序中的知识点 1.byte[] GetBytes(string s) 2.字节数组转short类型 一、关于区位码 1.区位码定义 区位码是一个4位的十进制数&#xff0c;每个区位码都对应着一个唯一的汉字&#xff0c;区位码…

小白综述:深度学习 OCR 图片文字识别

文章目录 1. OCR 算法流程1.1 传统 OCR 方法1.2 深度学习 OCR 方法1.2.1 two-stage方法&#xff1a;文字检测识别1.2.2 端到端方法 2. 文本检测算法3. 文本识别算法3.1 基于分割的单字符识别方法3.2 基于序列标注的文本行识别方法 1. OCR 算法流程 OCR (Optical Character Rec…

opencv期末练习题(3)附带解析

创建黑色画板&#xff0c;并支持两种画图功能 import mathimport cv2 import numpy as np """ 1. 创建一个黑色画板 2. 输入q退出 3. 输入m切换画图模式两种模式&#xff0c;画矩形和画圆形。用户按住鼠标左键到一个位置然后释放就可以画出对应的图像 "&qu…

Blender:从新手到专家的全方位指南

Blender&#xff0c;这款强大的开源3D建模和渲染软件&#xff0c;已经成为了CG行业的标准工具之一。它不仅拥有丰富的教程资源&#xff0c;而且还在不断发展和完善中。尽管Blender的教程主要集中在国外网站和YouTube上&#xff0c;但其全面的功能和易用性使它成为许多人的首选工…

基于深度卷积神经网络的猴痘分类识别系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 本文详细介绍了一基于深度卷积神经网络的猴痘分类识别系统。采用TensorFlow和Keras框架&#xff0c;通过卷积神经网络&#xff08;CNN&#xff09;进行模型训练和预测&#xff0c;利用迁移学习中的…