Pradžia / Programavimas / MongoDB pagrindai

MongoDB pagrindai

Kas yra MongoDB ir kodėl verta jį pažinti

Jei kada nors dirbai su tradicinėmis reliacinėmis duomenų bazėmis – MySQL, PostgreSQL ar panašiomis – turbūt žinai tą jausmą, kai schema tampa per griežta, o duomenų struktūra keičiasi greičiau, nei spėji atnaujinti lenteles. MongoDB atsirado kaip atsakas į šią problemą. Tai dokumentų orientuota NoSQL duomenų bazė, kuri saugo duomenis JSON formato dokumentuose, o ne eilutėse ir stulpeliuose.

MongoDB sukūrė kompanija 10gen (dabar vadinama MongoDB Inc.) 2009 metais. Nuo tada ji tapo viena populiariausių NoSQL sprendimų pasaulyje – ją naudoja tokios kompanijos kaip Forbes, eBay, Adobe ir daugybė startuolių. Bet populiarumas pats savaime nėra argumentas. Tikrasis klausimas – ar MongoDB tinka tau ir tavo projektui?

Trumpas atsakymas: MongoDB puikiai tinka, kai duomenų struktūra nėra griežtai apibrėžta, kai reikia greitai iteruoti, kai dirbi su hierarchiniais ar įdėtiniais duomenimis, arba kai reikia horizontalaus mastelio keitimo. Tačiau jei tavo aplikacija remiasi sudėtingomis transakcijomis tarp kelių lentelių – galbūt verta likti prie reliacinės duomenų bazės.

Pagrindinės sąvokos: dokumentai, kolekcijos ir duomenų bazės

Prieš nerdamas į techninius dalykus, reikia suprasti MongoDB terminologiją. Ji šiek tiek skiriasi nuo to, prie ko esame įpratę.

Dokumentas – tai pagrindinis duomenų vienetas MongoDB. Jis atrodo kaip JSON objektas, tik viduje saugomas BSON (Binary JSON) formatu, kuris leidžia saugoti daugiau duomenų tipų ir yra efektyvesnis. Štai paprastas dokumento pavyzdys:


{
  "_id": ObjectId("64a1b2c3d4e5f6789012345"),
  "vardas": "Jonas Jonaitis",
  "el_pastas": "[email protected]",
  "amzius": 28,
  "adresai": [
    {
      "tipas": "namų",
      "miestas": "Vilnius",
      "gatve": "Gedimino pr. 1"
    },
    {
      "tipas": "darbo",
      "miestas": "Kaunas",
      "gatve": "Laisvės al. 50"
    }
  ]
}

Atkreipk dėmesį – viename dokumente gali būti įdėtiniai masyvai ir objektai. Tai vienas didžiausių MongoDB privalumų. Reliacinėje duomenų bazėje adresams reikėtų atskiros lentelės su foreign key. Čia viskas telpa vienoje vietoje.

Kolekcija – tai dokumentų grupė, analogiška lentelei reliacinėje duomenų bazėje. Bet skirtumas esminis: kolekcijoje esantys dokumentai neprivalo turėti vienodos struktūros. Vienas dokumentas gali turėti lauką „telefonas”, kitas – ne. MongoDB tai leidžia be jokių papildomų konfigūracijų.

Duomenų bazė – kolekcijų rinkinys. Vienas MongoDB serveris gali turėti kelias duomenų bazes, kiekviena izoliuota nuo kitų.

Įdiegimas ir pirmieji žingsniai

Yra du būdai pradėti dirbti su MongoDB: įdiegti lokaliai arba naudoti MongoDB Atlas – debesų paslaugą su nemokamu planu. Pradedantiesiems rekomenduoju Atlas, nes nereikia rūpintis konfigūracija ir galima pradėti per kelias minutes.

Jei nori lokalaus įdiegimo Ubuntu sistemoje:


# Importuok MongoDB viešąjį GPG raktą
curl -fsSL https://pgp.mongodb.com/server-7.0.asc | \
   sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \
   --dearmor

# Pridėk MongoDB repozitoriją
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] \
https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list

# Įdiek
sudo apt-get update
sudo apt-get install -y mongodb-org

# Paleisk
sudo systemctl start mongod

Po įdiegimo paleisk mongosh – tai interaktyvus MongoDB apvalkalas, kuriame galėsi rašyti komandas tiesiogiai. Tai puiki vieta eksperimentuoti.

Sukurk pirmą duomenų bazę ir kolekciją:


