【最佳实践】使用 Elasticsearch SQL 实现数据查询

您所在的位置:网站首页 sql入门与数据分析实践 【最佳实践】使用 Elasticsearch SQL 实现数据查询

【最佳实践】使用 Elasticsearch SQL 实现数据查询

2024-07-06 13:40| 来源: 网络整理| 查看: 265

Elasticsearch 是一个全文搜索引擎,具有您期望的所有优点,例如相关性评分,词干,同义词等。 而且,由于它是具有水平可扩展的分布式文档存储,因此它可以处理数十亿行数据,而不会费劲。针对 Elasticsearch 专业人员来说,大多数人喜欢使用 DSL 来进行搜索,但是对于一些不是那么专业的人员来说,他们更为熟悉的是 SQL 语句。如何让他们对 Elasticsearch 的数据进行查询是一个问题。借助 Elasticsearch SQL,你可以使用熟悉的查询语法访问全文搜索,超快的速度和轻松的可伸缩性。X-Pack 包含一项 SQL 功能,可对 Elasticsearch 索引执行 SQL 查询并以表格格式返回结果。

数据准备

1、开通阿里云 Elasticsearch 1核2G 免费测试环境2、进入 Kibana

image.png

点击上面的 “Load a data set and a Kibana dashboard”:

image.png

点击上面的 Add data,这样我们就可以完成实验数据的导入了。在 Elasticsearch 中,我们会找到一个叫kibana_sample_data_flights 的索引。

SQL 实操 查询有哪些索引

根据 Elasticsearch 的文档 ,我们可以使用如下的命令来查看有哪些索引:

POST /_sql?format=txt { "query": "SHOW tables" }

上面的命令显示结果:

image.png

检索 Elasticsearch schema 信息:DSL vs SQL

首先,我们确定表/索引的 schema 以及可供我们使用的字段。 我们将通过 REST 界面执行此操作:

POST /_sql { "query": """ DESCRIBE kibana_sample_data_flights """ }

上面命令的结果:

{ "columns" : [ { "name" : "column", "type" : "keyword" }, { "name" : "type", "type" : "keyword" }, { "name" : "mapping", "type" : "keyword" } ], "rows" : [ [ "AvgTicketPrice", "REAL", "float" ], [ "Cancelled", "BOOLEAN", "boolean" ], [ "Carrier", "VARCHAR", "keyword" ], [ "Dest", "VARCHAR", "keyword" ], [ "DestAirportID", "VARCHAR", "keyword" ], [ "DestCityName", "VARCHAR", "keyword" ], [ "DestCountry", "VARCHAR", "keyword" ], [ "DestLocation", "GEOMETRY", "geo_point" ], [ "DestRegion", "VARCHAR", "keyword" ], [ "DestWeather", "VARCHAR", "keyword" ], [ "DistanceKilometers", "REAL", "float" ], [ "DistanceMiles", "REAL", "float" ], [ "FlightDelay", "BOOLEAN", "boolean" ], [ "FlightDelayMin", "INTEGER", "integer" ], [ "FlightDelayType", "VARCHAR", "keyword" ], [ "FlightNum", "VARCHAR", "keyword" ], [ "FlightTimeHour", "VARCHAR", "keyword" ], [ "FlightTimeMin", "REAL", "float" ], [ "Origin", "VARCHAR", "keyword" ], [ "OriginAirportID", "VARCHAR", "keyword" ], [ "OriginCityName", "VARCHAR", "keyword" ], [ "OriginCountry", "VARCHAR", "keyword" ], [ "OriginLocation", "GEOMETRY", "geo_point" ], [ "OriginRegion", "VARCHAR", "keyword" ], [ "OriginWeather", "VARCHAR", "keyword" ], [ "dayOfWeek", "INTEGER", "integer" ], [ "timestamp", "TIMESTAMP", "datetime" ] ] }

也可以通过 url 参数 format = txt 以表格形式格式化以上响应。 例如:

POST /_sql?format=txt { "query": "DESCRIBE kibana_sample_data_flights" }

上面命令查询的结果是:

