SQL 查询语言

Solr 支持广泛的 SQL 语法。

SQL 解析器不区分大小写

Solr 用来转换 SQL 语句的 SQL 解析器不区分大小写。但是,为了便于阅读,此页面上的所有示例都使用大写关键字。

仅在使用 LIMIT 时才支持 SELECT *

通常,您应该显式投影每个查询需要返回的字段,并避免使用 select *,这通常被认为是不良做法,并且当底层 Solr 模式更改时可能导致意外结果。但是,Solr 支持 select * 用于也包含 LIMIT 子句的查询。所有存储的字段和 score 都将包含在结果中。如果没有 LIMIT 子句,则只能返回启用了 docValues 的字段。

转义保留字

如果在 SQL 查询中使用了保留字,SQL 解析器将返回错误。保留字可以使用反引号转义并包含在查询中。例如

select `from` from emails

SELECT 语句

Solr 支持有限和无限的 select 查询。两种查询类型的语法相同,只是 SQL 语句中存在 LIMIT 子句。但是,它们的执行计划和对数据存储方式的要求非常不同。下面的部分探讨了这两种类型的查询。

带有 LIMIT 的基本 SELECT 语句

有限的 select 查询遵循以下基本语法

SELECT fieldA as fa, fieldB as fb, fieldC as fc FROM tableA WHERE fieldC = 'term1 term2' ORDER BY fa desc LIMIT 100

我们通过此示例介绍了许多语法选项,因此让我们逐步了解以下内容。

WHERE 子句和布尔谓词

WHERE 子句的谓词一侧必须有一个字段。不支持两个常量 (5 < 10) 或两个字段 (fielda > fieldb)。也不支持子查询。

WHERE 子句允许将 Solr 的搜索语法注入到 SQL 查询中。例如:

WHERE fieldC = 'term1 term2'

上面的谓词将对 fieldC 中的短语 'term1 term2' 执行全文搜索。

要执行非短语查询,只需在单引号内添加括号。例如:

WHERE fieldC = '(term1 term2)'

上面的谓词将在 fieldC 中搜索 term1 OR term2。

Solr 范围查询语法可以按如下方式使用:

WHERE fieldC = '[0 TO 100]'

可以按如下方式指定复杂的布尔查询:

WHERE ((fieldC = 'term1' AND fieldA = 'term2') OR (fieldB = 'term3'))

要指定 NOT 查询,可以使用 AND NOT 语法,如下所示:

WHERE (fieldA = 'term1') AND NOT (fieldB = 'term2')

支持的 WHERE 运算符

SQL 查询接口支持并向下推送大多数常见的 SQL 运算符,特别是:

运算符

描述

示例

Solr 查询

=

等于

fielda = 10

fielda:10

<>

不等于

fielda <> 10

-fielda:10

>

大于

fielda > 10

fielda:{10 TO *]

>=

大于或等于

fielda >= 10

fielda:[10 TO *]

<

小于

fielda < 10

fielda:[* TO 10}

<=

小于或等于

fielda <= 10

fielda:[* TO 10]

IN

指定多个值(多个 OR 子句的简写)

fielda IN (10,20,30)

(fielda:10 OR fielda:20 OR fielda:30)

LIKE

在字符串或文本字段上使用通配符匹配

fielda LIKE 'day%'

{!complexphrase}fielda:"day*"

BETWEEN

范围匹配

fielda BETWEEN 2 AND 4

fielda: [2 TO 4]

IS NULL

匹配具有空值的列

fielda IS NULL

(*:* -field:*)

IS NOT NULL

匹配具有值的列

fielda IS NOT NULL

field:*

使用 <> 代替 != 表示不等于

IN、LIKE、BETWEEN 支持 NOT 关键字来查找条件不为真的行,例如 fielda NOT LIKE 'day%'

字符串字面量必须用单引号括起来;双引号表示数据库对象,而不是字符串字面量。

