تشغيل استعلامات Data Explorer باستخدام API Discourse

:bookmark: This guide explains how to use the Discourse API to create, run, and manage queries with the Data Explorer plugin.

:person_raising_hand: Required user level: Administrator

Virtually any action that can be performed through the Discourse user interface can also be triggered with the Discourse API.

This document provides a comprehensive overview for utilizing the API specifically in conjunction with the Data Explorer plugin.

For a general overview of how to find the correct API request for an action, see: Reverse engineer the Discourse API.

Running a Data Explorer query

To run a Data Explorer query via the API, make a POST request to /admin/plugins/explorer/queries/<query-id>/run. You can find the query ID by visiting it through your Discourse site and checking the id parameter in the address bar.

Below is an example query with an ID of 20 that returns topics by views on a specified date:

--[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

This query can be run from a terminal with:

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"}'

Note that you’ll need to replace the <api-key>, <your-site-url> with your API key and domain.

Creating a query via the API

To create a Data Explorer query via the API, you’ll need to make a POST request to /admin/plugins/explorer/queries.

You will also need to specify the query name and sql to use for the new query in the API call.

Below is an example of how to create a new query using the API:

curl -X POST "https://your-site-url/admin/plugins/explorer/queries" \
-H "Content-Type: multipart/form-data;" \
-H "Api-Key: <api-key>" \
-H "Api-Username: <username>" \
-F 'query[name]=Example Query' \
-F 'query[sql]=SELECT COUNT(*) FROM users'

This API call will return a response like:

{"query":{"id":49,"name":"Example Query","description":null,"username":"<username>","group_ids":[],"last_run_at":null,"user_id":1,"sql":"SELECT COUNT(*) FROM users","param_info":[],"created_at":"2025-03-13T18:41:44.226Z","hidden":false}}%

You can then run the query by using the query ID from the response (in this case, 49 ).

Returned results will be structured within the rows field.

Handling large datasets

The Data Explorer plugin limits results to 1000 rows by default. To paginate through larger datasets, you can use the example query below:

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

To fetch the results page-by-page, increment the page parameter in the request:

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"}'

Stop when result_count is zero.

For additional information about handling large datasets, see: Result Limits and Exporting Queries

Removing relations data from the results

When Data Explorer queries are run through the user interface, a relations object is added to the results. This data is used for rendering the user in UI results, but you are unlikely to need it when running queries via the API.

To remove that data from the results, add a download=true parameter with your request:

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 authentication

Details about generating an API key for the requests can be found here: Create and configure an API key.

If the API key is only going to be used to run Data Explorer queries, you can select “Granular” from the Scope drop down menu, then select the “run queries” scope.

FAQs

Is there any api endpoint I can use to get the list of reports and the ID numbers? I want to build a dropdown with the list in it?

Yes, you can make an authenticated GET request to /admin/plugins/explorer/queries.json to get a list of all queries on the site.

Is it possible to send parameters with the post request?

Yes, include SQL parameters using the -F option, as shown in the examples.

Is CSV export for queries supported by the API?

While JSON output is standard, you can manually convert results to CSV. Native CSV export is no longer supported.

Additional resources

39 إعجابًا

يبدو أن هذا التعليق يشير إلى إمكانية تصدير CSV من واجهة برمجة التطبيقات (API). هل هذا ممكن؟ أنا فضولي فقط لأنني أحتاج البيانات بصيغة CSV. يمكنني دائمًا الحصول عليها بصيغة JSON وتحويلها إلى CSV ولكن إذا كانت هناك طريقة مدمجة للحصول على CSV فسيكون ذلك أسهل قليلاً.

هل من الممكن إجراء استعلام “مثل تم إنشاؤه أو تحديثه في آخر 50 ثانية”؟

:robot: الذكاء الاصطناعي يقول

-- [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 إعجابات

كنت أتساءل عن هذا أيضًا. هل JSON هو الطريقة الوحيدة لتصدير البيانات عبر واجهة برمجة التطبيقات أم أن تصدير CSV مدعوم أيضًا لـ Data Explorer؟

شكرا للجميع،

آسف على التأخير في الرد - كنت غير متصل بالإنترنت لبعض الوقت.

ما أفعله حاليًا هو البحث عن جميع المواضيع/المشاركات التي تم إنشاؤها اليوم، وتصفية المواضيع/المشاركات التي تم تحديثها قبل الطابع الزمني.

إعجاب واحد (1)

إذا كنت لا ترغب في القيام بذلك بنفسك، يمكنك سؤال الروبوت على ask.discourse.com. عادة ما يكون دقيقًا جدًا فيما يتعلق باستعلامات SQL المتعلقة بـ Discourse (ولكن لا تفترض أنه صحيح، تحقق من الكود للتأكد).

إعجاب واحد (1)

هل رأس Content-Type صحيح؟
في أدوات المطور، عند فحص استعلام مستكشف البيانات مع المعلمات، يظهر رأس 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

هي أنواع محتوى صالحة يمكنك استخدامها عند إجراء طلب واجهة برمجة التطبيقات.

إعجاب واحد (1)

@blake
language python
library requests
هل يمكنك تقديم استعلام استكشاف البيانات بنموذج API يتضمن ثلاثة معلمات
مرجع [الموضوع]( Passing params to Data Explorer using API requires enclosing a value ) الذي يقول إن المعلمات تحتاج إلى أن تكون محاطة بعلامات اقتباس مزدوجة بشكل صارم

بالتأكيد، إليك مثال باستخدام بايثون:

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"
}

# يتوقع مستكشف البيانات المعلمات كسلسلة نصية مشفرة بصيغة 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 إعجابات