MongoDB Academy · Oppitunti

Mongoose-kyselyt, ketjutus ja lean-dokumentit

Oppijat ketjuttavat Mongoose-kyselyapuja, käyttävät .lean()-funktiota suorituskykyisten raakojen POJO-objektien palauttamiseen ja vertaavat kyselyrajapintaa natiiviin ajuriin.

Oppitunti 3/413 vaihetta

Mongoose-kyselyt, ketjutus ja lean-dokumentit on ilmainen MongoDB Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu MongoDB Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. MongoDB Academy-kurssilla on yhteensä 4 oppituntia.

Mongoose-kyselyobjektit

Kun kutsutte Mongoose-kyselymetodia, kuten User.find(), se palauttaa Query-objektin Promisen sijaan. Tämä Query-objekti on laiska — sitä ei suoriteta, ennen kuin kutsutte sitä nimenomaisesti .then()-, await- tai .exec()-kutsulla. Ennen suoritusta voitte ketjuttaa siihen lisää kyselymuokkaimia ja muodostaa koko kyselyn. Tämä ketjutettava API on yksi Mongoose-ohjelmiston ergonomisimmista ominaisuuksista.

const User = require('./models/user');

// This does NOT execute immediately — returns a Query object
const query = User.find({ active: true });

// Now execute it with await
const users = await query;

// Or chain modifiers before executing:
const result = await User.find({ active: true })
  .sort({ createdAt: -1 })
  .limit(10)
  .select('name email -_id');
  // select() projects fields: '+field' includes, '-field' excludes

Kyselymuokkainten ketjuttaminen

Mongoose-kyselymuokkaimia, kuten .sort(), .limit(), .skip(), .select() ja .populate(), voidaan ketjuttaa missä järjestyksessä tahansa ennen suoritusta. Taustalla oleva Query-objekti kerää kaikki muokkaimet ja lähettää MongoDB:lle yhden optimoidun kyselyn. Toiminnallisesti tämä vastaa asetusten välittämistä natiivin ajurin find(filter, options)-metodille, mutta lukutapa on luontevampi sujuvana rakennusketjuna.

const orders = await Order
  .find({ status: 'completed', userId: currentUserId })
  .sort({ createdAt: -1 })              // newest first
  .skip(page * pageSize)                // pagination offset
  .limit(pageSize)                      // page size
  .select('_id total status createdAt') // projection
  .lean();                              // return plain objects (discussed next)

console.log('Orders on this page:', orders.length);

.lean()-metodi: raakojen POJO-objektien suorituskyky

Oletusarvoisesti Mongoose käärii jokaisen kyselyn palauttaman dokumentin Mongoose Document -instanssiksi — kyseessä on raskas objekti, joka sisältää muutosten seurannan, metodit, virtuaaliset ominaisuudet ja middleware-käsittelijät. .lean()-metodi käskee Mongoosea palauttamaan niiden sijaan tavallisia JavaScript-objekteja (POJO-objekteja). Lean-kyselyt ovat yleensä 2–5 kertaa nopeampia ja käyttävät vähemmän muistia, koska Mongoose ohittaa Document-objektien luonnin. Käyttäkää .lean()-metodia vain lukuun tarkoitetuissa toiminnoissa, joissa ette tarvitse dokumenttimetodeja tai tallennus- ja päivityskäsittelijöitä.

// Without .lean() — heavy Mongoose Document objects
const docsWithMethods = await User.find({ active: true });
// docsWithMethods[0].save() works, but incurs overhead

// With .lean() — plain JavaScript objects, much faster
const pureObjects = await User.find({ active: true }).lean();
// pureObjects[0].save() does NOT work — it's a plain object
// But JSON.stringify, spread operators, and array methods are all faster

console.log(typeof docsWithMethods[0].save); // 'function'
console.log(typeof pureObjects[0].save);     // 'undefined'

Milloin kannattaa käyttää .lean()-metodia ja milloin kokonaisia dokumentteja

Käyttäkää .lean()-metodia, kun luette vain tietoja (GET-päätepisteet), tarvitsette nopean JSON-sarjallistamisen tai käsittelette suurta määrää dokumentteja eräajona. Älkää käyttäkö .lean()-metodia, kun tarvitsette tulokselle .save()-metodia, virtuaalisia ominaisuuksia, dokumenttimiddlewareä tai instanssimetodeja. Hyvä nyrkkisääntö on: API-lukulistat → lean, muutosprosessit → täydet Mongoose-dokumentit.

// API read endpoint — use .lean() for speed
router.get('/products', async (req, res) => {
  const products = await Product.find({}).lean(); // fastest, no doc wrapper
  res.json(products);
});

// Update endpoint — use full Mongoose document to access instance methods
router.post('/users/:id/deactivate', async (req, res) => {
  const user = await User.findById(req.params.id); // full document, NO .lean()
  await user.sendDeactivationEmail(); // instance method won't work with .lean()
  user.active = false;
  await user.save(); // document method won't work with .lean()
  res.json({ success: true });
});