可以使用星号通配符进行简单的 LIKE 匹配,例如 field = 'sam*';这是 Solr 特有的,而不是 SQL 标准的一部分。

IN 子句的最大值数量受为您的集合配置的 maxBooleanClauses 限制。

当在多值字段上执行 ANDed 范围查询时,如果 ANDed 谓词看起来是不相交的集合,则 Apache Calcite 会短路为零结果。例如,b_is <= 2 AND b_is >= 5 从单值字段的角度来看,对 Calcite 来说似乎是不相交的集合。但是,对于多值字段,情况可能并非如此,因为 Solr 可能会匹配文档。解决方法是直接在用括号括起来的等于表达式中使用 Solr 查询语法:b_is = '(+[5 TO *] +[* TO 2])'

ORDER BY 子句

ORDER BY 子句直接映射到 Solr 字段。支持多个 ORDER BY 字段和方向。

在指定了限制的查询中,score 字段在 ORDER BY 子句中被接受。

如果 ORDER BY 子句包含 GROUP BY 子句中的确切字段,则返回的结果没有限制。如果 ORDER BY 子句包含与 GROUP BY 子句不同的字段,则会自动应用 100 的限制。要增加此限制,您必须在 LIMIT 子句中指定一个值。

Order by 字段区分大小写。

使用 FETCH 的 OFFSET

指定 ORDER BY 子句的查询也可以使用 OFFSET(从 0 开始的索引)和 FETCH 运算符来分页浏览结果;不支持没有 FETCH 的 OFFSET,并且会生成异常。例如,以下查询请求第二页 10 个结果:

ORDER BY ... OFFSET 10 FETCH NEXT 10 ROWS ONLY

使用 SQL 进行分页与使用 start 和 rows 在 Solr 查询中进行分页存在相同的性能损失,在分布式查询中必须从每个分片过度获取 OFFSET + LIMIT 文档,然后对来自每个分片的结果进行排序,以生成返回给客户端的结果页。因此,此功能应仅用于较小的 OFFSET / FETCH 大小,例如每个分片最多分页 10,000 个文档。Solr SQL 不会强制执行任何硬性限制,但是您深入结果的程度越深,每个后续的页面请求都会花费更长的时间并消耗更多的资源。SQL 中不支持 Solr 用于深度分页的 cursorMark 功能;请改用没有 LIMIT 的 SQL 查询通过 /export 处理程序流式传输大型结果集。SQL OFFSET 不适用于深度分页类型的用例。

LIMIT 子句

将结果集限制为指定的大小。在上面的示例中,子句 LIMIT 100 将结果集限制为 100 条记录。

有限制和无限限制的查询之间有一些区别需要注意:

有限制查询支持字段列表和 ORDER BY 中的 score。无限查询不支持。

有限制查询允许字段列表中的任何存储字段。无限查询要求字段存储为 DocValues 字段。

有限制查询允许 ORDER BY 列表中的任何索引字段。无限查询要求字段存储为 DocValues 字段。

如果一个字段被索引但未存储或没有 docValues,则可以在该字段上进行过滤,但不能在结果中返回它。

SELECT DISTINCT 查询

SQL 接口支持 SELECT DISTINCT 查询的 MapReduce 和 Facet 实现。

MapReduce 实现将元组混洗到执行 Distinct 操作的工作节点。此实现可以在极高基数字段上执行 Distinct 操作。

Facet 实现使用 JSON Facet API 将 Distinct 操作下推到搜索引擎中。此实现专为低到中等基数字段上的高性能、高 QPS 场景而设计。

JDBC 驱动程序和 HTTP 接口中都有 aggregationMode 参数,用于选择底层实现(map_reduce 或 facet)。两种实现的 SQL 语法是相同的。

SELECT distinct fieldA as fa, fieldB as fb FROM tableA ORDER BY fa desc, fb desc

统计函数

