Elasticsearch:如何使用 Elasticsearch 进行排序

news2024/11/15 23:33:31

虽然你在唱这首歌时可能会想象圣诞老人,但欧洲民间传说,尤其是阿尔卑斯地区的民间传说,有两个传奇人物圣尼古拉斯和坎普斯。 象征着慷慨和善良的圣尼古拉斯,在 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 中按距离有效地对地理点进行排序

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

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

相关文章

ROS 传感器—相机的使用

在ROS中,相机作为一种重要的传感器设备被广泛应用于机器人视觉、导航定位、目标检测与识别等多种场景。 ROS提供了一系列工具和接口来支持不同类型的相机,包括USB摄像头、GigE Vision相机、FireWire相机以及深度相机(如Kinect或Intel RealSe…

gRCP - 面向未来的第二代 RPC 技术,解析 HTTP2.0 和 Protobuf

目录 一、gRCP - 面向未来的第二代 RPC 技术 1.1、gRPC 简介 1.1.1、gRPC 是个啥? 1.1.2、gRPC 核心设计思路 1.1.3、gRPC 和 ThriftRPC 区别 1.1.4、为什么使用 gRPC?(好处) 1.2、HTTP2.0 协议 1.2.1、回顾 HTTP1.0 和 H…

LeetCode 2807. 在链表中插入最大公约数

给你一个链表的头 head ,每个结点包含一个整数值。 在相邻结点之间,请你插入一个新的结点,结点值为这两个相邻结点值的 最大公约数 。 请你返回插入之后的链表。 两个数的 最大公约数 是可以被两个数字整除的最大正整数。 示例 1&#xf…

高防ip适合防御网站和游戏类的攻击吗?

​  作为站长,要学会并承受得住网站外来攻击的压力,尤其是所属为 DDoS 攻击高发行业的网站类业务及游戏行业,是很容易被竞争对手或者一些伪黑客爱好者盯上的。 加上,有些站长并没有提前了解,就盲目进军了这两个行业&…

imgaug库指南(一):从入门到精通的【图像增强】之旅

文章目录 引言imgaug简介安装和导入imgaug代码示例imgaug的强大之处和用途小结结尾 引言 在深度学习和计算机视觉的世界里,数据是模型训练的基石,其质量与数量直接影响着模型的性能。然而,获取大量高质量的标注数据往往需要耗费大量的时间和…

关于“Python”的核心知识点整理大全59

目录 19.3.2 将数据关联到用户 1. 修改模型Topic models.py 2. 确定当前有哪些用户 3. 迁移数据库 注意 19.3.3 只允许用户访问自己的主题 views.py 19.3.4 保护用户的主题 views.py views.py 19.3.6 将新主题关联到当前用户 views.py 往期快速传送门&#x1f44…

鸿鹄工程项目管理系统源码:Spring Boot带来的快速开发与部署体验

随着企业规模的不断扩大和业务的快速发展,传统的工程项目管理方式已经无法满足现代企业的需求。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,企业需要借助先进的数字化技术进行转型。本文将介绍一款采用Spring CloudSpring BootMybat…

西电期末1017.有序序列插值

一.题目 二.分析与思路 简单题。主要考察简单的排序&#xff0c;最后的插入数据同样不用具体实现&#xff0c;只需在输出时多输出一下即可&#xff0c;注意顺序&#xff01;&#xff01; 三.代码实现 #include<bits/stdc.h>//万能头 int main() {int n;scanf("%d…

MySQL基础篇(一)SQL

视频地址: 黑马程序员 MySQL数据库入门到精通&#xff0c;从mysql安装到mysql高级、mysql优化全囊括 SQL&#xff0c;全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一 标准。 一、SQL通用语…

偏导函数公式以及使用 python 计算

偏导函数 偏导函数是多元函数对其中一个变量的偏导数。对于一个多元函数&#xff0c;其输入变量有两个或更多&#xff0c;而偏导函数则表示对其中一个变量的偏导数&#xff0c;将其他变量视为常数。 设有一个具有 n 个自变量的函数 f(,,...,)&#xff0c;则对于其中的某一个自…

ARM笔记-----输入捕获

输入捕获可以对输入的信号的上升沿、下降沿或者双边沿进行捕获&#xff0c;常用的有测量输入信号的脉 宽&#xff0c;和测量 PWM 输入信号的频率和占空比这两种。 输入捕获的大概的原理 当捕获到信号的跳变沿的时候&#xff0c;把计数器 CNT 的值锁存到捕获寄 存器 CCR 中…

彻底卸载 Microsoft Edge

如果您想学习如何在 Windows 10 上卸载 Microsoft Edge&#xff0c;您并不孤单。尽管目前基于 Chromium 的 Microsoft 浏览器版本比以前的 Edge 版本要好得多&#xff0c;但它仍然是一个不受欢迎的浏览器。 尽管您需要在 PC 上预装浏览器才能安装其他浏览器&#xff0c;但许多…

RK3568平台 input输入子系统

一.input子系统简介 Input 子系统是管理输入的子系统&#xff0c; 和 pinctrl 和 gpio 子系统一样&#xff0c; 都是 Linux 内核针对某一类设备而创建的框架。 input 子系统处理输入事务&#xff0c; 任何输入设备的驱动程序都可以通过 input 输入子系统提供的接口注册到内核&…

[每周一更]-(第53期):Python3和Django环境安装并搭建Django

Python和Django 的安装 Python和Django 兼容情况 django 1.11.x python 2.7 3.4 3.5 3.6 LTS python 目前在用版本 Python 3.6.5 2018-03-28 更新Python 2.7.15 2018-05-01 更新Python 2.7.5 2013-05-12 更新 python和python3安装pip 同时安装上 python2.7.18、python3.11…

3分钟Alibaba Cloud Linux镜像操作系统详解

Alibaba Cloud Linux是基于龙蜥社区OpenAnolis龙蜥操作系统Anolis OS的阿里云发行版&#xff0c;针对阿里云服务器ECS做了大量深度优化&#xff0c;Alibaba Cloud Linux由阿里云官方免费提供长期支持和维护LTS&#xff0c;Alibaba Cloud Linux完全兼容CentOS/RHEL生态和操作方式…

Java 第19章 IO流 课堂练习+本章作业

文章目录 Buffered流拷贝二进制文件创建文件写入文本读取文本文件存读Properties文件 Buffered流拷贝二进制文件 package com.hspedu.chapter19.outputStream;import java.io.*;public class BufferedCopy02 {public static void main(String[] args) {String srcFilePath &q…

杨中科 .NET Core 第一部分.NET Standard

1)不讲C#基础语法和NET基础类库(不需要学过ASPNET等)。需要懂HTML、JavaScript、数据库等。后续会录制基础视频 2)使用Visual Studio 2019 .NET .NET Framework Windows 程序 .NET Core 跨平台程序 .NET Standard 上述两者 遵从的标准 .NET5 开始上述统称为 .NET 新建.NET Sta…

DrGraph原理示教 - OpenCV 4 功能 - 颜色变幻

二值化是逐像素处理&#xff0c;而逐像素处理会有很多效果&#xff0c;这主要是给人眼看的&#xff0c;因为像素值的变化&#xff0c;直观的就是图像变化&#xff0c;比如颜色。 颜色变幻处理 OpenCV提供了一些图片&#xff0c;如下&#xff1a; 粗看是一些风格&#xff0c;…

设计模式(4)--对象行为(9)--策略

1. 意图 定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可相互替换。 本模式使得算法可独立于使用它的客户而变化。 2. 三种角色 抽象策略(Strategy)、具体策略(Concrete Strategy)、上下文环境(Context) 3. 优点 3.1 可重用的相关算法系列。 3.2 …

SpringBoot学习(三)-员工管理系统开发(重在理解)

注&#xff1a;此为笔者学习狂神说SpringBoot的笔记&#xff0c;其中包含个人的笔记和理解&#xff0c;仅做学习笔记之用&#xff0c;更多详细资讯请出门左拐B站&#xff1a;狂神说!!! 本文是基于狂神老师SpringBoot教程中的员工管理系统从0到1的实践和理解。该系统应用SpringB…