【问题标题】:Using Postgres's generate_series Function with ObjectionJS/KnexJS使用带有 Objection JS/Knex JS 的 Postgres generate_series 函数
【发布时间】:2019-06-20 22:24:51
【问题描述】:

我正在尝试执行高级分析查询来驱动 Web 应用程序。我正在使用带有TimescaleDB 的 Hapi、Objection、Knex 和 Postgres。对于典型的关系查询,这一切都运行良好。但是,我无法弄清楚如何执行此聚合查询,该查询涉及与从 Postgres 的generate_series 生成的匿名表连接。我不得不求助于编写原始 SQL,而不是 Objection/Knex 查询生成器。我正在使用一些 Postgres 的内置函数,以及来自 Timescale 的 time_buckettime_bucket 本质上是根据参数中指定的间隔创建数据汇总。查看此链接以了解有关我尝试做什么的更多信息Gap Filling

这是查询,它使用异议模型上的原始方法工作。我相信像这样进行字符串插值会导致潜在的 SQL 注入。但是,我希望将其转换为 Objection/Knex 使用的查询构建器方法,因此它更多的是 JavaScript,而不是 SQL,这将解决 SQL 注入问题。

let errorHistorgram = await Errors
    .raw(`SELECT period AS daily, coalesce(count,0) AS count
    FROM generate_series(date '${startTS}', date '${today}', interval '1d') AS period
      LEFT JOIN (
        SELECT time_bucket('1d',timestamp)::date AS date, count(timestamp)
        FROM my_error_table
        WHERE severity = 'HIGH'
          AND timestamp >= '${startTS}' AND timestamp < '${today}'
          AND device_id = ${deviceId}
        GROUP BY date
      ) t ON t.date = period;`)
      .debug();

我已经用 Objection/Knex 进行了几次尝试。这是我制作此查询的最成功尝试。但是,我认为 where 子句的位置不正确。

let errorHistorgram = await Errors
    .query()
    .select(raw(`time_bucket('1 day', timestamp) AS daily, count(timestamp)`))
    .where('device_id', deviceId)
    .andWhere('timestamp', '>', startTS)
    .andWhere('severity', 'HIGH')
    .leftJoin(`generate_series(date ${startTS}, date ${today}, interval 1d) AS series`, 'series.date', 'my_error_table.timestamp')
    .debug();

使用.debug(),我可以看到下面发布的查询输出。

select time_bucket('1 day', timestamp) AS daily, count(timestamp)
from my_error_table
left join "generate_series(date 2018-11-08T15:35:33"."050Z, date 2018-11-15T15:35:33"."133Z, interval 1d)" as "series"
  on "series"."date" = my_error_table."timestamp"
where "device_id" = ? and "timestamp" > ? and "severity" = ?'

感谢您提供任何帮助,因为我没有使用 Objection 来执行此操作,也找不到任何文档。

2018 年 11 月 15 日更新

我得到它来执行带有异议的查询。但是,我得到一个空数组作为结果。与我在上面制作的原始 SQL 查询不同(它确实给了我预期的结果),我只得到一个空数组作为查询生成器的输出。关于我做错了什么的任何想法。我试图将连接翻转为正确的连接,但没有运气。

 let errorHistorgram = await Errors
  .query()
  .select(raw(`time_bucket('1 day', timestamp) AS daily, count(timestamp)`))
  .where('device_id', deviceId)
  .andWhere('timestamp', '>', startTS)
  .andWhere('severity', 'HIGH')
  .groupBy('timestamp')
  .rightJoin(raw(`generate_series(date '${startTS}', date '${today}', interval '1d') AS series`), 'series.date', 'my_error_table.timestamp')
  .debug();

附件是 Debug 的 SQL 输出。

select time_bucket('1 day', timestamp) AS daily, count(timestamp) 
from my_errors_table
right join generate_series(date '2018-11-08', date '2018-11-15', interval '1d') AS series
on series = my_errors_table.timestamp
where device_id = ? and timestamp > ? and severity = ? 
group by timestamp

【问题讨论】:

  • 您的代码仍有几个地方可能存在 sql 注入漏洞。你能发现工作的原始查询和 knex 生成的查询不同的地方吗?这也会告诉你为什么查询没有返回预期的结果。
  • 就错位而言 ​​- 我认为我的主要问题是 SQL 查询使用左连接。我不能使用左连接,因为我的选择语句必须基于我的模型 (my_errors_table),而不是基于 generate_series 函数。使用 knex,我在告诉它在第一个查询而不是连接上应用 where 子句时遇到问题。似乎 where 查询正在应用于联接,而不是实际查询。这有意义吗?知道如何解决吗?
  • 我知道我有可能进行 SQL 注入,因为我使用的是 JS 的字符串插值,而不是 knex 的查询生成器。我希望通过我的 ORM 来解决这个问题,因此 SQL 注入目前对我来说不是问题。

标签: postgresql knex.js sql-function objection.js timescaledb


【解决方案1】:

Timescale 发布了一个名为Time Bucket Gapfill 的新功能。它使这变得更加容易,因为您不再需要使用 generate_series 进行左连接来填补空白。

我提供了一个示例,说明如何使用名为 Errors 的 ObjectionJS 模型来实现这一点。 time_bucket_gapfill 函数的输入是存储桶大小、时间戳列名称、startTS 和 endTS。桶大小变量应该是一个带有""(不是单引号)的字符串,对应于桶大小(例如:"10 seconds""30 minutes""1 hour""1 day")。 startTSstopTS 应该是 ISO 日期字符串。第二个select语句需要COALESCE,这样如果生成了一个bucket,而bucket中没有包含数据,它将输出0。分桶需要group by,才能根据您在select 语句中提供的聚合SQL 函数正确汇总数据。

import { raw } from 'objection';

const errors = await Errors
  .query()
  .select(
    raw("time_bucket_gapfill(?, timestamp, ?, ?) AS bucket", [bucketWidth, startTS, endTS]),
    raw('COALESCE(count(timestamp), 0) AS count'),
  ).where('device_id', deviceId)
  .andWhere('timestamp', '>=', startTS)
  .andWhere('timestamp', '<=', endTS)
  .groupBy('bucket')
  .orderBy('bucket');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    相关资源
    最近更新 更多