1

我的目标是在给定的时间范围内(fromDate < stats_date_id < toDate)对每个 DISTINCT provider_id 值计算 messages_sent 和 emails_sent 的总和,但不指定 provider_id。换句话说,我需要了解指定时间范围内的所有提供者,并将它们的messages_sent 和emails_sent 相加。

我有一个使用 express-cassandra 模式(在 Node.js 中)的 Cassandra 表,如下所示:

module.exports = {
  fields: {
    stats_provider_id: {
      type: 'uuid',
      default: {
        '$db_function': 'uuid()'
      }
    },
    stats_date_id: {
      type: 'timeuuid',
      default: {
        '$db_function': 'now()'
      }
    },
    provider_id: 'uuid',
    provider_name: 'text',
    messages_sent: 'int',
    emails_sent: 'int'
  },
  key: [
    [
      'stats_date_id'
    ],
    'created_at'
  ],
  table_name: 'stats_provider',
  options: {
    timestamps: {
      createdAt: 'created_at', // defaults to createdAt
      updatedAt: 'updated_at' // defaults to updatedAt
    }
  }
}

为了让它工作,我希望它就像执行以下操作一样简单:

let query = {
    stats_date_id: {
      '$gt': db.models.minTimeuuid(fromDate),
      '$lt': db.models.maxTimeuuid(toDate)
    }
  };
let selectQueries = [
    'provider_name',
    'provider_id',
    'count(direct_sent) as direct_sent',
    'count(messages_sent) as messages_sent',
    'count(emails_sent) as emails_sent',
  ];
  // Query stats_provider table
  let providerData = await db.models.instance.StatsProvider.findAsync(query, {select: selectQueries});

然而,这抱怨需要过滤结果: Error during find query on DB -> ResponseError: Cannot execute this query as it might involve data filtering and thus may have unpredictable performance.

我猜你不能有一个主键并对其进行日期范围搜索?如果是这样,这种查询的正确方法是什么?

4

1 回答 1

2

因此,虽然没有使用过 Express-Cassandra,但我可以告诉您,在分区键上运行范围查询是一个很难的“不”。原因是 Cassandra 无法确定该查询的单个节点,因此它必须轮询每个节点。由于这本质上是跨多个节点对表进行全面扫描,因此它会抛出该错误以防止您运行错误的查询。

但是,您可以对集群键运行范围查询,前提是您要过滤它之前的所有键。在你的情况下,如果我没看错,你的 PRIMARY KEY 看起来像:

PRIMARY KEY (stats_date_id, created_at)

主键定义会出现问题,原因有两个:

  1. stats_date_id是一个 TimeUUID。这非常适合数据分发。但它的查询灵活性很糟糕。事实上,您需要提供准确的 TimeUUID 值来返回特定分区的数据。由于 TimeUUID 具有毫秒精度,因此您需要知道查询的确切时间到毫秒。也许你有能力做到这一点,但通常这并不能成为一个好的分区键。

  2. 该分区 ( ) 下的任何行都created_at必须共享该确切时间,这通常会导致分区:集群键的基数比为 1:1。

我对解决此问题的建议是在基数级别稍低的日期列上进行分区。想想在某个时间范围内通常保存了多少提供者消息。还要选择不会将太多提供者消息存储在一起的东西,因为您不希望未绑定的分区增长(Cassandra 的硬限制是每个分区 20 亿个单元)。

也许是这样的:PRIMARY KEY (week,created_at)

因此,您的 CQL 查询可能类似于:

SELECT * FROM stats_provider
WHERE week='201909w1'
  AND created_at > '20190901'
  AND created_at < '20190905';

TL;博士;

  1. 时间桶上的分区不如精确到毫秒,但足够大以满足您通常的查询。
  2. 在分区内的第一个集群键上应用范围过滤器。
于 2019-09-19T18:26:21.377 回答