// Perjunk į naują duomenų bazę (sukuriama automatiškai)
use mano_projektas

// Įterpk pirmą dokumentą
db.vartotojai.insertOne({
  vardas: "Petras Petraitis",
  el_pastas: "[email protected]",
  registracija: new Date()
})

Viskas. Kolekcija vartotojai sukurta automatiškai, kai įterpei pirmą dokumentą. Nereikia jokio CREATE TABLE.

CRUD operacijos: kaip dirbti su duomenimis

CRUD – Create, Read, Update, Delete – tai keturios pagrindinės operacijos su bet kokia duomenų baze. Pažiūrėkime, kaip jos veikia MongoDB.

Įterpimas (Create):


// Vieno dokumento įterpimas
db.produktai.insertOne({
  pavadinimas: "Nešiojamas kompiuteris",
  kaina: 899.99,
  kategorija: "elektronika",
  sandėlyje: true,
  savybes: ["16GB RAM", "512GB SSD", "Intel i7"]
})

// Kelių dokumentų įterpimas vienu kartu
db.produktai.insertMany([
  { pavadinimas: "Pelė", kaina: 29.99, kategorija: "priedai" },
  { pavadinimas: "Klaviatūra", kaina: 59.99, kategorija: "priedai" },
  { pavadinimas: "Monitorius", kaina: 349.99, kategorija: "elektronika" }
])

Paieška (Read):


// Visi dokumentai kolekcijoje
db.produktai.find()

// Su filtru
db.produktai.find({ kategorija: "elektronika" })

// Sudėtingesni filtrai
db.produktai.find({ 
  kaina: { $lt: 100 },  // kaina mažesnė nei 100
  sandėlyje: true 
})

// Tik tam tikri laukai (projekcija)
db.produktai.find(
  { kategorija: "priedai" },
  { pavadinimas: 1, kaina: 1, _id: 0 }
)

// Rikiavimas ir ribojimas
db.produktai.find().sort({ kaina: -1 }).limit(3)

Atnaujinimas (Update):


// Atnaujink vieną dokumentą
db.produktai.updateOne(
  { pavadinimas: "Pelė" },
  { $set: { kaina: 24.99, atnaujinta: new Date() } }
)

// Atnaujink kelis dokumentus
db.produktai.updateMany(
  { kategorija: "priedai" },
  { $inc: { kaina: 5 } }  // padidink kainą 5 eurais
)

Ištrynimas (Delete):


// Ištrink vieną
db.produktai.deleteOne({ pavadinimas: "Pelė" })

// Ištrink kelis pagal kriterijų
db.produktai.deleteMany({ sandėlyje: false })

Svarbu žinoti apie MongoDB operatorius. $set, $inc, $push, $pull – tai atnaujinimo operatoriai. $lt, $gt, $in, $regex – paieškos operatoriai. Jų yra daug, ir jie suteikia labai daug lankstumo.

Indeksai: kodėl jie kritiškai svarbūs

Čia daugelis pradedančiųjų daro klaidą – ignoruoja indeksus, kol aplikacija tampa lėta. Indeksai MongoDB veikia panašiai kaip knygos turinys: vietoj to, kad skaitytum visą knygą ieškodamas temos, tiesiog pažiūri į turinį ir eini tiesiai į reikiamą puslapį.

Pagal nutylėjimą MongoDB sukuria indeksą tik laukui _id. Visi kiti laukai, pagal kuriuos dažnai ieškai, turėtų turėti savo indeksus.


// Paprastas indeksas
db.vartotojai.createIndex({ el_pastas: 1 })

// Unikalus indeksas (neleis dublikatų)
db.vartotojai.createIndex({ el_pastas: 1 }, { unique: true })

// Sudėtinis indeksas (keliems laukams)
db.produktai.createIndex({ kategorija: 1, kaina: -1 })

// Teksto paieškos indeksas
db.straipsniai.createIndex({ turinys: "text", pavadinimas: "text" })

// Patikrink, kokie indeksai egzistuoja
db.vartotojai.getIndexes()

// Išanalizuok užklausos veikimą
db.vartotojai.find({ el_pastas: "[email protected]" }).explain("executionStats")

Komanda explain() yra tavo geriausias draugas optimizuojant užklausas. Ji parodo, ar užklausa naudoja indeksą (IXSCAN) ar skaito visus dokumentus (COLLSCAN). Jei matai COLLSCAN dažnai naudojamoje užklausoje – laikas kurti indeksą.