findById- ja findOne-apumetodit

Mongoose lisää kyselyihin apumetodeja, joita natiivissa ajurissa ei ole. Model.findById(id) vastaa kutsua Model.findOne({ _id: id }) ja muuntaa merkkijonomuotoiset tunnisteet automaattisesti ObjectId-arvoiksi. Model.findByIdAndUpdate(id, update, options) ja Model.findByIdAndDelete(id) yhdistävät haun ja muokkauksen yhdeksi atomiseksi operaatioksi. Nämä metodit vähentävät huomattavasti CRUD-reittikäsittelijöiden toisteista koodia.

// findById — automatic ObjectId conversion from string
const user = await User.findById('64a1b2c3d4e5f6789012345a').lean();

// findByIdAndUpdate — find, update, and return result atomically
const updatedProduct = await Product.findByIdAndUpdate(
  productId,
  { $set: { price: 199.99 }, $inc: { updateCount: 1 } },
  { new: true, runValidators: true }  // return new doc, run validators
);

// findByIdAndDelete — find and delete atomically
const deletedUser = await User.findByIdAndDelete(userId);
console.log('Deleted:', deletedUser ? deletedUser.email : 'not found');

Dokumenttien laskeminen

Mongoose tarjoaa tehokkaat metodit dokumenttien laskemiseen. Model.countDocuments(filter) käyttää suodatinta ja laskee vastaavat dokumentit — se käy läpi vastaavat dokumentit ja hyödyntää indeksejä. Model.estimatedDocumentCount() käyttää kokoelman metatietoja ja antaa likimääräisen mutta välittömän lukumäärän ilman suodatinta. Se sopii suurten kokoelmien koontilukuihin esimerkiksi hallintapaneelissa, kun tarkan lukumäärän saaminen ei ole välttämätöntä.

// Exact count with filter — uses an index if available
const activeUsers = await User.countDocuments({ active: true, role: 'user' });
console.log('Active users:', activeUsers);

// Fast approximate count — no filter, uses collection stats
const totalProducts = await Product.estimatedDocumentCount();
console.log('Approximate total products:', totalProducts);

// In Express pagination:
const [data, total] = await Promise.all([
  User.find({}).skip(offset).limit(pageSize).lean(),
  User.countDocuments({})
]);
res.json({ data, total, pages: Math.ceil(total / pageSize) });

Populate: viitteiden ratkaiseminen

.populate() on yksi Mongoose-ohjelmiston tehokkaimmista ominaisuuksista — se korvaa ObjectId-viitekentän toisesta kokoelmasta haetulla varsinaisella viitatulla dokumentilla. Mongoose suorittaa taustalla toisen kyselyn viitattuun kokoelmaan ja korvaa tunnisteet tuloksilla. Tämä vastaa aggregointiputken $lookup-vaihetta, mutta tarjoaa yksinkertaisemman API:n.

const Order = mongoose.model('Order', new mongoose.Schema({
  userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  productIds: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Product' }]
}));

// Populate the userId reference with the full User document
const order = await Order
  .findById(orderId)
  .populate('userId', 'name email')    // only select name and email from User
  .populate('productIds', 'name price') // populate array of references
  .lean();

console.log(order.userId.email);      // 'alice@example.com'
console.log(order.productIds[0].name); // 'Laptop'

Mongoose vai natiivi ajuri: milloin valita kumpi

Mongoose lisää validoinnin, populaten, middlewaren ja kätevän kysely-API:n, mutta aiheuttaa jonkin verran lisäkustannuksia. Valitkaa Mongoose, kun sovelluksellanne on tarkasti määritellyt ja vakaat skeemat, haluatte skeemavalidoinnin ilman JSON Schema -validoijia, tarvitsette populaten viitteiden ratkaisemiseen tai rakennatte tavanomaista REST-APIa. Valitkaa natiivi ajuri, kun tarvitsette parasta mahdollista suorituskykyä, käsittelette dynaamisia skeemoja, rakennatte aggregaatioihin painottuvaa analytiikkaa tai kirjoitatte vähäriippuvuuksista mikropalvelua.

// Mongoose: ergonomic, validates, populate works
const user = await User.findOne({ email }).select('-password').populate('profile');

// Native driver: faster, raw, no middleware
const user = await db.collection('users')
  .findOne({ email }, { projection: { password: 0 } });

exec() ja virheenkäsittely

.exec()-metodin nimenomainen kutsuminen muuntaa Mongoose Query -objektin Promiseksi. Tämä on perinteinen tapa suorittaa kyselyitä käytettäessä .catch()-Promise-ketjutusta. async/await-syntaksilla voitte jättää .exec()-metodin pois — pelkkä await User.find({}) toimii hyvin. Jotkut kehittäjät suosivat silti .exec()-metodia selkeyden vuoksi tai rakentaessaan kyselyobjekteja ohjelmallisesti. Molemmat tavat tuottavat saman tuloksen.

