Nexa Intelligence Docs
Data & Storage

MongoDB

Spesifikasi teknis MongoDB di platform Nexa Intelligence, arsitektur koleksi operational data store (ODS) & registry crawler, sinkronisasi Postgres-to-Mongo, buffer staging dokumen, dan dead-job tracking.

MongoDB Operational Data Store & Crawler Registry

MongoDB bertindak sebagai Operational Data Store (ODS) dan Registry Target Crawler Berkecepatan Tinggi dalam ekosistem Nexa Intelligence Platform. Beroperasi pada database nexa, MongoDB memegang peran vital sebagai penghubung fleksibel antara System of Record transaksional di PostgreSQL dengan Job Dispatcher asinkron di A1 Scraper Controller (nexa-a) dan berbagai komponen worker.


Peran Arsitektural dalam Nexa

┌───────────────────────────────┐
│     PostgreSQL Database       │ ── System of Record (Relational Master Data,
│   (Master Data & Taxonomy)    │    Taxonomy Nodes, Organizations, Users)
└──────────────┬────────────────┘
               │
               │ POST /api/v1/sync (Bulk Upsert via f1-api-backend)
               ▼
┌───────────────────────────────┐
│        MongoDB (nexa)         │ ── Operational Data Store (ODS) & Registries:
│   • targets                   │    Struktur dokumen denormalisasi siap pakai
│   • target_keywords           │    tanpa JOIN multi-tabel yang membebani RDBMS
│   • service_settings          │
└──────────────┬────────────────┘
               │
               │ Polling / Query ListActive() & ListKeywordsByTarget()
               ▼
┌───────────────────────────────┐
│  A1 Scraper Controller (Go)   │ ── Mengubah dokumen target menjadi event pekerjaan
└──────────────┬────────────────┘
               │
               │ Emit event jobbus.Job
               ▼
┌───────────────────────────────┐
│  Kafka Topic: `queue-job`     │ ── Dikonsumsi oleh B1 Scraper & Crawler Swarm
└───────────────────────────────┘
  1. Denormalized Dispatch Registry (targets & target_keywords): Menyediakan struktur dokumen siap konsumsi untuk A1 Scraper Controller (Golang). Service A tidak perlu menjalankan query multi-JOIN relasional yang berat ke PostgreSQL untuk menentukan target dan taksonomi yang harus di-crawl.
  2. Dynamic Runtime Settings (service_settings): Menyimpan konfigurasi adaptif per-service (rate limit dinamis, konkurensi scraping, parameter retry, interval tick) serta status heartbeat liveness worker.
  3. Staging & Raw Document Buffer: Menyediakan buffer sementara dokumen payload mentah hasil crawling sebelum diproses melalui pipeline normalisasi C1 dan deduplikasi semantik C2.

Katalog Koleksi Database nexa

Seluruh koleksi di MongoDB berada di bawah database terpusat nexa:

Nama KoleksiPenulis Utama (Writer)Pembaca Utama (Reader)Deskripsi & Fungsi Arsitektur
targetsf1-api-backend (Sync) / Seeda1-scraper-controllerDaftar domain target scraping aktif (portal berita nasional/daerah, LPSE, dsb.) beserta konfigurasi selector dan interval crawling.
target_keywordsf1-api-backend (Sync)a1-scraper-controllerPasangan kata kunci taksonomi & daftar sinonim yang diikatkan ke masing-masing target scraping (targetId).
sourcesf1-api-backend (Sync)Scraper Engine / AuditRegistry legacy sumber data portal, adapter crawling, dan konfigurasi template request.
keywordsf1-api-backend (Sync)Legacy Scraper / FilterKatalog kata kunci global dari pohon taksonomi 5 pilar Nexa.
social_keywordsf1-api-backendB3 Scraper / IngestionKata kunci pemantauan dinamis untuk media sosial (X/Twitter, TikTok).
service_settingsf1-api-backend / WorkerScraper Services & AdminDokumen kontrol runtime service: dokumen <service> (konfigurasi yang dikirim) dan <service>:status (status operasional dari worker).

1. Skema Dokumen Koleksi Utama

Koleksi targets

Menyimpan spesifikasi scraping lengkap untuk setiap entitas portal atau sumber data:

{
  "_id": { "$oid": "66f4b1e5a1b2c3d4e5f67890" },
  "id": "5b9c3e7a-1d48-4062-9f8b-2a6c0d4e1f37",
  "domainName": "detik.com",
  "baseUrl": "https://news.detik.com/indeks",
  "sectorId": "71db32ab-80bb-4136-aa22-77e9c730826e",
  "sectorName": "swasta",
  "region": {
    "regionid": "a3f1c7d9-2b64-4e08-9c15-7d6a2f8b3e40",
    "regionname": "Indonesia",
    "regionlevel": "nasional"
  },
  "organization": {
    "orgid": "d4e2b8a1-6c39-4f57-8a2d-1b9e0c7f4a63",
    "orgname": "Detik Network",
    "orgtype": "media_nasional",
    "parentorgid": null
  },
  "scrapingConfig": {
    "engine": "news",
    "maxpages": 3,
    "timeoutms": 30000,
    "htmlselectors": {
      "articlewrapper": "article.list-content__item",
      "title": "h3.media__title a",
      "bodycontent": "div.detail__body-text",
      "publisheddate": "div.media__date"
    }
  },
  "active": true,
  "scrapeInterval": 300000000000
}

Koleksi target_keywords

Menghubungkan target spesifik dengan simpul taksonomi dan kumpulan frasa sinonim:

{
  "_id": { "$oid": "66f4b2a8a1b2c3d4e5f67891" },
  "id": "8d3e12ab-44c1-4b72-911a-821ef9a13092",
  "targetId": "5b9c3e7a-1d48-4062-9f8b-2a6c0d4e1f37",
  "phrase": "jambret",
  "phrases": [
    "begal motor",
    "jambret",
    "penjambretan jalanan",
    "rampas tas"
  ],
  "taxonomyLineage": {
    "leafnodeid": "18d2c278-963f-4573-932e-5f8626a2cd77",
    "leafnodename": "Penjambretan",
    "nodelevel": 5,
    "ancestrypathids": [
      "c210d284-5df1-4dcb-8f2b-7e0b511d911a",
      "2797e0c4-f0c2-4afa-b765-98234925f746",
      "3a4505fc-d342-477a-834e-738861e92a01",
      "f33b293c-bb3f-4baa-849d-5523b6a05d80",
      "18d2c278-963f-4573-932e-5f8626a2cd77"
    ],
    "ancestrypathnames": [
      "Sosial",
      "Keamanan & Ketertiban",
      "Kriminalitas",
      "Street Crime",
      "Penjambretan"
    ]
  },
  "active": true
}

Koleksi service_settings

Mengatur perilaku konkurensi worker tanpa interupsi restart:

{
  "_id": { "$oid": "66f4b301a1b2c3d4e5f67892" },
  "service": "a1-scraper-controller",
  "cadence_tick_ms": 1000,
  "default_scrape_interval_minutes": 5,
  "max_concurrent_targets": 20,
  "updated_at": "2026-10-02T08:00:00Z"
}

2. Mekanisme Sinkronisasi Postgres-to-Mongo

Sinkronisasi dari PostgreSQL ke MongoDB diorkestrasikan oleh F1 API Backend (POST /api/v1/sync atau background cron):

Setiap eksekusi sinkronisasi mengekstrak data relasional termutakhir dari tabel master PostgreSQL:

  1. scraping_targets bergabung dengan organization_nodes, master_sectors, dan master_regions.
  2. scraping_target_keywords digabungkan dengan taxonomy_keywords dan taxonomy_nodes.
  3. Frasa sinonim dikelompokkan menjadi array per target menggunakan fungsi SQL array_agg(...).
  4. Dokumen dipetakan ke skema denormalisasi MongoDB dan di-upsert via replaceOne dengan opsi upsert: true.

3. Konsumsi Data oleh A1 Scraper Controller (Go)

Service Go a1-scraper-controller memanfaatkan driver resmi MongoDB Go untuk membaca koleksi target aktif dan memancarkan tugas crawling:

// Cuplikan dari targetrepo/targetrepo.go (a1-scraper-controller)
type MongoRepo struct {
    client   *mongo.Client
    targets  *mongo.Collection
    keywords *mongo.Collection
}

func (m *MongoRepo) ListActive(ctx context.Context) ([]target.Target, error) {
    cur, err := m.targets.Find(ctx, bson.M{"active": true})
    if err != nil {
        return nil, err
    }
    defer cur.Close(ctx)

    var out []target.Target
    if err := cur.All(ctx, &out); err != nil {
        return nil, err
    }
    return out, nil
}

func (m *MongoRepo) ListKeywordsByTarget(ctx context.Context, targetID string) ([]target.Keyword, error) {
    cur, err := m.keywords.Find(ctx, bson.M{"targetId": targetID, "active": true})
    if err != nil {
        return nil, err
    }
    defer cur.Close(ctx)

    var out []target.Keyword
    if err := cur.All(ctx, &out); err != nil {
        return nil, err
    }
    return out, nil
}

Setiap target yang siap di-crawl di-assemble menjadi payload jobbus.Job (dengan UUID job_id unik) lalu dikirim ke Kafka topic queue-job.


4. Konfigurasi Lingkungan & Kebijakan Fail-Fast

Variabel Lingkungan

# Koneksi MongoDB Cluster Nexa
MONGO_URL=mongodb://localhost:27017
# Port internal pada container Docker Compose: 27017 (Dev host mapping sering menggunakan 27018)

Kebijakan Timeout & Resiliensi (Fail-Fast)

Secara default, MongoDB Node driver memiliki batas waktu pencarian server (serverSelectionTimeoutMS) sebesar 30 detik. Dalam lingkungan produksi, latensi setinggi ini dapat menyebabkan worker atau request HTTP menggantung (hang) dan menghasilkan cascade failure.

Di Nexa (f1-api-backend/src/config/mongo.ts), parameter tersebut dibatasi secara ketat menjadi 3 detik (3_000 ms):

  • Jika kluster MongoDB mengalami partisi jaringan atau proses failover pemilihan primer melebihi 3 detik, request segera dihentikan dengan status HTTP 503 Service Unavailable (dapat di-retry) alih-alih melempar error 500 Internal Server Error.
  • Probe /health menggunakan batas waktu yang lebih agresif yaitu 2 detik agar health check tidak menjadi titik lambat gateway.

5. Rekomendasi Indeks Performa Koleksi

Untuk memastikan query berkecepatan sub-milidetik pada volume data besar, indeks komposit berikut diaplikasikan pada MongoDB:

// mongosh "mongodb://localhost:27017/nexa"

// 1. Koleksi targets: Pencarian cepat target berstatus aktif
db.targets.createIndex({ "active": 1 });
db.targets.createIndex({ "id": 1 }, { unique: true });

// 2. Koleksi target_keywords: Pengambilan kata kunci berdasarkan ID target
db.target_keywords.createIndex({ "targetId": 1, "active": 1 });
db.target_keywords.createIndex({ "id": 1 }, { unique: true });

// 3. Koleksi service_settings: Pengambilan pengaturan dinamis service
db.service_settings.createIndex({ "service": 1 }, { unique: true });

On this page