Repository navigation
ZH Queries
当前版本为 2.1.2;本地及 GitHub CI 完整验证通过,仅通过 GitHub 分发;Maven Central 仍为 2.1.0。 使用这些修改须先从源码安装 2.1.2,见 2.1.2 发布说明。
结构化聚合的输出名在按所选后端规则规范化后必须唯一。检查集合包含分组 tag、每个聚合 alias,以及仅在 groupByTime 生成窗口列时加入的 window_start。重复 alias、重复分组 tag、tag 与 alias 冲突、生成窗口列冲突,均在 HTTP 请求或借用 session 前返回 ARGUMENT_ERROR。直接 TSDBAdapter.query/count、链式 list 和 offset 分页总数使用相同规则。
InfluxDB 1.x、InfluxDB 3 与 OpenGemini 保留带引号标识符的大小写,total 与 TOTAL 可以不同;IoTDB 使用小写物理列身份,因此二者冲突。没有生成窗口时,window_start 可以作为普通聚合 alias。已有不支持的游标、排序及时间区操作保留错误分类与优先级;本校验不增加这些能力,也不改写原生 SQL。对同一字段多次聚合时,应分别使用 temperature_avg、temperature_max 等唯一 alias。
本页涉及的输入校验、配置快照和初始化失败策略修复纳入 2.1.0;详见 2.1.0 变更记录。
InfluxDB 1.x 与 OpenGemini 的聚合响应还包含隐式物理 time 列,因此精确命名为 time 的聚合 alias 或分组 tag 在 I/O 前被拒绝;大小写不同的 Time 仍可使用。此协议专属保留名不扩大到 IoTDB 或 InfluxDB 3。
TGTemplate.query(Class<T>) 返回 com.alandevise.tsgate.core.TGQueryBuilder<T>。示例使用已注入的 TGTemplate 变量 tgTemplate;每次查询创建新的 builder,该对象可变,不应跨线程共享。
import com.alandevise.tsgate.core.TGQueryBuilder;
TGQueryBuilder<AccrueRecord> query = tgTemplate.query(AccrueRecord.class);List<AccrueRecord> rows = tgTemplate.query(AccrueRecord.class)
.database("tsdb")
.whereTag("device_code", "device_001")
.timeRange(startTime, endTime)
.orderByTimeDesc()
.limit(20)
.list();四个后端的 TSDBAdapter.query(database, query) 都执行相同的基础校验,包括调用方自行构造的 TSDBQuery。query 为 null、显式 limit <= 0、offset < 0 或 startTime > endTime 时,在数据库 I/O 前返回 ARGUMENT_ERROR,不会静默丢弃非法分页参数。limit 为 null 表示不在 SQL 中分页,后端结果限额仍然生效;offset 为零、起止时间相等均合法。原生 executeQuery(sql) 保留独立的 SQL 与结果限额契约。
count() 仍先忽略分页和游标设置再计数,但拒绝逆序时间范围。公共 SPI 默认 count(null) 返回 ARGUMENT_ERROR,包括使用该默认实现的第三方 adapter,不再产生未经统一分类的空指针错误。链式页大小、单条分页探测额度及后端专有的不支持操作语义保持原有契约。
OpenGemini 适配仅将服务端精确的查询错误 measurement not found 在明确指定 measurement 的结构化统一 query(...) 中转为空结果,在 count(...) 中转为零;database、retention policy、权限及其他查询错误保留。原生 executeQuery(...) 的单条语句可能涉及多个 measurement 或子查询,因此保留查询错误;直接调用借用的兼容 org.influxdb.InfluxDB 时,也保留服务端错误 DTO。写入确认后,新 measurement 或 tag series 可能暂未对查询可见;业务在读侧轮询时应设置明确截止时间,并匹配具体期望数据点。每次 adapter 查询仍只发送一次请求。详见 OpenGemini 接入指南。
public class ValueStatusResult {
private Double value;
private Long status;
}
List<ValueStatusResult> rows = tgTemplate.query(AccrueRecord.class)
.database("tsdb")
.select("value", "status")
.whereTag("source", "annotation-pojo")
.timeRange(startTime, endTime)
.orderByTimeAsc()
.limit(100)
.list(ValueStatusResult.class);以下 FIELD 排序功能适用于 IoTDB 和 InfluxDB 3;InfluxDB 1.x 只支持按时间排序。FIELD 可以作为第一排序列,也可以追加在时间列之后组成多字段排序。排序声明按调用顺序生成 SQL ORDER BY
;后一个排序列只在前面的排序列值相同时参与比较。
List<AccrueRecord> rows = tgTemplate.query(AccrueRecord.class)
.database("tsdb")
.timeRange(startTime, endTime)
.orderByTimeAsc()
.thenByFieldDesc("value")
.thenByFieldAsc("status")
.limit(100)
.list();上述查询会生成 ORDER BY time ASC, value DESC, status ASC。如果 FIELD 需要作为第一排序列,使用
orderByFieldAsc(field) 或 orderByFieldDesc(field);orderBy... 会替换已有排序,thenByField...
会追加或更新次级排序。
FIELD 排序支持普通 list()、page(pageNum, pageSize) 的 limit/offset 分页和 strictCursorPage(); 单字段时间游标
page() 以及聚合查询不支持 FIELD 排序,并会明确抛出不支持操作异常。 严格游标会按照每个 SortSpec 的方向生成对应的字典序条件,因此支持诸如
ORDER BY time DESC, device_code ASC 的混合排序。严格分页会自动在自定义排序末尾补齐缺失的 time + 全部 @TGTag 列,
补充列沿用第一项排序的方向;已经指定的列保留其方向。POJO 必须声明该表完整的 tag 键,排序与游标列当前仍要求非空。
public class AccrueAggregateResult {
private Long windowStart;
private String deviceCode;
private Double avgValue;
private Double maxValue;
private Long sampleCount;
}
List<AccrueAggregateResult> rows = tgTemplate.query(AccrueRecord.class)
.database("tsdb")
.whereTagIn("device_code", List.of("device_001", "device_002"))
.where("value", OperatorEnum.GE, 0)
.timeRange(startTime, endTime)
.groupByTime("5m")
.groupByTag("device_code")
.aggregate("value", AggregationFunctionEnum.AVG, "avg_value")
.aggregate("value", AggregationFunctionEnum.MAX, "max_value")
.aggregate("value", AggregationFunctionEnum.COUNT, "sample_count")
.timeZone("+08:00")
.orderByTimeAsc()
.limit(100)
.list(AccrueAggregateResult.class);groupByTime 只接受正整数及 ms/s/m/h/d 单位(例如 5m),纯数字按毫秒解释;小数、负数、零、复合表达式和超出 long 毫秒范围的窗口会在生成 SQL 前拒绝。
链式窗口聚合返回的 window_start 统一为毫秒时间戳,可使用 Long、Instant 或 Date 承接。
InfluxDB 返回不带时区后缀的窗口时间时,适配器按 UTC 解析,不受应用 JVM 默认时区影响。
IoTDB / InfluxDB 3 窗口时区遵循以下规则(InfluxDB 1.x 的限制见后端能力表):
- 默认 UTC。
UTC、+08:00等固定偏移使用固定时长窗口,可不设置时间范围。 -
America/New_York、Asia/Shanghai等地区时区需要显式且有界的timeRange(start, end)。 - 地区时区的
1d按查询日期的本地午夜切分;跨夏令时的一天可以是 23 或 25 小时。Nd从本地 1970-01-01 日期按 N 天对齐,边界使用各自日期的真实偏移。 - 为兼容两种后端,适配层按日历计算边界并生成有界 CASE 分组;不修改借出的 IoTDB Session 时区,也不依赖某个新版数据库特有函数。最多生成 10000 个窗口,超出时报
ARGUMENT_ERROR。 - 地区时区的
ms/s/m/h窗口使用查询开始时的偏移;若时间范围跨越偏移变化则明确报UNSUPPORTED_OPERATION。需要这类跨越查询时,可使用 UTC / 固定偏移,或改用自然日窗口。 - 查询范围仍按
start <= time <= end过滤,窗口边界采用左闭右开。若不希望包含下一日午夜,应将结束值设为下一日午夜前 1 毫秒。
例如 .timeRange(start, end).groupByTime("1d").timeZone("America/New_York") 在 2026-03-08 和 2026-11-01 分别使用 23 小时和 25 小时自然日,不再套用 1970 年的固定偏移。
- 链式方法主要是收集查询条件,整体没有严格顺序限制;
list()、page()、page(pageNum, pageSize)会真正执行查询,应放在最后调用。 -
database、select、timeRange、limit、orderByTimeAsc/Desc等配置类方法后调用会覆盖前调用。 -
where、whereTag、whereTagIn、groupByTag、aggregate等条件类方法会累加;当前普通条件之间是AND关系。 - 时间游标分页使用
page(),页大小来自limit();传统 offset 分页使用page(pageNum, pageSize),不建议再混用limit()、offset()。 - 每次查询建议重新调用
tgTemplate.query(...)创建 builder,不跨线程或跨请求复用。
省略聚合别名时,使用字段名、下划线和经 Locale.ROOT 转小写的函数名,例如 MIN(value) 默认别名始终为 value_min,不随 JVM 默认语言环境变化。业务显式指定的别名保持不变。
← 写入、模型与错误处理 · 分页与原生查询 →
TsGate 2.1.0 包含独立的 OpenGemini adapter 与 starter。配置、默认引擎边界及准确版本测试见 OpenGemini 接入指南,发行说明见 2.1.0 发布说明。
TsGate · Wiki home · 文档首页 · Apache-2.0 · NOTICE
Compatibility claims apply only to documented capabilities and verified versions. 兼容性承诺仅适用于已列明的能力和已验证的版本。