// With .exec() — explicit Promise conversion
const user = await User.findOne({ email }).exec();

// Without .exec() — implicit execution via await
const user = await User.findOne({ email });

// Error handling with try/catch (both forms work the same)
try {
  const user = await User.findById(id);
  if (!user) throw new Error('User not found');
} catch (err) {
  if (err.name === 'CastError') {
    res.status(400).json({ error: 'Invalid ID format' });
  } else {
    res.status(500).json({ error: err.message });
  }
}

Kyselyn rakennusmalli

Koska Mongoose-kyselyt ovat laiskoja, voitte rakentaa kyselyitä ehdollisesti erillisissä lausekkeissa ennen suoritusta. Tämä on hyödyllistä, kun kyselyparametrit ovat valinnaisia — voitte lisätä järjestyksen tai suodattimia vain, jos parametri on annettu. Tämä malli on paljon siistimpi kuin dynaamisten kyselymerkkijonojen muodostaminen, ja se pitää koodin luettavana.

async function searchProducts(filters) {
  let query = Product.find();

  if (filters.category) {
    query = query.where('category').equals(filters.category);
  }
  if (filters.maxPrice) {
    query = query.where('price').lte(filters.maxPrice);
  }
  if (filters.inStock) {
    query = query.where('stock').gt(0);
  }

  const sortField = filters.sortBy || 'createdAt';
  query = query.sort({ [sortField]: -1 }).limit(50).lean();

  return query; // executes here via await in the caller
}

Aggregointiputki Mongoose-ohjelmistossa

Mongoose-mallit tukevat myös aggregointiputkea kutsulla Model.aggregate(pipeline). Tavallisista Mongoose-kyselyistä poiketen aggregointi ohittaa skeeman tyyppimuunnokset, Mongoose-middlewarekäsittelijät ja populaten — se toimii samalla tavoin kuin natiivin ajurin aggregaten suora kutsuminen. Aggregate palauttaa tavallisen objektitaulukon, ei koskaan Mongoose-dokumentteja. Käyttäkää Model.aggregate()-metodia monimutkaiseen analytiikkaan ja raportointiin, joissa Mongoose-abstraktioista ei ole hyötyä.

// Aggregation in Mongoose — bypasses Mongoose middleware and casting
const salesByRegion = await Order.aggregate([
  { $match: { status: 'completed' } },
  {
    $group: {
      _id: '$region',
      totalRevenue: { $sum: '$total' },
      orderCount: { $sum: 1 },
      avgOrder: { $avg: '$total' }
    }
  },
  { $sort: { totalRevenue: -1 } }
]);

// salesByRegion is a plain array — no Mongoose Document wrapper

Pikatarkistus

Testaa, miten hyvin ymmärrät tämän oppitunnin MongoDB- ja NoSQL-tietokantoja koskevat käsitteet.

Oppitunnin yhteenveto

Tässä oppitunnissa opit, että Mongoose-kyselymetodit palauttavat laiskoja Query-objekteja, joita voidaan ketjuttaa .sort()-, .limit()-, .skip()-, .select()- ja .populate()-metodeilla ennen suoritusta, .lean() palauttaa tavallisia JavaScript-objekteja ja parantaa suorituskykyä vain lukuun tarkoitetuissa toiminnoissa ja Model.aggregate() ohittaa Mongoose-abstraktiot ja toimii analytiikan aggregointiputkissa natiivin ajurin tavoin. Seuraavaksi tutustumme Mongoose-middlewareen eli pre- ja post-käsittelijöihin, joilla voidaan lisätä mukautettua logiikkaa save-, find- ja muiden operaatioiden ympärille.

Aloita maksutta

Opi JavaScript tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”Mongoose-kyselyt, ketjutus ja lean-dokumentit” ilmainen?

Kyllä – oppitunnin ”Mongoose-kyselyt, ketjutus ja lean-dokumentit” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko MongoDB Academy-kurssin, päivitä CoddyKit PROhon. MongoDB Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Mongoose-kyselyt, ketjutus ja lean-dokumentit”?

Oppijat ketjuttavat Mongoose-kyselyapuja, käyttävät .lean()-funktiota suorituskykyisten raakojen POJO-objektien palauttamiseen ja vertaavat kyselyrajapintaa natiiviin ajuriin. Harjoittelet MongoDB Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni MongoDB Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin MongoDB Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Mongoose-kyselyt, ketjutus ja lean-dokumentit”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä MongoDB Academy-oppitunnilla?

Kyllä. Jokainen MongoDB Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Yhdistäminen virallisella Node.js-ajurilla
  2. Mongoose-skeemat, mallit ja virtuaaliominaisuudet
  3. Mongoose-kyselyt, ketjutus ja lean-dokumentit
  4. Mongoose-väliohjelmisto: pre- ja post-koukut
← Takaisin: MongoDB Academy