首页 / 资讯中心 / 文章详情

Apache Calcite Elasticsearch 适配器实战指南:模型配置、SQL 查询下推与 Scrolling 分页原理

Apache Calcite Elasticsearch 适配器实战指南:模型配置、SQL 查询下推与 Scrolling 分页原理 ★ FEATURED ARTICLE
后端数据库【免费下载链接】calciteApache Calcite项目地址https://gitcode.com/gh_mirrors/ca/calcite点击查看免费下载Apache Calcite 的 Elasticsearch 适配器位于elasticsearch/模块让你能够用标准 SQL 直接查询 Elasticsearch 索引并尽可能把过滤、排序、聚合等操作下推push-down为高效的 Elasticsearch SEARCH JSON由 ES 集群自身完成计算。本文以官方文档 elasticsearch_adapter.md 为主体结合elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/下的源码实现完整讲解模型文件的编写、sqlline连接、_MAP访问方式、查询下推机制、Scrolling API 分页以及版本支持范围帮助你从零上手并理解其底层工作原理。一、准备工作编译 Calcite 与模型文件的作用在开始查询 Elasticsearch 之前你需要先完成 Calcite 项目的下载与编译具体步骤见 教程。编译完成后接下来需要准备一份**模型定义model definition**文件可参考 model 文档。模型文件的作用是告诉 Calcite 如何创建 Elasticsearch 适配器的实例——包括连接哪些 ES 节点、是否需要认证、默认访问哪个索引等模型中的表名table name与 Elasticsearch 中的索引名index一一对应也就是说你在 ES 里建的每一个索引都会以同名的表出现在 Calcite 的逻辑模型中。如果需要在 Schema 中声明物化视图模型文件还支持materializations配置见 model.md#materialization每个物化定义由view、table、sql三个字段组成描述某个视图对应的物化表及其 SQL 定义。二、模型文件编写与参数详解官方文档给出的最简模型文件如下假设保存为model.json{ version: 1.0, defaultSchema: elasticsearch, schemas: [ { type: custom, name: elasticsearch, factory: org.apache.calcite.adapter.elasticsearch.ElasticsearchSchemaFactory, operand: { coordinates: {127.0.0.1: 9200} } } ] }其中关键点type为custom表示由factory指定的工厂类创建自定义 Schemafactory必须指向org.apache.calcite.adapter.elasticsearch.ElasticsearchSchemaFactory它实现了 Calcite 的SchemaFactory接口源码见 ElasticsearchSchemaFactory.java负责解析operand中的键值对并创建ElasticsearchSchemaoperand中的参数解析时使用了开启单引号支持的 JacksonObjectMapperALLOW_SINGLE_QUOTES因此文档示例中的{127.0.0.1: 9200}是合法的 JSON 字符串写法。2.1operand支持的全部参数结合工厂类源码ElasticsearchSchemaFactory.create()方法与官方文档operand支持的参数如下参数类型必填说明hostsJSON 字符串数组如[\127.0.0.1:9200\]二选一ES 集群的 HTTP 主机列表推荐使用每个条目通过HttpHost.create解析可含 scheme、端口coordinatesJSON 字符串对象如{127.0.0.1: 9200}二选一主机与端口的键值对已废弃源码中会打印Prefer using hosts, coordinates is deprecated.警告index字符串否指定只暴露某一个索引作为表不指定时通过GET /_alias自动发现集群中所有索引及别名见 ElasticsearchSchema.javapathPrefix字符串否请求路径前缀用于 ES 前面挂载了反向代理/网关的场景通过RestClientBuilder.setPathPrefix生效username/password字符串否ES 集群认证凭据两者同时提供时通过BasicCredentialsProvider注入到 REST 客户端disableSSLVerification布尔字符串否是否禁用 SSL 证书校验默认false生产环境必须保持false。为true时会使用不校验的 TrustManager 并忽略主机名校验源码见 ElasticsearchSchemaFactory.java#L242-L250官方文档补充的index与pathPrefix示例operand: { coordinates: {127.0.0.1: 9200}, index: usa, pathPrefix: path }在ElasticsearchSchema构造时若指定了index则只创建一个表若未指定则调用indicesFromElastic()请求/_alias端点把返回的索引名和别名都注册为表这也是测试中zips_alias别名可以像表一样被查询的原因见 ElasticSearchAdapterTest.java#L377-L381。2.2 REST 客户端缓存机制一个值得注意的底层细节ElasticsearchSchemaFactory使用 Guava Cache 对RestClient做了最多 100 个实例的缓存REST_CLIENT_CACHE_SIZE 100缓存键由 hosts、pathPrefix、username、password 组合而成见 ElasticsearchSchemaFactory.java#L73-L101。原因在于 Calcite 当前无法主动关闭它创建的客户端而每个RestClient会占用文件描述符等系统资源缓存可以在客户端数量超过上限时自动关闭最旧的实例。因此在同一进程内应避免创建过多不同的 ES REST 连接配置。三、通过 sqlline 连接并执行查询把上述模型文件保存为model.json后即可在 Calcite 项目根目录下启动sqlline连接$ ./sqlline sqlline !connect jdbc:calcite:modelmodel.json admin admin连接成功之后sqlline就能接受访问 Elasticsearch 的 SQL 查询了。下面这条查询取出索引usa中所有州名sqlline SELECT * from usa;返回结果形如每条记录以_MAP键暴露整个原始文档_MAP{pop13367, loc[-72.505565, 42.067203], cityEAST LONGMEADOW, id01028, stateMA} _MAP{pop1652, loc[-72.908793, 42.070234], cityTOLLAND, id01034, stateMA} _MAP{pop3184, loc[-72.616735, 42.38439], cityHATFIELD, id01038, stateMA} _MAP{pop43704, loc[-72.626193, 42.202007], cityHOLYOKE, id01040, stateMA} _MAP{pop2084, loc[-72.873341, 42.265301], cityHUNTINGTON, id01050, stateMA} _MAP{pop1350, loc[-72.703403, 42.354292], cityLEEDS, id01053, stateMA} _MAP{pop8194, loc[-72.319634, 42.101017], cityMONSON, id01057, stateMA} _MAP{pop1732, loc[-72.204592, 42.062734], cityWALES, id01081, stateMA} _MAP{pop9808, loc[-72.258285, 42.261831], cityWARE, id01082, stateMA} _MAP{pop4441, loc[-72.203639, 42.20734], cityWEST WARREN, id01092, stateMA}在测试模块中这组数据的对应场景由 ElasticSearchAdapterTest.java 使用嵌入式 ES 实例EmbeddedElasticsearchPolicy见 EmbeddedElasticsearchPolicy.java加载zips-mini.json数据源来验证其zips索引共 149 条记录。四、无需视图定义直接访问_MAP字段适配器支持在没有预先定义视图的情况下直接通过_MAP[字段名]的方式访问文档中的任意字段sqlline SELECT _MAP[city], _MAP[state] from elasticsearch.usa order by _MAP[state];这里的_MAP是 ES 文档在 Calcite 侧的原始映射列_MAP[city]这种写法在 Calcite 内部对应ITEM运算符RexCall项目源码中的ElasticsearchRules.isItem专门识别此类表达式见 ElasticsearchRules.java。视图view本质上就是把一组CAST(_MAP[x] AS type)表达式封装成带类型的列测试代码中的视图定义见 ElasticSearchAdapterTest.java#L124-L135展示了这种典型用法select cast(_MAP[city] AS varchar(20)) AS city, cast(_MAP[loc][0] AS float) AS longitude, cast(_MAP[pop] AS integer) AS pop, cast(_MAP[state] AS varchar(2)) AS state from elastic.zips五、查询下推把 SQL 编译成最精简的 ES SEARCH JSON适配器的核心设计目标是把 SQL 查询编译成尽可能高效的 Elasticsearch SEARCH JSON凡是能在 ES 侧完成的过滤filtering与排序sorting一律下推给 Elasticsearch 执行减少数据回传与内存计算。以select city, state from usa where city springfield order by state这类查询为例最终提交给 ES 的搜索 JSON 如下{ query: { constant_score: { filter: { term: { city: springfield } } } }, _source: [ city, state ], script_fields: {}, sort: [ { state: { missing: _last, order: asc } } ] }可以看到SQL 中的WHERE city ...被翻译成了term过滤、ORDER BY state被翻译成了sort数组升序、缺失值排最后missing: _lastSELECT中的列被写进了_source裁剪字段。测试中testFilterSort等用例通过ElasticsearchChecker.elasticsearchChecker(...)精确断言了生成的 ES JSON 结构见 ElasticSearchAdapterTest.java#L588-L620。5.1 谓词下推PredicateAnalyzer 与 QueryBuilderWHERE条件到 ES 查询的转换由 PredicateAnalyzer.java 完成。它基于 Calcite 的RexVisitorImpl遍历RexNode表达式树把每个可支持的节点翻译成对应的 ESQueryBuilderAND/OR→bool查询的must/should子句→term查询→mustNot(term)并配合exists保证 NULL 行为正确、、、→range查询日期/时间字面量会自动附加format: date_timeIN/NOT IN→terms查询 /mustNot(terms)LIKE→regexp查询支持 ESCAPE 转义见测试 ElasticSearchAdapterTest.java#L240-L316CONTAINS→match查询IS NULL/IS NOT NULL→mustNot(exists)/exists查询NOT→bool的mustNotBETWEEN等经Sarg归一化后的SEARCH调用 →terms/range查询Sarg为点集合时翻译为terms为区间时翻译为range。需要说明的边界_id、_index、_uid这些元数据列只支持等于/不等于比较见 PredicateAnalyzer.java#L366-L378其它操作符会抛出异常并回退为不下推。若某个表达式无法被分析器处理会抛出ExpressionNotAnalyzableException调用方ElasticsearchFilter随后回退到 Calcite 内存计算。5.2 查询外层包装constant_score 与 dis_maxElasticsearchFilter.java 中PredicateAnalyzerTranslator.translateMatch的包装逻辑是若条件不含OR则用constant_score包裹常量评分过滤效率最高测试断言可见constant_score:{filter:{...}}若条件含OR则改用dis_max包裹保证布尔逻辑正确测试testDismaxQuery断言了dis_max:{queries:[...]}结构见 ElasticSearchAdapterTest.java#L622-L649。5.3 排序、分页与列裁剪下推排序ORDER BY由 ElasticsearchSort.java 下推为sort数组并依据 SQL 语义自动设置missing: _last升序或missing: _first降序OFFSET/FETCH分别下推为from/size参数测试断言见 ElasticSearchAdapterTest.java#L396-L410。列裁剪SELECT出的列写入_source遇到CAST(_MAP[loc][0] AS float)这类无法直接映射为_source字段的表达式时适配器会改用script_fields脚本字段通过params._sourceES 5 使用 Painless或_sourceES 2 使用 Groovy读取原始文档见 ElasticsearchProject.java 与 ElasticsearchConstants.java 中的SOURCE_PAINLESS/SOURCE_GROOVY常量。聚合COUNT、MAX、MIN、AVG、SUM、ANY_VALUE会下推为 ES 指标聚合value_count、max、min、avg、sum、termsCOUNT(DISTINCT ...)的近似版本下推为cardinality聚合GROUP BY下推为 ES 桶聚合。完整支持矩阵见 ElasticsearchAggregate.java#L55-L57 与toElasticAggregate映射ElasticsearchAggregate.java#L168-L187。非精确的COUNT(DISTINCT)与 grouping sets 不支持会在规划阶段抛出InvalidRelException。六、Scrolling API无聚合查询的懒加载分批返回对于不含聚合函数如COUNT、MAX等的查询适配器默认使用 Elasticsearch 的 Scroll API官方文档说明见site/_docs/elasticsearch_adapter.md的 Use of Scrolling API 一节。这样做的目的是保证返回给最终用户的是一致、完整的数据集——结果按批次batch懒加载地返回而不是一次性全部拉到内存。Scrolling 的实现位于 Scrolling.java每次搜索请求通过POST /{index}/_search发起并携带scroll1m参数与sizefetchSize默认批次大小DEFAULT_FETCH_SIZE 5196定义于 ElasticsearchTransport.java#L68后续批次通过POST /_search/scroll携带scroll_id继续拉取见 ElasticsearchTransport.java#L149-L167结果通过AutoClosingIterator封装当所有查询结果都被消费完毕后scroll 会被自动清理closed释放 ES 服务端游标资源调用的是DELETE /_search/scroll见 ElasticsearchTransport.java#L169-L198如果查询本身带size/LIMIT且小于批次大小则跳过滚动直接单次搜索返回。需要注意Scrolling类在注释中明确标注非线程安全单条查询的迭代过程应在同一线程内串行消费。对应测试见 ScrollingTest.java。七、支持的 Elasticsearch 版本官方文档明确说明当前适配器支持 Elasticsearch 6.x 及更新版本最高至 7.15.2。一般而言项目会尽量跟随 Elastic 官方的支持计划EOL schedule来维护兼容性。另外重要限制是不支持 types映射类型只支持索引indices。从源码看版本适配是运行期动态完成的ElasticsearchTransport 在创建传输层时向GET /发起请求解析响应中的version.number由 ElasticsearchVersion.java 识别 2、5、6、7 等主版本其余返回UNKNOWN。版本差异主要影响两类行为script_fields 语法ES 2 使用_sourceGroovyES 5 使用params._sourcePainlessscroll 清理请求体ES 2 要求DELETE /_search/scroll携带 JSON 数组形式的scroll_id见 ElasticsearchTransport.java#L182-L197。版本解析测试见 ElasticsearchVersionTest.java可帮助理解格式校验规则。八、快速验证与继续深入如果你想在本地快速验证适配器行为elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/目录下的测试是最佳参考ElasticSearchAdapterTest.java覆盖视图查询、_MAP直接访问、过滤、排序、LIMIT/OFFSET、聚合、LIKE、IN、ORdis_max、SSL 与认证配置等端到端场景ScrollingTest.java验证滚动分批返回与自动清理EmbeddedElasticsearchPolicy.java测试使用的嵌入式 ES 单节点策略展示了建索引、建别名、bulk 写入的完整流程。核心实现源码则集中在elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/从ElasticsearchSchemaFactory配置解析→ElasticsearchSchema索引发现→ElasticsearchTableScan/ElasticsearchFilter/ElasticsearchProject/ElasticsearchSort/ElasticsearchAggregate各类关系算子的下推实现→ElasticsearchTransportHTTP 传输与滚动→ElasticsearchJson响应解析形成了一条完整的数据通路。把官方文档的配置示例与这些源码、测试对照阅读即可全面掌握该适配器的使用边界与内部原理。赞分享后端数据库【免费下载链接】calciteApache Calcite项目地址https://gitcode.com/gh_mirrors/ca/calcite点击查看免费下载相关推荐CodeQL Actions 0.4.13 变更解析Artifact Poisoning 查询新增 runner.temp 误报排除CodeQL Actions 0.4.13 变更解析Artifact Poisoning 查询新增 runner.temp 误报排除 导读 本文围绕 Code后端数据库OneUptime SQL 查询监控器SQL Query Monitor实战指南只读数据库查询、安全模型与告警配置OneUptime SQL 查询监控器SQL Query Monitor实战指南只读数据库查询、安全模型与告警配置 OneUptime 的 SQL 查询监可观测性后端运维前端云原生微服务AI AgentApache Zeppelin Elasticsearch 解释器实战指南配置、查询命令与结果可视化Apache Zeppelin Elasticsearch 解释器实战指南配置、查询命令与结果可视化 Elasticsearch 是一个高可扩展的开源全文搜索后端前端大数据数据分析上一篇Advanced Binary Deobfuscation环境搭建指南Ubuntu 18.04下的快速配置教程下一篇5大核心功能解析为什么Frappe是企业级Web应用开发的终极解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站