MongoDB Academy · Lektion

Sort, Skip, Limit und Projektionen kombinieren

Sie kombinieren Filter, Projektion, Sortierung und Paginierung zu einer vollständigen Abfragekette für einen realistischen Listenendpunkt.

Lektion 4 von 413 Schritte

Sort, Skip, Limit und Projektionen kombinieren ist eine kostenlose MongoDB Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des MongoDB Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der MongoDB Academy-Kurs umfasst insgesamt 4 Lektionen.

Die vollständige Abfragekette

Eine MongoDB-Listenabfrage in Produktionsqualität kombiniert vier Komponenten: einen Filter zur Auswahl passender Dokumente, eine Projektion zur Auswahl der zurückzugebenden Felder, eine Sortierung zur Anordnung der Ergebnisse und eine Paginierung (skip/limit oder Keyset-Paginierung), um jeweils eine Seite zurückzugeben. Für das Schreiben korrekter und leistungsfähiger Abfragen ist es entscheidend zu verstehen, wie diese Komponenten zusammenwirken und in welcher Reihenfolge MongoDB sie ausführt.

Die interne Ausführungsreihenfolge von MongoDB

Unabhängig davon, in welcher Reihenfolge Sie Methoden im Treiber verketten, führt MongoDB Abfragekomponenten immer in dieser festen Reihenfolge aus: 1) Filter → 2) Sortierung → 3) Skip → 4) Limit. Die Projektion wird angewendet, während Dokumente aus der Storage Engine gelesen werden. Diese Reihenfolge ist wichtig: skip arbeitet mit der sortierten Ergebnismenge, und limit begrenzt die endgültige Ausgabe nach dem Überspringen. Sie können diese Ausführungsreihenfolge nicht ändern.

// These two are identical — order of chaining doesn't matter
db.products.find({}).sort({ price: -1 }).skip(20).limit(10);
db.products.find({}).limit(10).skip(20).sort({ price: -1 });
// MongoDB executes: filter -> sort -> skip -> limit

Einen List-API-Endpunkt erstellen

Fügen wir alle Bestandteile in einem realistischen Listen-Endpunkt zusammen. Das folgende Beispiel ruft Seite 2 aktiver Produkte aus der Kategorie „electronics“ ab, sortiert nach absteigender Bewertung, und gibt nur die für eine Produktkarten-Oberfläche benötigten Felder zurück. Jeder Bestandteil der Kette erfüllt einen bestimmten Zweck.

const filter = { category: 'electronics', isActive: true };
const projection = { _id: 1, name: 1, price: 1, thumbnailUrl: 1, rating: 1 };
const PAGE = 2;
const PAGE_SIZE = 20;

const products = await db.collection('products')
  .find(filter)                     // 1. filter
  .project(projection)              // projection
  .sort({ rating: -1, _id: -1 })   // 2. sort
  .skip((PAGE - 1) * PAGE_SIZE)    // 3. skip
  .limit(PAGE_SIZE)                 // 4. limit
  .toArray();

Der unterstützende Index

Damit die vollständige Abfragekette effizient ist, benötigen Sie einen Index, der Filter und Sortierung abdeckt. Der Filter verwendet category und isActive (Gleichheit), die Sortierung verwendet rating und _id. Der ideale zusammengesetzte Index platziert zuerst die Gleichheitsfelder und danach die Sortierfelder: { category: 1, isActive: 1, rating: -1, _id: -1 }.

// Index supporting the list API query
db.products.createIndex({ category: 1, isActive: 1, rating: -1, _id: -1 });

// Verify: explain should show IXSCAN and no SORT stage
db.products.find({ category: 'electronics', isActive: true })
  .sort({ rating: -1, _id: -1 })
  .skip(20)
  .limit(20)
  .explain('executionStats');

Die vollständige Mongoose-Abfragekette

Die Fluent API von Mongoose macht dieselbe Abfragekette lesbar und typsicher. Verketten Sie .find(), .select(), .sort(), .skip() und .limit(), und rufen Sie anschließend .lean() für ein einfaches POJO-Ergebnis auf (deutlich schneller als vollständige Mongoose-Dokumente, wenn Sie keine Lifecycle-Hooks oder Virtuals benötigen).