Tačiau indeksai nėra nemokama pietūs. Kiekvienas indeksas užima vietą atmintyje ir sulėtina įrašymo operacijas, nes kiekvieną kartą įterpiant ar atnaujinant dokumentą reikia atnaujinti ir indeksus. Taisyklė paprasta: kurik indeksus tiems laukams, pagal kuriuos dažnai ieškai, bet nekurk indeksų viskam iš eilės.

Agregacijos: kai paprasta paieška nebeužtenka

Agregacijos yra viena galingiausių MongoDB funkcijų, bet ir viena labiausiai ignoruojamų pradedančiųjų. Agregacijos leidžia transformuoti, grupuoti ir analizuoti duomenis tiesiogiai duomenų bazėje – panašiai kaip SQL GROUP BY, JOIN, HAVING ir kiti.

Agregacijos veikia kaip konvejeris (pipeline): duomenys eina per keletą etapų, kiekvienas etapas transformuoja rezultatą.


// Kiek produktų kiekvienoje kategorijoje ir kokia vidutinė kaina
db.produktai.aggregate([
  {
    $group: {
      _id: "$kategorija",
      kiekis: { $sum: 1 },
      vidutine_kaina: { $avg: "$kaina" },
      max_kaina: { $max: "$kaina" }
    }
  },
  {
    $sort: { kiekis: -1 }
  }
])

// Sudėtingesnis pavyzdys: užsakymų analizė
db.uzsakymai.aggregate([
  // Filtruok tik šių metų užsakymus
  {
    $match: {
      data: { $gte: new Date("2024-01-01") }
    }
  },
  // Išskleisk produktų masyvą
  {
    $unwind: "$produktai"
  },
  // Grupuok pagal produktą
  {
    $group: {
      _id: "$produktai.id",
      pavadinimas: { $first: "$produktai.pavadinimas" },
      pardavimai: { $sum: "$produktai.kiekis" },
      pajamos: { $sum: { $multiply: ["$produktai.kaina", "$produktai.kiekis"] } }
    }
  },
  // Rikiuok pagal pajamas
  {
    $sort: { pajamos: -1 }
  },
  // Paimk tik top 10
  {
    $limit: 10
  }
])

Dažniausiai naudojami agregacijos etapai:

  • $match – filtravimas (naudok kuo anksčiau, kad sumažintum duomenų kiekį)
  • $group – grupavimas pagal lauką
  • $project – laukų pasirinkimas ir transformavimas
  • $sort – rikiavimas
  • $limit ir $skip – puslapiavimas
  • $unwind – masyvo išskleidimas į atskirus dokumentus
  • $lookup – jungimas su kita kolekcija (kaip SQL JOIN)

MongoDB su Node.js: praktinis pavyzdys

Teorija yra gerai, bet realybėje MongoDB naudosi per kokią nors programavimo kalbą. Node.js ir MongoDB yra labai populiari kombinacija – abu naudoja JavaScript, abu puikiai tinka realaus laiko aplikacijoms.

Įdiek Mongoose – populiariausią Node.js biblioteką darbui su MongoDB:


npm install mongoose

Mongoose suteikia schemų validaciją, modelius ir daug patogių metodų. Štai paprastas pavyzdys:


const mongoose = require('mongoose');

// Prisijunk prie duomenų bazės
mongoose.connect('mongodb://localhost:27017/mano_projektas');

// Apibrėžk schemą
const vartotojasSchema = new mongoose.Schema({
  vardas: {
    type: String,
    required: true,
    trim: true
  },
  el_pastas: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  amzius: {
    type: Number,
    min: 0,
    max: 150
  },
  sukurta: {
    type: Date,
    default: Date.now
  },
  aktyvus: {
    type: Boolean,
    default: true
  }
});

// Sukurk modelį
const Vartotojas = mongoose.model('Vartotojas', vartotojasSchema);

// Naudok modelį
async function pavyzdys() {
  // Sukurk naują vartotoją
  const naujas = new Vartotojas({
    vardas: "Ona Onaitė",
    el_pastas: "[email protected]",
    amzius: 25
  });
  
  await naujas.save();
  
  // Ieškokime vartotojų
  const aktyvus = await Vartotojas.find({ aktyvus: true })
    .select('vardas el_pastas')
    .sort('vardas')
    .limit(10);
  
  console.log(aktyvus);
  
  // Atnaujink
  await Vartotojas.findOneAndUpdate(
    { el_pastas: "[email protected]" },
    { amzius: 26 },
    { new: true }  // grąžink atnaujintą dokumentą
  );
}

