Discourse API を使用して Data Explorer クエリを実行する

<div data-theme-toc="true"> </div>

> :bookmark: このガイドでは、Discourse API を使用して、[Data Explorer](https://meta.discourse.org/t/32566?silent=true) プラグインでクエリを作成、実行、管理する方法について説明します。
>
> :person_raising_hand: 必要なユーザーレベル: 管理者

Discourse ユーザーインターフェースから実行できるほぼすべての操作は、Discourse API を使用してトリガーすることもできます。

このドキュメントでは、特に [Data Explorer](https://meta.discourse.org/t/32566?silent=true) プラグインと連携して API を利用するための包括的な概要を提供します。

アクションに適した API リクエストを見つける方法の一般的な概要については、https://meta.discourse.org/t/how-to-reverse-engineer-the-discourse-api/20576 を参照してください。

## [Data Explorer](https://meta.discourse.org/t/32566?silent=true) クエリの実行

API を介して [Data Explorer](https://meta.discourse.org/t/32566?silent=true) クエリを実行するには、`/admin/plugins/explorer/queries/<query-id>/run` に対して `POST` リクエストを送信します。クエリ ID は、Discourse サイトからアクセスし、アドレスバーの `id` パラメータを確認することで見つけることができます。

以下は、ID が `20` のクエリの例で、指定された日付の閲覧数に基づいてトピックを返します。

``` sql
--[params]
-- date :viewed_at

SELECT
topic_id,
COUNT(1) AS views_for_date
FROM topic_views
WHERE viewed_at = :viewed_at
GROUP BY topic_id
ORDER BY views_for_date DESC

このクエリは、ターミナルから次のように実行できます。

curl -X POST "https://your-site-url/admin/plugins/explorer/queries/20/run" \
-H "Content-Type: multipart/form-data;" \
-H "Api-Key: <api-key>" \
-H "Api-Username: system" \
-F 'params={"viewed_at":"2019-06-10"}'

<api-key><your-site-url> を、ご自身の API キーとドメインに置き換える必要があることに注意してください。

大規模データセットの処理

Data Explorer プラグインは、デフォルトで結果を 1000 行に制限します。より大きなデータセットをページングするには、以下のクエリ例を使用できます。

--[params]
-- integer :limit = 100
-- integer :page = 0
SELECT * 
FROM generate_series(1, 10000)
OFFSET :page * :limit 
LIMIT :limit

結果をページごとに取得するには、リクエスト内の page パラメータをインクリメントします。

curl -X POST "https://your-site-url/admin/plugins/explorer/queries/27/run" \
-H "Content-Type: multipart/form-data;" \
-H "Api-Key: <api-key>" \
-H "Api-Username: system" \
-F 'params={"page":"0"}'

result_count がゼロになったら停止します。

大規模データセットの処理に関する追加情報については、Result Limits and Exporting Queries を参照してください。

結果からの relations データの削除

Data Explorer クエリがユーザーインターフェース経由で実行されると、結果に relations オブジェクトが追加されます。このデータは UI 結果でユーザーを描画するために使用されますが、API 経由でクエリを実行する場合には不要である可能性が高いです。

結果からそのデータを削除するには、リクエストに download=true パラメータを追加します。

curl -X POST "https://your-site-url/admin/plugins/explorer/queries/27/run" \
-H "Content-Type: multipart/form-data;" \
-H "Api-Key: <api-key>" \
-H "Api-Username: system" \
-F 'params={"page":"0"}' \
-F "download=true"

API 認証

リクエスト用の API キーを生成する詳細については、こちらを参照してください: Create and configure an API key

API キーを Data Explorer クエリの実行にのみ使用する場合、「Scope」ドロップダウンメニューから「Granular」を選択し、「run queries」スコープを選択できます。

FAQ

レポートのリストとその ID 番号を取得できる API エンドポイントはありますか?ドロップダウンを作成したいのです。

はい、認証された GET リクエストを /admin/plugins/explorer/queries.json に対して行うと、サイト上のすべてのクエリのリストを取得できます。

API 経由でクエリを作成することは可能ですか?

はい。その方法に関するドキュメントは、Create a Data Explorer query using the API にあります。

POST リクエストでパラメータを送信することは可能ですか?

はい、例に示されているように、-F オプションを使用して SQL パラメータを含めることができます。

クエリの CSV エクスポートは API でサポートされていますか?

JSON 出力が標準ですが、結果を手動で CSV に変換することは可能です。ネイティブの CSV エクスポートはサポートされなくなりました。

追加リソース

「いいね!」 39
Watching API
"DataExplorer::ValidationError: Missing parameter end_date of type string
Get total list of topics and their view counts from Discourse API
Best API for All First Posts in a Category
Category API request downloads all topics
Reports by Discourse
TimeStamp of Tag
How can I get the list of Discourse Topic IDs dynamically
Passing params to Data Explorer using API requires enclosing a value
Get Latest topic for Current user
Getting recently updated posts using the REST API
`DataExplorer::ValidationError: Missing parameter` when running Data Explorer queries with [params] via API
`DataExplorer::ValidationError: Missing parameter` when running Data Explorer queries with [params] via API
Backend data retrieve for analytics
Discourse-user-notes API
Admin dashboard report reference guide
How to query the topics_with_no_response.json API with filters
Use API to get topics for a period using js
Access Discourse database with n8n
Why getUserById doesn't return the user's email?
Grant a custom badge through the API
Is there an API endpoint for recently edited posts
How to query gamification score via the API?
1.5X cheers on specific TL's or groups
Page Publishing
Identifying users in multiple groups using AND rather than OR?
Validation error even when parameter passed while running data explorer API with Curl
How to change the response default_limit in data explorer?
How to change the response default_limit in data explorer?
Order/Filter searched topics by latest update to First Post
API Filter users by emails, including secondary emails
Ability to have granular scope for data explorer?
Daily, weekly, or total stats by user over a specified time range
Looking for help posting automating data explorer reports to my forum
How to get all topics from a specific category using offset/page param in the API query?
Discourse 有哪个接口能直接获取某个帖子的最后一条评论信息
想得到活跃的用户——通过api
API endpoint to create invite links has moved to /invites.json
How to get a password from database?
Can I send an external URL to the Discourse API for it to return topics linking to that URL?
How to fetch posts/topics by multiple usernames
Restrict moderator access to only the stats panel on the admin dashboard?
How to get all the deleted posts for a specific topic
Discourse forum traffic query data
Download a user's posting history via Discourse API?
Discourse Data Explorer Query Response to Slack
Filter topics in category containing file attachments
Discord Integration with Webhooks
Download result of queries into Google Spreadsheet
Who's online "API"?
Is there any endpoint that would provide a user's external account IDs from their Discourse ID?
API post request without an Accept header returns 406
Best way to get (via API) a list of users from a group, and their bios
Create a Data Explorer query using the API
How to get a full list of badges of all users
API rate limits

このコメントは、APIからCSVエクスポートができることを示唆しているようです。それは可能ですか?CSVとしてデータが必要なので、単に興味があります。JSONとして取得してCSVに変換することは常にできますが、CSVを取得する組み込みの方法があれば、もう少し簡単になります。

「過去50秒間に作成または更新された」というクエリを実行することは可能ですか?

:robot: AI says

-- [params]
-- int :seconds = 50

SELECT
    p.id AS post_id,
    p.created_at,
    p.updated_at,
    p.raw AS post_content,
    p.user_id,
    t.title AS topic_title,
    t.id AS topic_id
FROM posts p
INNER JOIN topics t ON t.id = p.topic_id
WHERE
    (EXTRACT(EPOCH FROM (NOW() - p.created_at)) <= :seconds
    OR EXTRACT(EPOCH FROM (NOW() - p.updated_at)) <= :seconds)
    AND p.deleted_at IS NULL
    AND t.deleted_at IS NULL
ORDER BY p.created_at DESC
LIMIT 50

簡単なテストをしたところ、動作するようです :slight_smile:

「いいね!」 1

投稿に追加した「いいね :heart:」が表示されません。

post_actions を使用する必要があると思います。

--[params]
--string :timespan = 50 seconds

SELECT post_id,
       user_id,
       created_at,
       updated_at,
       deleted_at
FROM  post_actions
WHERE post_action_type_id=2 AND updated_at > NOW() - INTERVAL :timespan

より見栄えの良い結果が得られるバージョン

--[params]
--string :timespan = 50 seconds
--boolean :include_in_timespan_deleted = false

SELECT
  post_id,
  user_id,
  CASE
    WHEN EXTRACT(EPOCH FROM (NOW() - created_at)) < 60 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - created_at))), ' seconds ago')
    WHEN EXTRACT(EPOCH FROM (NOW() - created_at)) < 3600 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - created_at)) / 60), ' minutes ago')
    WHEN EXTRACT(EPOCH FROM (NOW() - created_at)) < 86400 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - created_at)) / 3600), ' hours ago')
    ELSE CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - created_at)) / 86400), ' days ago')
  END AS relative_created_at,
  CASE
    WHEN EXTRACT(EPOCH FROM (NOW() - updated_at)) < 60 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - updated_at))), ' seconds ago')
    WHEN EXTRACT(EPOCH FROM (NOW() - updated_at)) < 3600 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - updated_at)) / 60), ' minutes ago')
    WHEN EXTRACT(EPOCH FROM (NOW() - updated_at)) < 86400 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - updated_at)) / 3600), ' hours ago')
    ELSE CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - updated_at)) / 86400), ' days ago')
  END AS relative_updated_at,
  CASE
    WHEN deleted_at IS NULL THEN 'no'
    WHEN EXTRACT(EPOCH FROM (NOW() - deleted_at)) < 60 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - deleted_at))), ' seconds ago')
    WHEN EXTRACT(EPOCH FROM (NOW() - deleted_at)) < 3600 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - deleted_at)) / 60), ' minutes ago')
    WHEN EXTRACT(EPOCH FROM (NOW() - deleted_at)) < 86400 THEN CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - deleted_at)) / 3600), ' hours ago')
    ELSE CONCAT(ROUND(EXTRACT(EPOCH FROM (NOW() - deleted_at)) / 86400), ' days ago')
  END AS relative_deleted_at
FROM
  post_actions
WHERE
  post_action_type_id = 2
  AND updated_at > NOW() - INTERVAL :timespan
  AND (
    :include_in_timespan_deleted = false
    OR (deleted_at IS NOT NULL AND deleted_at > NOW() - INTERVAL :timespan)
  )

「いいね!」 2

ああ、読み間違えました!「作成または更新された最後の50秒」のようなクエリを実行できますか?

(一重引用符の位置に注意してください)

「いいね!」 1

皆さん、ありがとうございます。

ask.discourse が私をここに誘導したので、ここに投稿しました。
API がこの機能を提供しているかどうかを知りたいのです。

「いいね!」 1

はい、もちろんです。

データエクスプローラーでクエリを作成し、このガイドで説明されているようにAPI経由でクエリを実行します。

MoinのクエリをAPI経由で実行したところ、期待どおりの結果が正しく返されました。

「いいね!」 4

私もこれについて疑問に思っていました。API経由でデータをエクスポートする方法はJSONだけですか、それともData ExplorerではCSVエクスポートもサポートされていますか?

皆さん、ありがとうございます。

返信が遅くなり申し訳ありません。少しの間オフラインでした。

現在行っているのは、今日作成されたすべてのトピック/投稿を検索し、タイムスタンプより前に更新されたトピック/投稿を除外することです。

「いいね!」 1

自分でやりたくない場合は、ask.discourse.com のボットに尋ねることができます。通常、Discourse 関連の SQL クエリに関してはかなり正確です(ただし、正しいと 仮定 せずに、コードを確認して確実にしてください)。

「いいね!」 1

Content-Type ヘッダーは正しいですか?
開発者ツールで、パラメータ付きのData Explorerクエリを検査すると、Content-Type ヘッダーは次のように表示されます。

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

しかし、現在の cURL コマンドには次が含まれています。

-H "Content-Type: multipart/form-data;"


「いいね!」 1
  • multipart/form-data
  • application/x-www-form-urlencoded
  • application/json

は、APIリクエストを行う際に使用できる有効なコンテンツタイプです。

「いいね!」 1

@blake
language python
library requests
APIサンプルData Explorerクエリを、3つのパラメータを含めて提供していただけますか?
パラメータは、paramsを厳密に二重引用符で囲む必要があると記載されているTopicを参照してください。

はい、Python を使用した例を次に示します。

import json
import requests

API_KEY      = "YOUR_API_KEY"
API_USERNAME = "system"
QUERY_ID     = 20
SITE_URL     = "https://your-site-url"

# すべての値は文字列である必要があります
params = {
    "user_id":   "2",
    "viewed_at": "2019-06-10",
    "limit":     "5"
}

# Data Explorer は params を JSON エンコードされた文字列として期待します
payload = {
    "params": json.dumps(params)
}

url = f"{SITE_URL}/admin/plugins/explorer/queries/{QUERY_ID}/run"
headers = {
    "Api-Key":       API_KEY,
    "Api-Username":  API_USERNAME,
    "Content-Type":  "application/json"
}

r = requests.post(url, headers=headers, json=payload)
r.raise_for_status()
print(r.json())
「いいね!」 3