const products = await Product
  .find({ category: 'electronics', isActive: true })
  .select({ name: 1, price: 1, thumbnailUrl: 1, rating: 1, _id: 1 })
  .sort({ rating: -1, _id: -1 })
  .skip((PAGE - 1) * PAGE_SIZE)
  .limit(PAGE_SIZE)
  .lean();  // returns plain objects, not Mongoose Documents

console.log('Products on page:', products.length);

Paginierungsmetadaten zurückgeben

Die Antwort einer gut konzipierten Listen-API enthält neben dem Datenarray Paginierungsmetadaten: die aktuelle Seite, die Seitengröße, die Gesamtzahl der Dokumente, die Gesamtzahl der Seiten sowie die Information, ob eine nächste oder vorherige Seite vorhanden ist. So können Clients Paginierungssteuerelemente darstellen, ohne eine separate Count-Abfrage auszuführen. Geben Sie Metadaten und Daten immer gemeinsam in einem Antwortobjekt zurück.

const [total, items] = await Promise.all([
  Product.countDocuments({ category: 'electronics', isActive: true }),
  Product.find({ category: 'electronics', isActive: true })
    .select({ name: 1, price: 1, thumbnailUrl: 1, rating: 1 })
    .sort({ rating: -1 })
    .skip((PAGE - 1) * PAGE_SIZE)
    .limit(PAGE_SIZE)
    .lean()
]);

res.json({
  data:       items,
  page:       PAGE,
  pageSize:   PAGE_SIZE,
  total:      total,
  totalPages: Math.ceil(total / PAGE_SIZE),
  hasNext:    PAGE * PAGE_SIZE < total,
  hasPrev:    PAGE > 1
});

Standardwerte anwenden und Eingaben validieren

Validieren und bereinigen Sie die Query-Parameter page, limit und sort immer, bevor Sie sie in einer MongoDB-Abfrage verwenden. Ungeprüfte Werte können Fehler durch skip(-5), enorme limit(999999)-Aufrufe mit übermäßigem Speicherverbrauch oder Sortier-Injection verursachen, wenn Sie vom Benutzer bereitgestellte Sortierschlüssel direkt weitergeben. Erlaubte Sortierfelder sollten auf einer Whitelist stehen; außerdem sollten Sie sinnvolle Mindest- und Höchstwerte für page und limit erzwingen.

const ALLOWED_SORT_FIELDS = new Set(['rating', 'price', 'createdAt']);

function parseListParams(query) {
  const page  = Math.max(1, parseInt(query.page)  || 1);
  const limit = Math.min(100, Math.max(1, parseInt(query.limit) || 20));
  const sortField = ALLOWED_SORT_FIELDS.has(query.sort) ? query.sort : 'createdAt';
  const sortDir   = query.order === 'asc' ? 1 : -1;
  return { page, limit, sort: { [sortField]: sortDir, _id: sortDir } };
}

Zwischen Offset- und Keyset-Paginierung wechseln

Sie können beide Paginierungsmodi über denselben Endpunkt anbieten, indem Sie prüfen, ob ein cursor-Parameter (Keyset) oder ein page-Parameter (Offset) vorhanden ist. Wenn cursor angegeben ist, wenden Sie den Bereichsfilter an und lassen skip() weg. Wenn nur page angegeben ist, verwenden Sie skip(). So können Sie Clients schrittweise von der Offset- auf die Keyset-Paginierung umstellen, ohne die Abwärtskompatibilität zu beeinträchtigen.

async function listItems(req, res) {
  const limit = 20;
  let query = Product.find({ isActive: true }).sort({ createdAt: -1, _id: -1 }).limit(limit);

  if (req.query.cursor) {
    const { createdAt, _id } = decodeCursor(req.query.cursor);
    query = query.where('$or').equals([
      { createdAt: { $lt: new Date(createdAt) } },
      { createdAt: new Date(createdAt), _id: { $lt: _id } }
    ]);
  } else if (req.query.page) {
    const page = Math.max(1, parseInt(req.query.page) || 1);
    query = query.skip((page - 1) * limit);
  }

  const items = await query.lean();
  res.json({ items, nextCursor: items.length === limit ? encodeCursor(items.at(-1)) : null });
}

