SQL 查询语言
Solr SQL 模块通过将 SQL 与 Solr 的全文搜索功能无缝结合,将 SQL 查询功能引入 Solr。支持 MapReduce 样式和 JSON 分面 API 聚合,这意味着可以使用 SQL 查询来支持高查询量和高基数用例。
模块
这通过 sql
Solr 模块提供,该模块需要在使用前启用。
SQL 架构
SQL 接口允许将 SQL 查询发送到 Solr,并作为响应获取流式传输回的文档。在底层,Solr 的 SQL 接口使用 Apache Calcite SQL 引擎将 SQL 查询转换为作为 流表达式实现的物理查询计划。
有关 Solr 如何支持 Solr SQL 查询的更多信息,请参见下面的 配置 部分。
Solr 集合和数据库表
在标准的 SELECT
语句(如 SELECT <expressions> FROM <table>
)中,表名对应于 Solr 集合名。表名不区分大小写。
SQL 查询中的列名直接映射到要查询的集合的 Solr 索引中的字段。这些标识符区分大小写。支持别名,并且可以在 ORDER BY
子句中引用它们。
SELECT *
语法表示所有字段,仅受支持于带有 LIMIT
子句的查询。score
字段仅可用于包含 LIMIT
子句的查询。
例如,我们可以索引 Solr 的示例文档,然后构建类似这样的 SQL 查询
SELECT name_exact as name, manu as mfr, price as retail FROM techproducts ORDER BY retail DESC
我们使用的 Solr 中的集合是“techproducts”,我们要求返回“name_exact”、“manu”和“price”字段,别名“retail”用于排序,以显示最昂贵的到最便宜的产品。
Solr SQL 语法
Solr 支持广泛的 SQL 语法。
SQL 解析器不区分大小写
Solr 用于翻译 SQL 语句的 SQL 解析器不区分大小写。但是,为了便于阅读,本页上的所有示例都使用大写关键字。 |
仅在使用 LIMIT 时才支持 SELECT *
通常,你应该明确地投影每个查询需要返回的字段,避免使用 |
SELECT 语句
Solr 支持有限和无限选择查询。除了 SQL 语句中的 LIMIT
子句之外,这两种类型的查询之间的语法是相同的。但是,它们有非常不同的执行计划和对数据存储方式的不同要求。以下部分探讨了这两种类型的查询。
WHERE 子句和布尔谓词
WHERE 子句必须在谓词的一侧有一个字段。不支持两个常量 |
WHERE
子句允许将 Solr 的搜索语法注入到 SQL 查询中。在示例中
WHERE fieldC = 'term1 term2'
上面的谓词将在 fieldC 中执行短语“term1 term2”的全文搜索。
要执行非短语查询,只需在单引号内添加括号。例如
WHERE fieldC = '(term1 term2)'
上述谓词在 fieldC
中搜索 term1
或 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 查询 |
---|---|---|---|
= |
等于 |
|
|
<> |
不等于 |
|
|
> |
大于 |
|
|
>= |
大于或等于 |
|
|
< |
小于 |
|
|
<= |
小于或等于 |
|
|
IN |
指定多个值(多个 OR 子句的简写) |
|
|
LIKE |
字符串或文本字段中的通配符匹配 |
|
|
BETWEEN |
范围匹配 |
|
|
IS NULL |
匹配具有空值的列 |
|
(*:* -field:*) |
IS NOT NULL |
匹配具有值的列 |
|
|
-
对于不等于,请使用
<>
代替!=
-
IN、LIKE、BETWEEN 支持 NOT 关键字,以查找条件不为真的行,例如
fielda NOT LIKE 'day%'
-
字符串文本必须用单引号引起来;双引号表示数据库对象,而不是字符串文本。
-
可以使用带有星号通配符的简单 LIKE,例如
field = 'sam*'
;这是 Solr 特有的,不属于 SQL 标准。 -
IN
子句的最大值数量受为你的集合配置的maxBooleanClauses
限制。 -
在对多值字段执行 AND 范围查询时,如果 AND 谓词似乎是分离的集合,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
子句中指定一个值。
按字段排序区分大小写。
OFFSET 与 FETCH
指定 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 不会强制执行任何硬限制,但您深入搜索结果的程度越深,每个后续页面请求所花费的时间就越长,并且消耗的资源就越多。Solr 的 cursorMark
深度分页功能在 SQL 中不受支持;相反,使用没有 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 场景而设计。
aggregationMode
参数在 JDBC 驱动程序和 HTTP 接口中均可用,用于选择底层实现(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 组件生成。
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 实现提供对中等基数字段的高性能聚合。
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”聚合模式与工作节点一起提供了性能更高的选项。
配置
用于 SQL 接口的请求处理程序配置为隐式加载,这意味着开始使用此功能几乎不需要做什么。
/sql 请求处理程序
/sql
处理程序是并行 SQL 接口的前端。所有 SQL 查询都发送到 /sql
处理程序进行处理。该处理程序还在 map_reduce
模式下运行 GROUP BY
和 SELECT DISTINCT
查询时协调分布式 MapReduce 作业。默认情况下,/sql
处理程序将从其自己的集合中选择工作节点来处理分布式操作。在此默认情况下,/sql
处理程序所在的集合充当 MapReduce 查询的默认工作者集合。
默认情况下,/sql
请求处理程序配置为隐式处理程序,这意味着它始终在每个 Solr 安装中启用,并且不需要进一步配置。
SQL 请求授权
如果您的 Solr 集群配置为使用 基于规则的授权插件,则需要对您打算针对其执行 SQL 查询的所有集合的 /sql
、/select
和 /export
端点授予 GET
和 POST
权限。/select
端点用于 LIMIT
查询,而 /export
处理程序用于没有 LIMIT
的查询,因此在大多数情况下,您需要授予对两者都访问权限。如果您正在为 /sql
处理程序使用工作集合,则只需要为工作集合授予对 /sql
端点的访问权限,而无需授予对数据层中集合的访问权限。在后台,SQL 处理程序还使用内部 Solr 服务器标识向 /admin/luke
端点发送请求以获取集合的模式元数据。因此,您无需为用户授予对 /admin/luke
端点的明确权限来执行 SQL 查询。
如下文 最佳实践 部分所述,您可能希望为并行化 SQL 查询设置一个单独的集合。如果您有高基数字段和大量数据,请务必查看该部分并考虑使用单独的集合。 |
/stream 和 /export 请求处理程序
流式 API 是一个可扩展的并行计算框架,用于 SolrCloud。 流式表达式 为流式 API 提供了一个查询语言和一个序列化格式。
流式 API 提供对快速 MapReduce 的支持,使其能够对极大型数据集执行并行关系代数。在底层,SQL 接口使用 Apache Calcite SQL 解析器解析 SQL 查询。然后它将查询转换为并行查询计划。并行查询计划使用流式 API 和流式表达式表示。
与 /sql
请求处理程序一样,/stream
和 /export
请求处理程序配置为隐式处理程序,并且不需要进一步配置。
字段
在某些情况下,SQL 查询中使用的字段必须配置为 DocValue 字段。如果查询是无限的,则所有字段都必须是 DocValue 字段。如果查询是有限的(使用 limit
子句),则字段不必启用 DocValues。
多值字段
项目列表中的多值字段将作为值的 |
JDBC 驱动程序
JDBC 驱动程序随 SolrJ 一起提供。以下是使用 JDBC 驱动程序创建连接并执行查询的示例代码
Connection con = null;
try {
con = DriverManager.getConnection("jdbc:solr://" + zkHost + "?collection=collection1&aggregationMode=map_reduce&numWorkers=2");
stmt = con.createStatement();
rs = stmt.executeQuery("SELECT a_s, sum(a_f) as sum FROM collection1 GROUP BY a_s ORDER BY sum desc");
while(rs.next()) {
String a_s = rs.getString("a_s");
double s = rs.getDouble("sum");
}
} finally {
rs.close();
stmt.close();
con.close();
}
连接 URL 必须包含 zkHost
和 collection
参数。集合必须是指定 ZooKeeper 主机上的有效 SolrCloud 集合。集合还必须使用 /sql
处理程序进行配置。aggregationMode
和 numWorkers
参数是可选的。
HTTP 接口
Solr 通过 /sql
处理程序接受 SQL 查询。
以下是执行 facet 模式 SQL 聚合查询的示例 curl 命令
curl --data-urlencode 'stmt=SELECT to, count(*) FROM collection4 GROUP BY to ORDER BY count(*) desc LIMIT 10' http://localhost:8983/solr/collection4/sql?aggregationMode=facet
以下是示例结果集
{"result-set":{"docs":[
{"count(*)":9158,"to":"[email protected]"},
{"count(*)":6244,"to":"[email protected]"},
{"count(*)":5874,"to":"[email protected]"},
{"count(*)":5867,"to":"[email protected]"},
{"count(*)":5595,"to":"[email protected]"},
{"count(*)":4904,"to":"[email protected]"},
{"count(*)":4622,"to":"[email protected]"},
{"count(*)":3819,"to":"[email protected]"},
{"count(*)":3678,"to":"[email protected]"},
{"count(*)":3653,"to":"[email protected]"},
{"EOF":"true","RESPONSE_TIME":10}]}
}
请注意,结果集是一个元组数组,其中包含与 SQL 列列表匹配的键/值对。最后一个元组包含 EOF 标志,表示流的结束。
并行 SQL 查询
前面的部分描述了 SQL 接口如何将 SQL 语句转换为流表达式。请求的一个参数是 aggregationMode
,它定义查询是否应使用类似 MapReduce 的混洗技术或将操作推送到搜索引擎中。
并行查询
并行 SQL 架构包含三个逻辑层:SQL 层、处理程序 层和数据表 层。默认情况下,SQL 层和处理程序层会折叠到同一个物理 SolrCloud 集合中。
SQL 层
SQL 层是 /sql
处理程序所在的位置。/sql
处理程序获取 SQL 查询并将其转换为并行查询计划。然后,它选择工作程序节点来执行该计划,并将查询计划发送到每个工作程序节点以并行运行。
工作程序节点执行查询计划后,/sql
处理程序随后执行工作程序节点返回的元组的最终合并。
工作程序层
工作程序层中的工作程序从 /sql
处理程序接收查询计划并执行并行查询计划。并行执行计划包括需要在数据表层上进行的查询以及满足查询所需的关系代数。分配给查询的每个工作程序节点从数据表中随机处理 1/N 的元组。工作程序节点执行查询计划并将元组流式传输回工作程序节点。
数据表层
数据表层是表所在的位置。每个表都是其自己的 SolrCloud 集合。数据表层从工作程序节点接收查询并发出元组(搜索结果)。数据表层还处理发送到工作程序的元组的初始排序和分区。这意味着元组在进入网络之前始终进行排序和分区。分区元组直接以正确的排序顺序发送到正确的工作程序节点,以便进行缩减。
上图将三个层分解为不同的 SolrCloud 集合,以提高清晰度。实际上,/sql
处理程序和工作程序集合默认共享同一个集合。
该图像显示了单个并行 SQL 查询(MapReduce 上的 SQL)的网络流。当 map_reduce 聚合模式用于 GROUP BY 聚合或 SELECT DISTINCT 查询时,将使用此网络流。当使用 facet 聚合模式时,将使用传统的 SolrCloud 网络流(不含工作程序)。 |
以下是流程说明
-
客户端将 SQL 查询发送到
/sql
处理程序。该请求由单个/sql
处理程序实例处理。 -
/sql
处理程序解析 SQL 查询并创建并行查询计划。 -
查询计划被发送到工作程序节点(以绿色显示)。
-
工作程序节点并行执行该计划。该图表显示了每个工作程序节点联系数据表层中的一个集合(以蓝色显示)。
-
数据表层中的集合是 SQL 查询中的表。请注意,该集合有五个分片,每个分片有 3 个副本。
-
请注意,每个工作程序从每个分片中联系一个副本。由于有 5 个工作程序,因此每个工作程序从每个分片中返回 1/5 的搜索结果。分区在数据表层内部完成,因此在网络中不会出现数据重复。
-
另外,请注意,使用此设计,数据层中的所有副本都会同时对数据进行洗牌(排序和分区)。随着分片、副本和工作器数量的增加,此设计允许将大量的计算能力应用于单个查询。
-
工作器节点并行处理从数据表层返回的元组。工作器节点执行满足查询计划所需的关联代数。
-
工作器节点将元组流回
/sql
处理程序,在此处执行最终合并,最后将元组流回客户端。
SQL 客户端和数据库可视化工具
SQL 接口支持从 SQL 客户端和数据库可视化工具发送的查询。
本指南包含配置以下工具和客户端的文档
通用客户端
对于大多数基于 Java 的客户端,需要将以下 jar 放置在客户端类路径中
-
SolrJ 依赖项
.jar
s 位于$SOLR_TIP/server/solr-webapp/webapp/WEB-INF/lib/*
和$SOLR_TIP/server/lib/ext/*
中。在 Solr 发行版中,这些依赖项未与 Solr 的依赖项分开,因此您必须全部包含或手动选择所需的确切集合。请参阅 Maven 发行版,了解您版本所需的精确依赖项。 -
SolrJ
.jar
位于$SOLR_TIP/server/solr-webapp/webapp/WEB-INF/lib/solr-solrj-<version>.jar
如果您使用 Maven,则 org.apache.solr.solr-solrj
工件包含所需的 jar。
一旦 jar 在类路径中可用,Solr JDBC 驱动程序名称为 org.apache.solr.client.solrj.io.sql.DriverImpl
,并且可以使用以下连接字符串格式进行连接
jdbc:solr://SOLR_ZK_CONNECTION_STRING?collection=COLLECTION_NAME
还可以将其他参数(如 aggregationMode
和 numWorkers
)可选地添加到连接字符串中。