集計ダッシュボードレポートデータを時間間隔ごとに集計

最近、Discourse ダッシュボードレポートと類似のデータを返すが、期間別に集計可能なデータエクスプローラークエリを作成しました。例えば、指定された開始日と終了日の間に作成されたトピックの数を示すが、集計を日次ではなく週次で行うといったものです。

クエリのパラメータは以下のルールで設定されます:

クエリパラメータ:query_interval(Postgres のインターバル、例:‘1 day’、‘7 days’、‘1 week’、‘1 month’)、start_date(‘yyyy-mm-dd’)、end_date(‘yyyy-mm-dd’)、category_ids(カンマ区切りのカテゴリ ID リスト、デフォルトは -1)、include_subcategories(ブーリアン、デフォルトは true)。指定された開始日と終了日の間に作成された投稿の数を返します。結果はクエリインターバルでグループ化されます。category_ids リストに -1 が含まれている場合、すべてのカテゴリの結果が返されます。

期間ごとの初回応答までの平均時間

--[params]
-- string :query_interval = 7 days
-- date :start_date
-- date :end_date
-- int_list :category_ids = -1
-- boolean :include_subcategories = true

WITH query_periods AS (
  SELECT generate_series(:start_date, :end_date, :query_interval::interval)::date as period_start
),
subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (:category_ids)
),
sub_subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (SELECT id FROM subcategory_ids)
),

topics_and_replies AS (
    SELECT
    t.created_at AS topic_created_at,
    p.topic_id AS reply_topic_id,
    p.created_at AS reply_created_at,
    period_start
    FROM topics t
    JOIN query_periods
    ON t.created_at::date >= period_start AND t.created_at::date < period_start + interval :query_interval
    JOIN posts p
    ON p.topic_id = t.id
    WHERE t.posts_count > 1
    AND t.archetype = 'regular'
    AND t.deleted_at IS NULL
    AND CASE
        WHEN -1 IN (:category_ids)
            THEN true
        WHEN :include_subcategories = false
            THEN t.category_id IN (:category_ids)
        ELSE t.category_id IN (:category_ids) OR t.category_id IN (SELECT id FROM subcategory_ids) OR t.category_id IN (SELECT id FROM sub_subcategory_ids)
    END
    AND p.post_number > 1
    AND p.post_type = 1
    AND p.deleted_at IS NULL
)

SELECT period_start, ROUND(AVG(reply_time_hours)::numeric, 2) AS response_time_hours FROM(
    SELECT
    qp.period_start,
    EXTRACT(EPOCH FROM MIN(reply_created_at) - topic_created_at):: float / 3600 AS reply_time_hours
    FROM query_periods qp
    JOIN topics_and_replies tar
    ON tar.period_start = qp.period_start
    GROUP BY reply_topic_id, topic_created_at, qp.period_start
) replies_for_period
GROUP BY period_start
ORDER BY period_start

期間ごとの解決済みトピックの総数

--[params]
-- string :query_interval = 7 days
-- date :start_date
-- date :end_date
-- int_list :category_ids = -1
-- boolean :include_subcategories = true

WITH query_periods AS (
  SELECT generate_series(:start_date, :end_date, :query_interval::interval)::date as period_start
),
subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (:category_ids)
),
sub_subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (SELECT id FROM subcategory_ids)
)

SELECT
period_start,
COUNT(1) AS solved_count
FROM user_actions ua
JOIN query_periods
ON ua.created_at::date >= period_start AND ua.created_at::date < period_start + interval :query_interval
JOIN topics t
ON t.id = ua.target_topic_id
JOIN posts p 
ON p.id = ua.target_post_id
WHERE ua.action_type = 15
AND t.deleted_at IS NULL
AND p.deleted_at IS NULL
AND CASE
    WHEN -1 IN (:category_ids)
        THEN true
    WHEN :include_subcategories = false
        THEN t.category_id IN (:category_ids)
    ELSE t.category_id IN (:category_ids) OR t.category_id IN (SELECT id FROM subcategory_ids) OR t.category_id IN (SELECT id FROM sub_subcategory_ids)
END
GROUP BY period_start
ORDER BY period_start

期間ごとのトピック数

--[params]
-- string :query_interval = 7 days
-- date :start_date
-- date :end_date
-- int_list :category_ids = -1
-- boolean :include_subcategories = true

WITH query_periods AS (
  SELECT generate_series(:start_date, :end_date, :query_interval::interval)::date as period_start
),
subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (:category_ids)
),
sub_subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (SELECT id FROM subcategory_ids)
)

SELECT qp.period_start,
COUNT(t.id)
FROM query_periods qp
JOIN topics t
ON t.created_at::date >= qp.period_start AND t.created_at::date < qp.period_start + interval :query_interval
WHERE t.deleted_at IS NULL
AND t.archetype = 'regular'
AND CASE
    WHEN -1 IN (:category_ids)
        THEN true
    WHEN :include_subcategories = false
        THEN t.category_id IN (:category_ids)
    ELSE t.category_id IN (:category_ids) OR t.category_id IN (SELECT id FROM subcategory_ids) OR t.category_id IN (SELECT id FROM sub_subcategory_ids)