Die Abfragekette testen

Testen Sie Ihre vollständige Abfragekette sowohl mit Unit-Tests (Mocking des Treibers) als auch mit Integrationstests (echtes MongoDB über ein In-Memory-mongod oder Testcontainers). Befüllen Sie die Test-Collection mit genügend Dokumenten, um die Korrektheit der Paginierungsgrenzen zu überprüfen: Seite 1 und Seite 2 müssen zusammen genau 2×pageSize unterschiedliche Dokumente ohne Duplikate oder Lücken zurückgeben.

Paginierte Ergebnisse zwischenspeichern

Erwägen Sie bei leseintensiven paginierten APIs, Antworten auf HTTP-Ebene (Redis oder ein CDN) zwischenzuspeichern. Verwenden Sie dabei die vollständige Query-Zeichenfolge einschließlich page, sort und Filtern als Schlüssel. Cache-TTLs von 10–60 Sekunden reduzieren die Datenbanklast bei häufigen Abfragen erheblich. Beachten Sie, dass zwischengespeicherte Antworten leicht veraltete Daten enthalten können – für die meisten Anwendungsfälle akzeptabel, jedoch nicht für Finanz- oder Echtzeit-Dashboards.

Die vollständige Kette mit explain() überprüfen

Führen Sie Ihre vollständige find-Kette mit explain('executionStats') aus, um den gesamten Abfrageplan zu überprüfen. Der ideale Plan zeigt: IXSCAN für den Filter, denselben Index-Scan auch für die Sortierung (keine separate SORT-Phase) und einen Wert von nReturned, der Ihrem limit-Wert entspricht. Jede unerwartete COLLSCAN- oder SORT-Phase weist auf einen fehlenden oder ineffektiven Index hin.

const stats = await db.collection('products')
  .find({ category: 'electronics', isActive: true })
  .sort({ rating: -1, _id: -1 })
  .skip(20)
  .limit(20)
  .explain('executionStats');

const stage = stats.executionStats.executionStages;
console.log('Stage:', stage.stage);        // should be LIMIT
console.log('Docs examined:', stats.executionStats.totalDocsExamined);
console.log('Docs returned:', stats.executionStats.nReturned);

Kurze Überprüfung

Testen Sie Ihr Verständnis der Konzepte zu MongoDB & NoSQL-Datenbanken aus dieser Lektion.

Zusammenfassung der Lektion

In dieser Lektion haben Sie gelernt: MongoDB führt Filter → Sortierung → Skip → Limit unabhängig von der Reihenfolge der Methodenverkettung immer in dieser Reihenfolge aus, der unterstützende Index sollte zuerst die Gleichheitsfelder und danach die Sortierfelder enthalten, und Paginierungsmetadaten (total, hasNext, hasPrev) sollten gemeinsam mit dem Datenarray zurückgegeben werden. Als Nächstes beschäftigen wir uns mit dem Importieren und Exportieren von Daten mithilfe von mongoimport, mongoexport und Seed-Skripten.

Kostenlos starten

Lerne JavaScript mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
30
Lektionen
120

Häufig gestellte Fragen

Ist die Lektion „Sort, Skip, Limit und Projektionen kombinieren“ kostenlos?

Ja — der vollständige Text von „Sort, Skip, Limit und Projektionen kombinieren“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des MongoDB Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der MongoDB Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Sort, Skip, Limit und Projektionen kombinieren“?

Sie kombinieren Filter, Projektion, Sortierung und Paginierung zu einer vollständigen Abfragekette für einen realistischen Listenendpunkt. Du übst MongoDB Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um MongoDB Academy zu starten?

Keine Vorkenntnisse erforderlich. MongoDB Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Sort, Skip, Limit und Projektionen kombinieren“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser MongoDB Academy-Lektion Code schreiben und ausführen?

Ja. Jede MongoDB Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Sortieren mit sort() und mehreren Schlüsseln
  2. Skip und Limit: Offset-Paginierung
  3. Keyset-Paginierung mit Bereichsabfragen
  4. Sort, Skip, Limit und Projektionen kombinieren
← Zurück zu MongoDB Academy