column | type | mapping ------------------+---------------+--------------- AvgTicketPrice |REAL |float Cancelled |BOOLEAN |boolean Carrier |VARCHAR |keyword Dest |VARCHAR |keyword DestAirportID |VARCHAR |keyword DestCityName |VARCHAR |keyword DestCountry |VARCHAR |keyword DestLocation |GEOMETRY |geo_point DestRegion |VARCHAR |keyword DestWeather |VARCHAR |keyword DistanceKilometers|REAL |float DistanceMiles |REAL |float FlightDelay |BOOLEAN |boolean FlightDelayMin |INTEGER |integer FlightDelayType |VARCHAR |keyword FlightNum |VARCHAR |keyword FlightTimeHour |VARCHAR |keyword FlightTimeMin |REAL |float Origin |VARCHAR |keyword OriginAirportID |VARCHAR |keyword OriginCityName |VARCHAR |keyword OriginCountry |VARCHAR |keyword OriginLocation |GEOMETRY |geo_point OriginRegion |VARCHAR |keyword OriginWeather |VARCHAR |keyword dayOfWeek |INTEGER |integer timestamp |TIMESTAMP |datetime

是不是感觉回到 SQL 时代啊:)

向前迈进,只要提供来自 REST API 的示例响应,我们就会使用上面显示的表格响应结构。 要通过控制台实现相同的查询,需要使用以下命令登录:

./bin/elasticsearch-sql-cli http://localhost:9200

我们可在屏幕上看到如下的画面:

image.png

太神奇了。我们直接看到 SQL 的命令提示符了。在上面的命令行中,我们打入如下的命令:

DESCRIBE kibana_sample_data_flights;

image.png

这个结果和我们在 Kibana 中得到的结果是一样的。

上面的 schema 也会随对在 SELECT 子句中显示的字段的任何查询一起返回,从而为任何潜在的驱动程序提供格式化或对结果进行操作所需的必要类型信息。 例如,考虑带有 LIMIT 子句的简单 SELECT,以使响应简短。 默认情况下,我们返回 1000 行。

我们发现索引的名字 kibana_sample_data_flights 比较长,为了方便,我们来创建一个 alias:

PUT /kibana_sample_data_flights/_alias/flights

这样在以后的操作中,当我们使用 flights 的时候,其实也就是对索引 kibana_sample_data_flights 进行操作。

我们执行如下的命令:

POST /_sql?format=txt { "query": "SELECT FlightNum FROM flights LIMIT 1" }

显示结果:

FlightNum --------------- 9HY9SWR

相同的 REST 请求/响应由 JDBC 驱动程序和控制台使用:

sql> SELECT OriginCountry, OriginCityName FROM flights LIMIT 1; OriginCountry | OriginCityName ---------------+----------------- DE |Frankfurt am Main

请注意,如果在任何时候请求的字段都不存在(区分大小写),则表格式和强类型存储区的语义意味着将返回错误-这与 Elasticsearch 行为不同,在该行为中,根本不会返回该字段。 例如,将上面的内容修改为使用字段 “OrigincityName” 而不是 “OriginCityName” 会产生有用的错误消息:

sql> SELECT OriginCountry, OrigincityName FROM flights LIMIT 1; Bad request [Found 1 problem(s) line 1:23: Unknown column [OrigincityName], did you mean any of [OriginCityName, DestCityName]?]

同样,如果我们尝试在不兼容的字段上使用函数或表达式,则会出现相应的错误。 通常,分析器在验证 AST 时会较早失败。 为了实现这一点,Elasticsearch 必须了解每个字段的索引映射和功能。 因此,任何具有安全性访问 SQL 接口的客户端都需要适当的权限。

如果我们继续提供每一个请求和相应的回复,我们将最终获得一篇冗长的博客文章! 为了简洁起见,以下是一些带有感兴趣的注释的日益复杂的查询。

使用 WHERE 及 ORDER BY 来 SELECT

“找到飞行时间超过5小时的美国最长10班航班。”

POST /_sql?format=txt { "query": """ SELECT OriginCityName, DestCityName FROM flights WHERE FlightTimeHour > 5 AND OriginCountry='US' ORDER BY FlightTimeHour DESC LIMIT 10 """ }

显示结果是:

OriginCityName | DestCityName ---------------+------------------- Chicago |Oslo Cleveland |Seoul Denver |Chitose / Tomakomai Nashville |Verona Minneapolis |Tokyo Portland |Treviso Spokane |Vienna Kansas City |Zurich Kansas City |Shanghai Los Angeles |Zurich

限制行数的运算符因 SQL 实现而异。 对于 Elasticsearch SQL,我们在实现 LIMIT 运算符时与 Postgresql/Mysql 保持一致。

Math

只是一些随机数字...

sql> SELECT ((1 + 3) * 1.5 / (7 - 6)) * 2 AS random; random --------------- 12.0

这代表服务器端对功能执行某些后处理的示例。 没有等效的 Elasticsearch DSL 查询。

Functions & Expressions

“在2月份之后查找所有航班,该航班的飞行时间大于5小时,并且按照时间最长来排序。”