END
GROUP BY qp.period_start
ORDER BY qp.period_start

期間ごとの投稿数

--[params]
-- string :query_interval = 7 days
-- date :start_date
-- date :end_date
-- int_list :category_ids = -1
-- boolean :include_subcategories = true

WITH query_periods AS (
  SELECT generate_series(:start_date, :end_date, :query_interval::interval)::date as period_start
),
subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (:category_ids)
),
sub_subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (SELECT id FROM subcategory_ids)
)

SELECT
period_start,
COUNT(p.id)
FROM query_periods qp
JOIN posts p
ON p.created_at::date >= qp.period_start AND p.created_at::date < qp.period_start + interval :query_interval
JOIN topics t
ON t.id = p.topic_id
WHERE t.archetype = 'regular'
AND t.deleted_at IS NULL
AND CASE
    WHEN -1 IN (:category_ids)
        THEN true
    WHEN :include_subcategories = false
        THEN t.category_id IN (:category_ids)
    ELSE t.category_id IN (:category_ids) OR t.category_id IN (SELECT id FROM subcategory_ids) OR t.category_id IN (SELECT id FROM sub_subcategory_ids)
END
AND p.deleted_at IS NULL
AND p.post_type = 1
GROUP BY period_start
ORDER BY period_start
「いいね!」 7

@simon さん、ありがとうございます!とても素晴らしいですね。

当初、インターバルを選択する際に start_dateend_date パラメータが依然として必須であること、およびその逆の状況に混乱していました。しかし、指定された日付範囲内で X インターバルごとの結果を返す仕組みであることがわかりました。これは、1 年間の月次変化や同様のシナリオを素早く確認する際に非常に便利です。

カテゴリとサブカテゴリの包含機能も素晴らしいです。コミュニティの異なる部分のアクティビティを追跡しているため、カテゴリ全体とそのサブカテゴリのパフォーマンスを素早く確認できるのは非常に役立ちます。

これらのクエリを、サブカテゴリの結果をカンマ区切りのリストとして表示するように単純に修正する方法はありますか?

例えば、カテゴリ 1(10 投稿)、2(20 投稿)、3(30 投稿)でインターバル中に作成された投稿などです。

クエリに 1,2,3category_ids を追加すると、合計(60 投稿)が返されます。10,20,30 を返す方法があれば嬉しいです。そうすれば、カテゴリ間の並列比較が可能になります。

「いいね!」 2

それは可能かもしれません。より簡単なアプローチは、クエリを修正してカテゴリごとに1行を返すようにすることです。これは、最後の GROUP BY 句にカテゴリ ID を含めることで実現できます。私が投稿したすべての例で試したわけではありませんが、以下は「期間ごとの投稿数」のクエリをそのように修正したものです:

--[params]
-- string :query_interval = 7 days
-- date :start_date
-- date :end_date
-- int_list :category_ids = -1
-- boolean :include_subcategories = true

WITH query_periods AS (
  SELECT generate_series(:start_date, :end_date, :query_interval::interval)::date as period_start
),
subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (:category_ids)
),
sub_subcategory_ids AS (
    SELECT id FROM categories
    WHERE parent_category_id IN (SELECT id FROM subcategory_ids)
)

SELECT
period_start,
t.category_id,
COUNT(p.id)
FROM query_periods qp
JOIN posts p
ON p.created_at::date >= qp.period_start AND p.created_at::date < qp.period_start + interval :query_interval
JOIN topics t
ON t.id = p.topic_id
WHERE t.archetype = 'regular'
AND t.deleted_at IS NULL
AND CASE
    WHEN -1 IN (:category_ids)
        THEN true
    WHEN :include_subcategories = false
        THEN t.category_id IN (:category_ids)
    ELSE t.category_id IN (:category_ids) OR t.category_id IN (SELECT id FROM subcategory_ids) OR t.category_id IN (SELECT id FROM sub_subcategory_ids)
END
AND p.deleted_at IS NULL
AND p.post_type = 1
GROUP BY period_start, t.category_id
ORDER BY period_start

以下は、私の開発環境のサイトでの結果の例です:

「いいね!」 3

素晴らしい、改めてありがとうございます!これで私の必要としていることが実現できると思います :slight_smile:

「いいね!」 2

これは素晴らしいです、@simon、ありがとうございます。
単純な質問かもしれませんが、以下のことは可能でしょうか。

  1. カスタムレポートをダッシュボード、レポートセクションに含めることは可能ですか?また、その方法は?
  2. DataExplorerで実行したクエリの結果に基づいて、例えば管理者にメッセージを送信するなど、何らかのアクションをトリガーすることは可能ですか?

ありがとうございます。

「いいね!」 1