SQL 接口支持对数值字段计算的简单统计信息。支持的函数包括 COUNT(*)、COUNT(DISTINCT field)、APPROX_COUNT_DISTINCT(field)、MIN、MAX、SUM 和 AVG。

由于这些函数从不需要对数据进行混洗,因此会将聚合下推到搜索引擎中,并由 Stats Component 生成。

SELECT COUNT(*) as count, SUM(fieldB) as sum FROM tableA WHERE fieldC = 'Hello'

APPROX_COUNT_DISTINCT 指标使用 Solr 的 HyperLogLog (hll) 统计函数来计算给定字段的近似基数,当查询性能很重要且不需要精确计数时应使用此指标。

GROUP BY 聚合

SQL 接口还支持 GROUP BY 聚合查询。

与 SELECT DISTINCT 查询一样,SQL 接口支持 MapReduce 实现和 Facet 实现。MapReduce 实现可以在极高基数字段上构建聚合。Facet 实现提供对具有中等基数字段的高性能聚合。

具有聚合的基本 GROUP BY

以下是请求聚合的 GROUP BY 查询的基本示例:

SELECT fieldA as fa, fieldB as fb, COUNT(*) as count, SUM(fieldC) as sum, AVG(fieldY) as avg

FROM tableA

WHERE fieldC = 'term1 term2'

GROUP BY fa, fb

HAVING sum > 1000

ORDER BY sum asc

LIMIT 100

让我们将其分解为几个部分:

列标识符和别名

列标识符可以包含 Solr 索引中的字段和聚合函数。支持的聚合函数有:

COUNT(*):计算一组存储桶的记录数。

SUM(field):对一组存储桶中的数值字段求和。

AVG(field):计算一组存储桶中的数值字段的平均值。

MIN(field):返回一组存储桶中数值字段的最小值。

MAX(field):返回一组存储桶中数值字段的最大值。

字段列表中的非函数字段确定要计算聚合的字段。

目前 Solr 不支持使用 COUNT(DISTINCT ) 计算每个组中特定字段的不同值的数量;只能为每个 GROUP BY 维度计算 COUNT(*)。

HAVING 子句

HAVING 子句可以包含字段列表中列出的任何函数。支持如下复杂的 HAVING 子句:

SELECT fieldA, fieldB, COUNT(*), SUM(fieldC), AVG(fieldY)

FROM tableA

WHERE fieldC = 'term1 term2'

GROUP BY fieldA, fieldB

HAVING ((SUM(fieldC) > 1000) AND (AVG(fieldY) <= 10))

ORDER BY SUM(fieldC) ASC

LIMIT 100

聚合模式

Solr 的 SQL 功能可以通过两种方式处理聚合(结果分组):

facet:这是 默认 的聚合模式,它使用 JSON Facet API 或 StatsComponent 进行聚合。在这种情况下,聚合逻辑被下推到搜索引擎中,只有聚合通过网络发送。这是 Solr 的正常运行模式。当 GROUP BY 字段的基数较低到中等时,速度很快。但是当 GROUP BY 字段中存在高基数字段时,它会崩溃。

map_reduce:此实现将元组混洗到工作节点,并在工作节点上执行聚合。它涉及对整个结果集进行排序和分区,然后将其发送到工作节点。在这种方法中,元组到达工作节点时会按 GROUP BY 字段排序。然后,工作节点可以一次汇总一个组的聚合。这允许无限基数聚合,但您需要付出将整个结果集通过网络发送到工作节点的代价。

这些模式在向 Solr 发送请求时,通过 aggregationMode 属性进行定义。

聚合模式的选择取决于您所使用的字段的基数。如果您分组的字段的基数较低到中等,则 “facet” 聚合模式将提供更高的性能,因为它只返回最终的分组,非常类似于现在 facet 的工作方式。但是,如果您的字段具有高基数,则使用工作节点的 “map_reduce” 聚合模式会提供更高的性能。