POST /_sql?format=txt { "query": """ SELECT MONTH_OF_YEAR(timestamp), OriginCityName, DestCityName FROM flights WHERE FlightTimeHour > 1 AND MONTH_OF_YEAR(timestamp) > 2 ORDER BY FlightTimeHour DESC LIMIT 10 """ }

显示结果是:

MONTH_OF_YEAR(timestamp)|OriginCityName | DestCityName ------------------------+---------------+--------------- 4 |Chicago |Oslo 4 |Osaka |Spokane 4 |Quito |Tucson 4 |Shanghai |Stockholm 5 |Tokyo |Venice 5 |Tokyo |Venice 5 |Tokyo |Venice 5 |Buenos Aires |Treviso 5 |Amsterdam |Birmingham 5 |Edmonton |Milan

这些功能通常需要在 Elasticsearch 中运用 Painless 变形才能达到等效的效果,而 SQL 的功能声明避免任何脚本编写。 还要注意我们如何在 WHERE 和 SELECT 子句中使用该函数。 WHERE 子句组件被下推到 Elasticsearch,因为它影响结果计数。 SELECT 函数由演示中的服务器端插件处理。

请注意,可用功能列表可通过 “SHOW FUNCTIONS” 检索

sql> SHOW FUNCTIONS; name | type -----------------+--------------- AVG |AGGREGATE COUNT |AGGREGATE FIRST |AGGREGATE FIRST_VALUE |AGGREGATE LAST |AGGREGATE LAST_VALUE |AGGREGATE MAX |AGGREGATE ...

将其与我们之前的数学能力相结合,我们可以开始制定查询,对于大多数 DSL 用户来说,查询将非常复杂。

“找出最快的2个航班(速度)的距离和平均速度,这些航班在星期一,星期二或星期三上午9点至11点之间离开,并且距离超过500公里。 将距离和速度四舍五入到最接近的整数。 如果速度相等,请先显示最长的时间。”

首先我们在上面的 DESCRIBE kibana_sample_data_flights 命令的输出中,我们可以看到 FlightTimeHour 是一个 keyword。这个显然是不对的,因为它是一个数值。也许在最初的设计时这么想的。我们需要把这个字段改为 float 类型的数据。

PUT flight1 { "mappings": { "properties": { "AvgTicketPrice": { "type": "float" }, "Cancelled": { "type": "boolean" }, "Carrier": { "type": "keyword" }, "Dest": { "type": "keyword" }, "DestAirportID": { "type": "keyword" }, "DestCityName": { "type": "keyword" }, "DestCountry": { "type": "keyword" }, "DestLocation": { "type": "geo_point" }, "DestRegion": { "type": "keyword" }, "DestWeather": { "type": "keyword" }, "DistanceKilometers": { "type": "float" }, "DistanceMiles": { "type": "float" }, "FlightDelay": { "type": "boolean" }, "FlightDelayMin": { "type": "integer" }, "FlightDelayType": { "type": "keyword" }, "FlightNum": { "type": "keyword" }, "FlightTimeHour": { "type": "float" }, "FlightTimeMin": { "type": "float" }, "Origin": { "type": "keyword" }, "OriginAirportID": { "type": "keyword" }, "OriginCityName": { "type": "keyword" }, "OriginCountry": { "type": "keyword" }, "OriginLocation": { "type": "geo_point" }, "OriginRegion": { "type": "keyword" }, "OriginWeather": { "type": "keyword" }, "dayOfWeek": { "type": "integer" }, "timestamp": { "type": "date" } } } }

我们需要 reindex 这个索引。

POST _reindex { "source": { "index": "flights" }, "dest": { "index": "flight1" } }

那么现在 flight1 的数据中,FlightTimeHour 字段将会是一个 float 的类型。我们再次重新设置 alias 为 flights:

POST _aliases { "actions": [ { "add": { "index": "flight1", "alias": "flights" } }, { "remove": { "index": "kibana_sample_data_flights", "alias": "flights" } } ] }

那么现在 flights 将是指向 flight1 的一个 alias。我们使用如下的 SQL 语句来查询:

sql> SELECT timestamp, FlightNum, OriginCityName, DestCityName, ROUND(DistanceMiles) AS distance, ROUND(DistanceMiles/FlightTimeHour) AS speed, DAY_OF_WEEK(timestamp) AS day_of_week FROM flights WHERE DAY_OF_WEEK(timestamp) >= 0 AND DAY_OF_WEEK(timestamp) =9 AND HOUR_OF_DAY(timestamp)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3