pavyzdys().catch(console.error);

Mongoose schema suteikia tai, ko MongoDB pats savaime neturi – validaciją. Jei bandysi išsaugoti vartotoją be el. pašto arba su neigiamu amžiumi, Mongoose metys klaidą dar prieš siunčiant duomenis į duomenų bazę. Tai labai naudinga.

Kita vertus, jei nori dirbti tiesiogiai su MongoDB driver be Mongoose:


const { MongoClient } = require('mongodb');

async function main() {
  const client = new MongoClient('mongodb://localhost:27017');
  
  try {
    await client.connect();
    
    const db = client.db('mano_projektas');
    const kolekcija = db.collection('vartotojai');
    
    // Įterpk
    const rezultatas = await kolekcija.insertOne({
      vardas: "Tomas Tomaitis",
      el_pastas: "[email protected]"
    });
    
    console.log('Įterptas ID:', rezultatas.insertedId);
    
  } finally {
    await client.close();
  }
}

main();

Tiesioginis driver suteikia daugiau kontrolės ir yra šiek tiek greitesnis, bet Mongoose – patogesnė kasdieniam naudojimui. Mano rekomendacija: pradedantiesiems ir vidutinio dydžio projektams – Mongoose. Dideliems projektams, kur svarbus kiekvienas milisekundė – verta apsvarstyti tiesioginį driver.

Kai MongoDB tampa tikra jėga – ir kur ji neveikia

Prabėjus per pagrindinius MongoDB konceptus, verta pabūti sąžiningais ir pakalbėti apie tai, kas veikia gerai ir kas – ne taip gerai.

MongoDB tikrai šviečia šiose situacijose: turinio valdymo sistemose, kur kiekvienas straipsnis ar produktas gali turėti skirtingą struktūrą; realaus laiko aplikacijose, kur svarbus greitis; IoT projektuose, kur sensoriai siunčia didelius kiekius nestruktūrizuotų duomenų; kataloguose su sudėtingomis hierarchijomis. Taip pat MongoDB Atlas pasiūlo puikias galimybes – pilno teksto paiešką, geografines užklausas, duomenų sinchronizavimą su mobiliosiomis aplikacijomis.

Tačiau yra situacijų, kur MongoDB nėra geriausias pasirinkimas. Jei tavo aplikacija daro daug sudėtingų transakcijų tarp kelių kolekcijų – nors MongoDB 4.0+ palaiko multi-document transactions, jos nėra tokios natūralios kaip reliacinėse duomenų bazėse. Finansų sistemos, kur kiekvienas centas turi būti tiksliai apskaitytas, dažnai geriau veikia su PostgreSQL. Taip pat jei tavo duomenys yra labai struktūrizuoti ir schema nesikeičia – reliacinė duomenų bazė gali būti efektyvesnė.

Praktiniai patarimai, kuriuos verta įsiminti:

  • Dokumentų dydis – MongoDB riboja dokumentą iki 16MB. Jei dokumentas gali augti neribotai (pvz., komentarų sąrašas), geriau saugok komentarus atskiroje kolekcijoje su nuoroda į pagrindinį dokumentą.
  • Įdėtiniai dokumentai vs. nuorodos – jei duomenys visada naudojami kartu, įdėk juos į vieną dokumentą. Jei duomenys naudojami atskirai arba gali būti susieti su daugeliu dokumentų – naudok nuorodas.
  • Reguliariai tikrink indeksus – komanda db.collection.aggregate([{$indexStats: {}}]) parodys, kurie indeksai naudojami ir kurie – ne. Nenaudojami indeksai tik eikvoja resursus.
  • Naudok projekciją – jei reikia tik kelių laukų, nurodyk juos užklausoje. Nereikia traukti viso dokumento, jei naudosi tik vieną lauką.
  • MongoDB Compass – tai oficialus grafinis MongoDB įrankis. Puikiai tinka vizualizuoti duomenis, testuoti užklausas ir analizuoti veikimą.

MongoDB nėra sidabrinė kulka, bet tai tikrai galingas įrankis, kurį verta turėti savo arsenale. Pradėk nuo mažo projekto, eksperimentuok su agregacijomis, suprask, kaip veikia indeksai – ir labai greitai pajusi, kodėl tiek daug kūrėjų jį mėgsta. Technologijų pasaulyje retai būna vienas teisingas atsakymas, bet MongoDB tikrai yra vienas iš tų sprendimų, kuris gali labai palengvinti gyvenimą tinkamame kontekste.