> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# Metabase: لوحات وتقارير من بياناتك دون كتابة كود
- URL: https://arabroot.io/articles/metabase-لوحات-وتقارير-من-بياناتك/
- Published: 2026-10-08T07:30:00.000Z
- Updated: 2026-10-12T05:26:09.000Z
- Description: كيف تثبت Metabase على سيرفرك، وتبني منه أسئلة ولوحة مبيعات من بيانات شركتك بالنقر أو بلغة SQL.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, الذكاء الاصطناعي والبيانات, قواعد البيانات, تطبيقات لفريقك, الاستضافة الذاتية, Metabase

كل أسبوع يسألك المدير السؤال نفسه: كم بعنا هذا الشهر؟ وأي مدينة الأعلى؟ وما المنتج الذي تراجع؟ فتفتح قاعدة البيانات وتكتب استعلام SQL وتنسخ النتيجة إلى ملف Excel وترسله، ثم يأتي السؤال التالي بعد ساعة بفلتر مختلف. والحل الذي تستخدمه الفرق لهذه المشكلة هو أداة ذكاء الأعمال Business Intelligence، واختصاراً BI، وهي أداة تتصل بقاعدة بياناتك وتسمح لغير المبرمج أن يسأل البيانات ويرى النتيجة رسماً بيانياً، وتجمع الرسوم في لوحة Dashboard تتحدث أرقامها وحدها. وهذا المقال هو أول مقال في [مرحلة البيانات وذكاء الأعمال](https://arabroot.io/start/#stage-data)، ونستخدم فيه Metabase لأنها أسهل أداة مفتوحة المصدر يبدأ بها فريق صغير، وسوف نناقش ما يلي:

- ما هي أداة BI، ومعنى مصدر البيانات والسؤال Question والبطاقة Card واللوحة Dashboard والفلتر.
- من أين تقرأ الأداة بياناتها، ولماذا لا تربطها بقاعدة الإنتاج بمستخدم المالك.
- مكونات Metabase: قاعدة بياناتها الخاصة وقاعدة بياناتك، والرخصة والإصدارات التجارية.
- تجربة عملية: متجر تجريبي على PostgreSQL ببيانات عربية، ثم تثبيت Metabase بإصدار مثبت وقاعدة تطبيق PostgreSQL، وبناء أسئلة ولوحة بفلاتر.
- العربية في Metabase بصراحة: ما يعمل وما لا يعمل.
- النسخ الاحتياطي والتحديث والتشغيل في الإنتاج.

## ما هي أداة ذكاء الأعمال BI؟

أداة BI تقف بين قاعدة البيانات والشخص الذي يريد أرقاماً، وفيها أربعة مفاهيم تتكرر في كل أداة، سواء كانت Metabase أو Superset أو Power BI:

- **مصدر البيانات Data Source:** قاعدة بيانات تتصل بها الأداة، مثل PostgreSQL أو MySQL، أو ملف CSV ترفعه.
- **السؤال Question أو الاستعلام Query:** طلب محدد مثل «مجموع المبيعات لكل مدينة»، تبنيه بالنقر في واجهة رسومية أو تكتبه بلغة SQL.
- **البطاقة Card أو الرسم Chart:** نتيجة السؤال مع طريقة عرضها، رقم كبير أو أعمدة أو خط زمني أو جدول.
- **اللوحة Dashboard:** مجموعة بطاقات في صفحة واحدة، ومعها فلاتر Filters مثل المدينة والفترة تغير كل البطاقات معاً، وكل من يفتحها يرى أرقاماً محدثة من قاعدة البيانات مباشرة.

والصورة التالية تبين الطريق من البيانات إلى اللوحة:

![مخطط فيه ثلاثة مصادر بيانات PostgreSQL وMySQL وملف CSV تتصل بأداة BI عبر مستخدم للقراءة فقط، وداخل الأداة سؤال بالنقر وسؤال SQL يتحولان إلى بطاقة فيها نتيجة ونوع رسم، ثم لوحة Dashboard بفلاتر يراها المدير وفريق المبيعات](https://arabroot.io/content/images/2026/10/metabase-01-bi-flow.webp)

المصدر، ثم السؤال، ثم البطاقة، ثم اللوحة التي يراها الفريق

وقد يتساءل البعض: لماذا لا نكتفي بجدول Excel يحدث يدوياً؟ والإجابة أن الملف صورة للبيانات في لحظة تصديره، فكل سؤال جديد يحتاج تصديراً جديداً، وكل نسخة في بريد شخص مختلف قد تكون قديمة، أما اللوحة فتسأل قاعدة البيانات في كل مرة تفتح فيها، والصلاحيات فيها تحدد من يرى ماذا.

## من أين تقرأ الأداة بياناتها؟

الطريقة البسيطة أن تعطي الأداة عنوان قاعدة الإنتاج وكلمة مرور المستخدم الذي يملكها، وهذه الطريقة غير مناسبة إطلاقاً لسببين: الأول أن تقريراً واحداً ثقيلاً، مثل مجموع مبيعات سنتين دون فهرس، يقرأ ملايين الصفوف ويأخذ المعالج والذاكرة من التطبيق الذي يخدم العملاء، والثاني أن مستخدم المالك يستطيع أن يعدل ويحذف، فخطأ واحد في محرر SQL داخل الأداة يغير بيانات حقيقية. والصورة التالية تبين الطريقتين:

![مخطط من جزأين: في الطريقة البسيطة تتصل أداة BI بقاعدة الإنتاج بمستخدم المالك فيبطؤ المتجر بسبب مسح كامل لسنتين من الطلبات، وفي الطريقة الأنسب تقرأ الأداة من نسخة للقراءة Read Replica بمستخدم bi_reader لديه صلاحية SELECT فقط والتطبيق يكتب في القاعدة الرئيسية](https://arabroot.io/content/images/2026/10/metabase-02-where-to-query.webp)

التقارير تقرأ من نسخة بمستخدم للقراءة فقط، والمتجر يحتفظ بسرعته

لذلك فالقاعدة هي: مستخدم قاعدة بيانات للقراءة فقط خاص بأداة BI، وإذا كانت التقارير ثقيلة أو القاعدة مشغولة فاجعل الأداة تقرأ من نسخة للقراءة Read Replica كما شرحنا في [توجيه القراءة في عناقيد قواعد البيانات](https://arabroot.io/articles/%D8%B9%D9%86%D8%A7%D9%82%D9%8A%D8%AF-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#read-routing)، أو من مخزن بيانات Data Warehouse منفصل، وهذا موضوع مخطط له في [مرحلة البيانات](https://arabroot.io/start/#stage-data). وسوف نرى في التجربة كيف يرفض PostgreSQL أمر الحذف من مستخدم القراءة.

## مكونات Metabase وقاعدة بياناتها الخاصة

[Metabase](https://www.metabase.com/?ref=arabroot.io) تطبيق Java واحد يعمل على المنفذ 3000، ولكن لاحظ أن هناك قاعدتي بيانات مختلفتين في كل تركيب، كما تبين الصورة التالية:

![مخطط فيه المتصفح ثم Reverse Proxy مع HTTPS ثم Container لتطبيق Metabase على المنفذ 3000، ومنه سهم إلى قاعدة التطبيق PostgreSQL التي تحفظ الأسئلة واللوحات والمستخدمين والصلاحيات والاتصالات، وسهم بمستخدم bi_reader إلى قاعدة بيانات المتجر، وسهم متقطع إلى مصادر أخرى، وتحذير أن ملف H2 الافتراضي داخل ال Container للتجربة فقط](https://arabroot.io/content/images/2026/10/metabase-03-metabase-parts.webp)

قاعدة التطبيق تحفظ عمل فريقك، وقاعدة المتجر هي البيانات التي تسألها

- **قاعدة التطبيق Application Database:** تحفظ فيها Metabase الأسئلة واللوحات والمستخدمين والصلاحيات وبيانات الاتصال بمصادرك، وبشكل افتراضي هي ملف H2 داخل ال Container، ولكن [توثيق قاعدة التطبيق](https://www.metabase.com/docs/latest/installation-and-operation/configuring-application-database?ref=arabroot.io) يوصي ب PostgreSQL للإنتاج ويصف H2 بأنه للتجربة المحلية وينصح بتجنبه في الإنتاج، والسبب أن الملف يضيع مع ال Container إذا لم تربطه ب Volume، وأنه أضعف في الأداء والموثوقية، ولهذا توجد صفحة كاملة عن [الانتقال من H2](https://www.metabase.com/docs/latest/installation-and-operation/migrating-from-h2?ref=arabroot.io).
- **قاعدة بياناتك:** المصدر الذي تسأله، وتتصل به Metabase بالمستخدم الذي تعطيها إياه.
- **مفتاح التشفير:** بيانات الاتصال بمصادرك، ومنها كلمات المرور، تحفظ في قاعدة التطبيق، والمتغير `MB_ENCRYPTION_SECRET_KEY` يجعلها مشفرة كما في [توثيق التشفير](https://www.metabase.com/docs/latest/databases/encrypting-details-at-rest?ref=arabroot.io)، ويجب أن يكون 16 حرفاً على الأقل بحسب [صفحة المتغيرات](https://www.metabase.com/docs/latest/configuring-metabase/environment-variables?ref=arabroot.io).

### الرخصة والإصدارات التجارية

بحسب [ملف الرخصة في المستودع](https://github.com/metabase/metabase/blob/master/LICENSE.txt?ref=arabroot.io) فالكود خارج المجلد `enterprise` برخصة AGPL، وما داخله برخصة Metabase التجارية، وال Image `metabase/metabase` على Docker Hub هي النسخة المفتوحة برخصة AGPL، أما `metabase/metabase-enterprise` فبالرخصة التجارية. وتعرض [صفحة الأسعار](https://www.metabase.com/pricing/?ref=arabroot.io) النسخة المفتوحة مجاناً إلى جانب خطط مدفوعة Starter وPro وEnterprise، وبعض الميزات التي تحتاجها الشركات الأكبر مدفوعة، فمثلاً تقييد الصفوف والأعمدة لكل مجموعة متاح بحسب [صفحته](https://www.metabase.com/docs/latest/permissions/row-and-column-security?ref=arabroot.io) في Pro وEnterprise فقط. وتوضح [صفحة الرخص](https://www.metabase.com/license/?ref=arabroot.io) أن الرخصة التجارية تسمح أيضاً بتضمين Embedding اللوحات في تطبيقك دون أن يخضع تطبيقك لشروط AGPL، فإذا كنت ستضمن لوحات Metabase داخل منتج تبيعه فاقرأ الصفحة قبل أن تبدأ.

## تجربة عملية: متجر تجريبي ولوحة مبيعات

الآن سوف نقوم ببناء متجر تجريبي يبيع التمور والقهوة والعسل في عشر مدن سعودية، ثم نثبت Metabase ونبني عليه لوحة مبيعات. نشغل كل شيء في Docker على المنافذ المحلية `127.0.0.1` فقط، وإذا لم يكن Docker مثبتاً لديك فابدأ من [دليل تثبيت Docker](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-docker-%D8%B9%D9%84%D9%89-ubuntu/).

### بيانات المتجر ومستخدم القراءة

البيانات خمسة جداول: المدن والمنتجات والعملاء والطلبات وبنود الطلبات، ثم View اسمه `sales` يجمعها في جدول واحد مسطح يسهل السؤال منه، ثم مستخدم `bi_reader` لا يملك إلا القراءة. والأسماء العربية في البيانات عن قصد حتى نرى كيف تعرضها الأداة، وتصميم الجداول نفسه شرحناه في [تصميم قواعد البيانات](https://arabroot.io/articles/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D8%B9-%D9%88%D8%A7%D9%84%D8%AA%D9%88%D8%B3%D8%B9/):

```sql
-- Sample data for the BI lab: a fictional online store (orders, products, cities)
SELECT setseed(0.42);

CREATE TABLE cities (
  id     int PRIMARY KEY,
  name   text NOT NULL,
  region text NOT NULL
);
INSERT INTO cities VALUES
  (1,'الرياض','الوسطى'),(2,'جدة','الغربية'),(3,'مكة المكرمة','الغربية'),
  (4,'المدينة المنورة','الغربية'),(5,'الدمام','الشرقية'),(6,'الخبر','الشرقية'),
  (7,'أبها','الجنوبية'),(8,'تبوك','الشمالية'),(9,'بريدة','الوسطى'),(10,'حائل','الشمالية');

CREATE TABLE products (
  id       int PRIMARY KEY,
  name     text NOT NULL,
  category text NOT NULL,
  price    numeric(10,2) NOT NULL
);
INSERT INTO products VALUES
  (1,'تمر سكري 1 كجم','تمور',45),(2,'تمر عجوة 1 كجم','تمور',90),(3,'تمر خلاص 1 كجم','تمور',38),
  (4,'قهوة عربية شقراء 500 جم','قهوة',65),(5,'قهوة مختصة إثيوبية 250 جم','قهوة',58),(6,'هيل حب 250 جم','بهارات',72),
  (7,'دلة قهوة نحاس','أواني',140),(8,'فناجين قهوة 12 قطعة','أواني',55),(9,'زعفران 5 جم','بهارات',95),
  (10,'عسل سدر 500 جم','عسل',180),(11,'عسل طلح 500 جم','عسل',120),(12,'بخور عود 50 جم','بخور',210);

CREATE TABLE customers (
  id        int PRIMARY KEY,
  city_id   int NOT NULL REFERENCES cities(id),
  joined_at date NOT NULL
);
INSERT INTO customers
SELECT g,
       -- Riyadh and Jeddah get more customers than small cities
       (ARRAY[1,1,1,1,2,2,2,3,4,5,5,6,7,8,9,10])[1 + floor(random()*16)::int],
       date '2025-01-01' + floor(random()*600)::int
FROM generate_series(1, 3000) g;

CREATE TABLE orders (
  id          int PRIMARY KEY,
  customer_id int NOT NULL REFERENCES customers(id),
  created_at  timestamptz NOT NULL,
  status      text NOT NULL CHECK (status IN ('مكتمل','ملغي','مسترجع'))
);
INSERT INTO orders
SELECT g,
       1 + floor(random()*3000)::int,
       timestamptz '2025-01-01 08:00+03' + (random() * (timestamptz '2026-09-30' - timestamptz '2025-01-01')),
       CASE WHEN random() < 0.90 THEN 'مكتمل' WHEN random() < 0.6 THEN 'ملغي' ELSE 'مسترجع' END
FROM generate_series(1, 20000) g;

CREATE TABLE order_items (
  order_id   int NOT NULL REFERENCES orders(id),
  product_id int NOT NULL REFERENCES products(id),
  quantity   int NOT NULL,
  PRIMARY KEY (order_id, product_id)
);
INSERT INTO order_items
SELECT DISTINCT ON (o.id, p.pid) o.id, p.pid, 1 + floor(random()*3)::int
FROM orders o
CROSS JOIN LATERAL (SELECT 1 + floor(random()*12)::int AS pid FROM generate_series(1, 1 + (o.id % 3))) p;

-- One flat view for reports: each row is one product line in one order
CREATE VIEW sales AS
SELECT o.id            AS order_id,
       o.created_at,
       o.status,
       c.name          AS city,
       c.region,
       p.name          AS product,
       p.category,
       oi.quantity,
       oi.quantity * p.price AS revenue
FROM orders o
JOIN customers cu  ON cu.id = o.customer_id
JOIN cities c      ON c.id = cu.city_id
JOIN order_items oi ON oi.order_id = o.id
JOIN products p    ON p.id = oi.product_id;

-- A read-only user for the BI tool: it can read the data and nothing else
CREATE ROLE bi_reader LOGIN PASSWORD 'change-me-reader';
GRANT CONNECT ON DATABASE store TO bi_reader;
GRANT USAGE ON SCHEMA public TO bi_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO bi_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO bi_reader;
```

نحفظ الملف باسم `store.sql` ونشغل قاعدة المتجر على شبكة Docker خاصة بالتجربة، دون نشر منفذها على الجهاز:

```bash
docker network create lab-bi-net
docker run -d --name lab-bi-store --network lab-bi-net \
  -e POSTGRES_DB=store -e POSTGRES_USER=store_owner -e POSTGRES_PASSWORD=change-me-owner \
  -v lab-bi-store-data:/var/lib/postgresql postgres:18.6
docker exec -i lab-bi-store psql -U store_owner -d store < store.sql
```

والمخرج سوف يكون كما يلي:

```text
 setseed
---------

(1 row)

CREATE TABLE
INSERT 0 10
CREATE TABLE
INSERT 0 12
CREATE TABLE
INSERT 0 3000
CREATE TABLE
INSERT 0 20000
CREATE TABLE
INSERT 0 37860
CREATE VIEW
CREATE ROLE
GRANT
GRANT
GRANT
ALTER DEFAULT PRIVILEGES
```

قبل أن نعطي المستخدم لأي أداة نتأكد أنه يقرأ ولا يكتب، فنسأل بالمستخدم `bi_reader` عن أعلى خمس مدن، ثم نحاول أن نحذف طلباً:

```bash
docker exec -e PGPASSWORD=change-me-reader lab-bi-store psql -h 127.0.0.1 -U bi_reader -d store \
  -c "SELECT city, count(DISTINCT order_id) AS orders, round(sum(revenue)) AS revenue
      FROM sales WHERE status = 'مكتمل' GROUP BY city ORDER BY revenue DESC LIMIT 5;" \
  -c "DELETE FROM orders WHERE id = 1;"
```

والمخرج سوف يكون كما يلي:

```text
  city  | orders | revenue
--------+--------+---------
 الرياض |   4482 | 1644749
 جدة    |   3281 | 1217843
 الدمام |   2233 |  850585
 بريدة  |   1259 |  475893
 تبوك   |   1207 |  444478
(5 rows)

ERROR:  permission denied for table orders
```

القراءة تعمل والحذف مرفوض من قاعدة البيانات نفسها، وهذا هو المهم، فالحماية لا تعتمد على إعدادات Metabase ولا على حرص من يستخدمها.

### تثبيت Metabase مع قاعدة تطبيق PostgreSQL

نولد كلمة مرور قاعدة التطبيق ومفتاحي التشفير والجلسات في ملف `.env`، ثم نكتب ملف Compose بإصدار مثبت من Metabase وPostgreSQL:

```bash
mkdir -p ~/lab-bi/metabase && cd ~/lab-bi/metabase
cat > .env <<EOF
MB_DB_PASS=$(openssl rand -hex 16)
MB_ENCRYPTION_SECRET_KEY=$(openssl rand -base64 32)
MB_SESSION_SECRET_KEY=$(openssl rand -hex 24)
EOF
chmod 600 .env
```

```yaml
name: lab-bi

services:
  metabase-db:
    image: postgres:18.6
    container_name: lab-bi-mbdb
    restart: unless-stopped
    environment:
      POSTGRES_DB: metabase
      POSTGRES_USER: metabase
      POSTGRES_PASSWORD: ${MB_DB_PASS}
    volumes:
      - mbdb-data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U metabase -d metabase"]
      interval: 5s
      retries: 10

  metabase:
    image: metabase/metabase:v0.63.19.3
    container_name: lab-bi-metabase
    restart: unless-stopped
    depends_on:
      metabase-db:
        condition: service_healthy
    ports:
      - "127.0.0.1:19300:3000"
    environment:
      MB_DB_TYPE: postgres
      MB_DB_HOST: metabase-db
      MB_DB_PORT: 5432
      MB_DB_DBNAME: metabase
      MB_DB_USER: metabase
      MB_DB_PASS: ${MB_DB_PASS}
      MB_ENCRYPTION_SECRET_KEY: ${MB_ENCRYPTION_SECRET_KEY}
      MB_SESSION_SECRET_KEY: ${MB_SESSION_SECRET_KEY}
      MB_SITE_URL: http://127.0.0.1:19300
      JAVA_TIMEZONE: Asia/Riyadh
    healthcheck:
      test: ["CMD-SHELL", "curl -fs http://localhost:3000/api/health"]
      interval: 15s
      start_period: 300s
      retries: 5
    networks:
      - default
      - data

networks:
  data:
    name: lab-bi-net
    external: true

volumes:
  mbdb-data:
    name: lab-bi-mbdb-data
```

```bash
docker compose up -d
docker logs -f lab-bi-metabase
```

والمخرج المهم من السجل، بعد حذف بقية الأسطر:

```text
2026-10-07 18:33:11,527 INFO util.encryption :: Saved credentials encryption is ENABLED for this Metabase instance. 🔐
2026-10-07 18:35:21,461 INFO core.core :: Starting Metabase version v0.63.19.3 (c03ae81) ...
2026-10-07 18:35:25,054 INFO core.core :: Setting up and migrating Metabase DB. Please sit tight, this may take a minute...
2026-10-07 18:35:28,435 INFO app-db.setup :: Successfully verified PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) application database connection. ✅
2026-10-07 18:38:06,769 INFO app-db.encryption :: MB_ENCRYPTION_SECRET_KEY set on a new database. Marked database as encrypted. ✅
2026-10-07 18:38:14,032 INFO core.core :: Metabase Initialization COMPLETE
```

في الإعداد أعلاه لاحظ التالي:

- ثبتنا ال Image على `v0.63.19.3` وليس `latest` التي تستخدمها أمثلة [توثيق Docker](https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker?ref=arabroot.io)، والسبب أن `latest` قد تنقلك إلى إصدار رئيسي جديد يغير قاعدة التطبيق عند أول إعادة تشغيل دون أن تقرر ذلك، وقد صدر 0.64 أثناء كتابة المقال، فالتحديث يكون بقرار وبعد نسخة احتياطية.
- التشغيل الأول أخذ قرابة ثلاث دقائق لإنشاء جداول قاعدة التطبيق، لذلك جعلنا `start_period` في فحص الصحة خمس دقائق، فلا تظن أن ال Container معطل إذا لم يفتح فوراً.
- ال Container على شبكتين: الشبكة الافتراضية للمشروع مع قاعدة التطبيق، والشبكة `lab-bi-net` مع قاعدة المتجر، فقاعدة التطبيق لا تصل إليها قاعدة المتجر ولا العكس، والتفصيل في [شبكات Docker](https://arabroot.io/articles/%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-docker-%D9%88%D8%A3%D9%81%D8%B6%D9%84-%D8%A7%D9%84%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA/).
- `JAVA_TIMEZONE` تحدد المنطقة الزمنية التي تجمع عليها التقارير اليومية والشهرية، فطلب الساعة الواحدة فجراً بتوقيت الرياض يحسب في يومه الصحيح.

### الإعداد الأول وإضافة قاعدة المتجر

افتح `http://127.0.0.1:19300` فيظهر معالج الإعداد الأول، تكتب فيه اسمك وبريدك وكلمة مرور المدير واسم الموقع، ثم يسألك عن قاعدة بياناتك. والصورة التالية تبين نموذج إضافة قاعدة PostgreSQL من صفحة الإدارة، بالعنوان `lab-bi-store` وهو اسم ال Container على الشبكة، والمستخدم `bi_reader`:

![صفحة إضافة قاعدة بيانات في إدارة Metabase فيها الاسم store والعنوان lab-bi-store والمنفذ 5432 واسم القاعدة store والمستخدم bi_reader وكلمة المرور وخيار المخططات All وخيارا SSL ونفق SSH](https://arabroot.io/content/images/2026/10/metabase-04-add-database.webp)

الاتصال بقاعدة المتجر بمستخدم القراءة فقط

بعد الحفظ تقرأ Metabase بنية الجداول Sync وتعرض الجداول والأعمدة، ولاحظ خيار نفق SSH، فهو مفيد عندما تكون قاعدة البيانات على سيرفر آخر لا تريد أن تفتح منفذها للشبكة.

### السؤال الأول بالنقر

من زر **New** نختار **Question**، ثم البيانات `Sales`، ثم الفلتر `Status is مكتمل`، ثم **Summarize** مجموع `Revenue` حسب `City`، وأخيراً الترتيب تنازلياً، والصورة التالية تبين محرر الأسئلة Notebook بعد هذه الخطوات:

![محرر الأسئلة في Metabase بعنوان المبيعات حسب المدينة، فيه البيانات Sales والفلتر Status is مكتمل والتجميع Sum of Revenue by City والترتيب تنازلياً بمجموع المبيعات، وتحتها أزرار Filter وSummarize وJoin data وRow limit وCustom column وزر Visualize](https://arabroot.io/content/images/2026/10/metabase-05-notebook.webp)

سؤال بالنقر دون كتابة SQL: فلتر ثم تجميع ثم ترتيب

وعند الضغط على **Visualize** تختار Metabase نوع الرسم المناسب، والنتيجة كما في الصورة التالية:

![رسم أعمدة في Metabase لمجموع المبيعات في عشر مدن، الرياض أعلاها بقرابة 1.6 مليون ثم جدة ثم الدمام ثم بريدة وتبوك وحائل ومكة المكرمة والخبر والمدينة المنورة وأبها، وأسماء المدن بالعربية تحت الأعمدة، وفي الأسفل Showing 10 rows](https://arabroot.io/content/images/2026/10/metabase-06-question-bar.webp)

النتيجة رسم أعمدة، والأسماء العربية من البيانات تظهر صحيحة

وهذا هو الفرق الحقيقي الذي تقدمه Metabase لفريقك: موظف المبيعات الذي لا يعرف SQL يستطيع أن يبني هذا السؤال بنفسه، ويغير الفلتر إلى «ملغي» ليرى أين تلغى الطلبات أكثر.

### سؤال SQL بمتغيرات

بعض الأسئلة أسهل كتابة بلغة SQL، وMetabase تسمح بذلك من **New** ثم **SQL query**، والأهم أنها تسمح بمتغيرات تربطها بفلاتر اللوحة، كما في [توثيق متغيرات SQL](https://www.metabase.com/docs/latest/questions/native-editor/sql-parameters?ref=arabroot.io). والسؤال التالي يعرض أكثر خمسة منتجات مبيعاً، والمتغيران `{{city}}` و`{{created}}` من نوع Field Filter مرتبطان بعمودي المدينة والتاريخ، فتضيف Metabase الشرط المناسب مكانهما، وإذا لم يختر المستخدم قيمة يصبح الشرط صحيحاً دائماً:

```sql
SELECT product AS "المنتج", sum(quantity) AS "الكمية", round(sum(revenue)) AS "المبيعات"
FROM sales
WHERE status = 'مكتمل'
  AND {{city}}
  AND {{created}}
GROUP BY product
ORDER BY "المبيعات" DESC
LIMIT 5
```

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

### لوحة المبيعات بفلترين

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

![لوحة المبيعات في Metabase فيها فلترا المدينة والتاريخ، وبطاقة إجمالي المبيعات 6.7M ر.س، وخط المبيعات الشهرية من يناير 2025 إلى سبتمبر 2026، وأعمدة المبيعات حسب المدينة، ودائرة المبيعات حسب الفئة بإجمالي 6,681,949، وجدول أكثر المنتجات مبيعاً يتصدره بخور عود 50 جم](https://arabroot.io/content/images/2026/10/metabase-07-dashboard.webp)

لوحة المبيعات كاملة: خمس بطاقات وفلتران

وعند اختيار مدينة جدة وفترة الأشهر الاثني عشر السابقة تتغير البطاقات كلها معاً، كما في الصورة التالية:

![لوحة المبيعات نفسها بعد اختيار المدينة جدة والتاريخ Previous 12 months، فأصبح الإجمالي 698.4k ر.س، والخط الشهري من أكتوبر 2025، وعمود واحد لجدة، ودائرة الفئات بإجمالي 698,370، وجدول المنتجات بأرقام جدة](https://arabroot.io/content/images/2026/10/metabase-08-dashboard-filter.webp)

فلتر المدينة والفترة يغير كل البطاقات، بما فيها سؤال SQL

لاحظ أن الجدول الذي بنيناه بسؤال SQL تغير أيضاً، لأن المتغيرين فيه مربوطان بفلاتر اللوحة، أما الرقم الإجمالي فيعرض اختصاراً مثل `6.7M`، وتستطيع أن تغير التنسيق من إعدادات البطاقة إذا أردت الرقم كاملاً.

### العربية في Metabase بصراحة

هناك جانبان للعربية في أي أداة BI: البيانات العربية داخل الرسوم، وواجهة الأداة نفسها، وفيما يلي ما وجدناه في الإصدار v0.63.19.3:

- **البيانات العربية تعمل:** أسماء المدن والمنتجات والفئات تظهر صحيحة في الأعمدة والدوائر والجداول، وعناوين البطاقات واللوحة والفلاتر العربية كذلك، وعملية الفلترة بقيمة عربية مثل «مكتمل» تعمل.
- **الواجهة مترجمة إلى العربية:** قائمة اللغات في الإصدار الذي ثبتناه تحتوي `ar` و`ar_SA`، وتستطيع أن تختارها لكل مستخدم من إعدادات حسابه أو للموقع كله.
- **ولكن الاتجاه يبقى من اليسار لليمين:** القائمة الجانبية تبقى في اليسار والجداول والمحاور لا تنعكس، وهذا ما يقوله [توثيق اللغات](https://www.metabase.com/docs/latest/configuring-metabase/localization?ref=arabroot.io) صراحة: الواجهة مصممة حول اللغات التي تقرأ من اليسار لليمين، رغم أن Metabase تدعم لغات من اليمين لليسار.
- **الترجمة غير كاملة:** بعض النصوص بقيت بالإنجليزية في تجربتنا، مثل اسم عمود City تحت الرسم وجزء من عنوان المحور، لأن الأسماء التلقائية للأعمدة تأتي من أسماء الأعمدة في قاعدة البيانات، وتستطيع أن تغيرها من صفحة البيانات الوصفية Table Metadata إلى أسماء عربية.

والصورة التالية تبين اللوحة نفسها بعد اختيار العربية للمستخدم:

![لوحة المبيعات بعد اختيار اللغة العربية، الأزرار والفلاتر بالعربية مثل جديد ويبحث والأشهر 12 السابقة، والأشهر على المحور بالعربية مثل نوفمبر 2025 ويناير 2026، ولكن الصفحة ما زالت من اليسار لليمين، وCity وSum of Revenue جزئياً بالإنجليزية](https://arabroot.io/content/images/2026/10/metabase-09-arabic-ui.webp)

الواجهة بالعربية، والاتجاه ما زال من اليسار لليمين

والصورة التالية تبين الصفحة الرئيسية بالعربية، ولاحظ أن القائمة الجانبية مترجمة وفي اليسار:

![الصفحة الرئيسية في Metabase بالعربية فيها مرحباً Lab، والقائمة الجانبية في اليسار بعناصر الرئيسية وأضف بياناتك والمجموعات وتحليلاتنا وقواعد البيانات والنماذج والمقاييس وسلة المهملات، وبطاقات لوحة المبيعات والمبيعات حسب المدينة](https://arabroot.io/content/images/2026/10/metabase-10-arabic-home.webp)

الصفحة الرئيسية مترجمة، والقائمة في اليسار كما في الإنجليزية

وبالتالي فإذا كان فريقك يقرأ العربية ويقبل واجهة من اليسار لليمين فMetabase مناسبة، أما إذا كان شرطك واجهة عربية كاملة من اليمين لليسار فهذا لا يتوفر فيها اليوم، ولا في Superset كما سنرى في [دليل Superset](https://arabroot.io/articles/apache-superset-%D9%84%D9%84%D8%AA%D8%AD%D9%84%D9%8A%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9/).

### النسخ الاحتياطي لقاعدة التطبيق

كل عمل فريقك في Metabase، من الأسئلة واللوحات والمستخدمين والصلاحيات، موجود في قاعدة التطبيق، وبحسب [توثيق النسخ الاحتياطي](https://www.metabase.com/docs/latest/installation-and-operation/backing-up-metabase-application-data?ref=arabroot.io) يكفي أن تنسخ هذه القاعدة، لذلك نأخذ تفريغاً منها:

```bash
docker exec lab-bi-mbdb pg_dump -U metabase -d metabase -Fc -f /tmp/metabase-app.dump
docker exec lab-bi-mbdb ls -la /tmp/metabase-app.dump
```

```text
-rw-r--r-- 1 root root 1037434 Oct  8 07:31 /tmp/metabase-app.dump
```

ملف من قرابة 1 ميجابايت يحمل كل عمل الفريق، فانقله خارج ال Container وضعه ضمن نظام النسخ الذي شرحناه في [النسخ الاحتياطي بقاعدة 3-2-1](https://arabroot.io/articles/%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D8%A8%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-3-2-1-%D9%88%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%AF%D8%A9/)، وخذ نسخة قبل كل تحديث للإصدار، والسبب أن الإصدار الجديد يغير جداول قاعدة التطبيق عند أول تشغيل، فإذا فشل التحديث رجعت إلى الإصدار السابق مع نسخة القاعدة التي تناسبه. واحفظ أيضاً ملف `.env`، لأن نسخة القاعدة دون `MB_ENCRYPTION_SECRET_KEY` لا تفتح كلمات مرور الاتصال المشفرة فيها.

### التنظيف

```bash
cd ~/lab-bi/metabase && docker compose down -v
docker rm -f lab-bi-store
docker volume rm lab-bi-store-data
docker network rm lab-bi-net
```

## قبل أن تشغلها لفريقك

- **HTTPS أمامها:** انشر Metabase خلف Reverse Proxy بشهادة، مثل [Caddy](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-caddy-reverse-proxy/)، واجعل `MB_SITE_URL` العنوان الحقيقي، فالروابط في رسائل البريد والاشتراكات تبنى منه.
- **مستخدم قراءة لكل مصدر:** كما فعلنا مع `bi_reader`، وعلى نسخة للقراءة إن أمكن.
- **إصدار مثبت وتحديث مقصود:** اقرأ ملاحظات الإصدار، وخذ نسخة من قاعدة التطبيق، ثم غير الرقم في ملف Compose.
- **الصلاحيات من البداية:** أنشئ مجموعات للمبيعات والإدارة، وحدد من يرى أي قاعدة ومن يستخدم محرر SQL، قبل أن تدعو الفريق كله.
- **لا تجعل الاستعلامات الثقيلة مفاجأة:** البطاقة التي تقرأ ملايين الصفوف تبطئ كل من يفتح اللوحة، فأضف الفهارس المناسبة أو جداول ملخصة كما في [PostgreSQL لكل شيء](https://arabroot.io/articles/postgresql-%D9%84%D9%83%D9%84-%D8%B4%D9%8A%D8%A1-%D9%85%D8%AA%D9%89-%D8%AA%D9%83%D9%81%D9%8A%D9%83-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%AD%D8%AF%D8%A9/).

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- أداة BI تحول الأسئلة المتكررة إلى لوحات تتحدث وحدها، والمفاهيم المشتركة هي المصدر والسؤال والبطاقة واللوحة والفلتر.
- لا تربط الأداة بقاعدة الإنتاج بمستخدم المالك، وإنما بمستخدم للقراءة فقط، ويفضل على نسخة للقراءة، وقد رأينا PostgreSQL يرفض الحذف منه.
- Metabase تحفظ عملك في قاعدة تطبيق، فاستخدم PostgreSQL بدلاً من H2، واضبط `MB_ENCRYPTION_SECRET_KEY`، وانسخ القاعدة والمفتاح معاً.
- النسخة المفتوحة برخصة AGPL، وبعض الميزات مثل تقييد الصفوف والأعمدة والتضمين دون شروط AGPL في الخطط التجارية.
- البيانات العربية تعرض صحيحة والواجهة مترجمة، ولكن الاتجاه يبقى من اليسار لليمين.

وفي المقال التالي نثبت [Apache Superset](https://arabroot.io/articles/apache-superset-%D9%84%D9%84%D8%AA%D8%AD%D9%84%D9%8A%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9/) على البيانات نفسها، وهي أداة أثقل وأقوى لمن يكتب SQL، أما المقارنة الكاملة بين Metabase وSuperset وPower BI ففي [Metabase أم Superset أم Power BI؟](https://arabroot.io/articles/metabase-%D8%A7%D9%85-superset-%D8%A7%D9%85-power-bi/)

[![](https://arabroot.io/content/images/2026/10/superset-cover-cover.webp)دليلApache Superset: تحليلات ولوحات تقارير على سيرفرككيف تثبت Apache Superset على سيرفرك، وتبني منه مقاييس ورسوماً ولوحة تقارير من بيانات شركتك.](https://arabroot.io/articles/apache-superset-%D9%84%D9%84%D8%AA%D8%AD%D9%84%D9%8A%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9/)[![](https://arabroot.io/content/images/2026/10/db-clustering-cover-cover.webp)دليلعناقيد قواعد البيانات Database Clustering ومتى تحتاجهاماذا تعني كلمة Cluster في كل قاعدة بيانات، ومتى تحتاج نسخاً أو انتقالاً تلقائياً، ومتى يكفيك سيرفر واحد مع نسخ احتياطية جيدة.](https://arabroot.io/articles/%D8%B9%D9%86%D8%A7%D9%82%D9%8A%D8%AF-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/postgres-for-everything-cover-cover.webp)دليلPostgreSQL لكل شيء: متى يكفيك ومتى تحتاج غيره؟كم عملاً يستطيع PostgreSQL واحد أن يقوم به في شركتك الناشئة قبل أن تحتاج Redis أو Elasticsearch، وما الإشارة التي تقول لك إن الوقت قد حان.](https://arabroot.io/articles/postgresql-%D9%84%D9%83%D9%84-%D8%B4%D9%8A%D8%A1-%D9%85%D8%AA%D9%89-%D8%AA%D9%83%D9%81%D9%8A%D9%83-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%AD%D8%AF%D8%A9/)[![](https://arabroot.io/content/images/2026/10/backup-321-cover-cover.webp)دليلالنسخ الاحتياطي بقاعدة 3-2-1 واختبار الاستعادةكيف تحدد كم بيانات تقبل أن تخسر، وتبني نسخاً لا يستطيع المخترق حذفها، وتتأكد أنك تستطيع استعادتها فعلاً.](https://arabroot.io/articles/%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D8%A8%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-3-2-1-%D9%88%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%AF%D8%A9/)[![](https://arabroot.io/content/images/2026/10/db-design-cover-cover.webp)دليلتصميم قواعد البيانات: التطبيع Normalization ومتى تكسرهلماذا يفشل الجدول الواحد الكبير، وكيف تقسمه إلى جداول لا تناقض نفسها، وما الذي يتغير عندما تكبر البيانات.](https://arabroot.io/articles/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D8%B9-%D9%88%D8%A7%D9%84%D8%AA%D9%88%D8%B3%D8%B9/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، وتثبيت Metabase v0.63.19.3 مع قاعدة تطبيق PostgreSQL 18.6 في Docker، وبناء لوحة مبيعات على متجر تجريبي ببيانات عربية، واختبار الواجهة العربية.
- أكتوبر 2026: ربط مقال المقارنة بين أدوات التقارير بعد نشره.