将Postgres的generate_series函数与ObjectionJS / KnexJS结合使用

时间:2018-11-15 15:51:53

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

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

这是查询,它使用对象模型上的raw方法工作。我相信像这样进行字符串内插会导致潜在的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" = ?'

我们会提供任何帮助,因为我没有使用异议来做,因此也找不到任何文档。

更新11/15/2018

我可以用Objection执行查询。但是,我得到一个空数组作为结果。与上面我编写的原始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

1 个答案:

答案 0 :(得分:0)

Timescale发布了一项名为Time Bucket Gapfill的新功能。这使此操作变得容易得多,因为您无需再与generate_series进行左连接来填补空白。

我提供了一个示例,该示例说明了如何使用名为Errors的ObjectionJS模型来实现此功能。 time_bucket_gapfill函数的输入是存储区大小,时间戳列名称,startTS和endTS。值区大小变量应为带有""(不是单引号)的字符串,该字符串与值区大小相对应(例如:"10 seconds""30 minutes""1 hour",{{1} }。 "1 day"startTS应该是ISO日期字符串。第二个select语句需要stopTS,以便在存储桶中不包含任何数据的情况下生成的存储桶将输出0。根据您在COALESCE语句中提供的汇总SQL函数,存储桶需要group by来正确汇总数据。

select