Zaawansowane Aspekty Mongoose: agregacja i indeksy

Mongoose oferuje zaawansowane możliwości, które znacznie rozszerzają możliwości MongoDB. Dziś przyjrzymy się dwóm potężnym funkcjom: agregacji oraz indeksom. Agregacja pozwala na złożone przetwarzanie danych, a indeksy umożliwiają optymalizację zapytań i poprawę wydajności.

Agregacja: Używanie pipeline’u agregacyjnego

Agregacja to funkcja MongoDB umożliwiająca przetwarzanie i analizowanie danych poprzez stosowanie sekwencji operacji (pipeline’u). Agregacje są idealne, gdy potrzebujemy bardziej zaawansowanych analiz niż proste zapytania .find().

Podstawy pipeline’u agregacyjnego

Pipeline agregacyjny składa się z różnych etapów (stages), z których każdy wykonuje określoną operację na danych. Oto najpopularniejsze operacje:

  • $match: Filtruje dokumenty na podstawie kryteriów.
  • $group: Grupuje dokumenty na podstawie określonych pól i umożliwia agregowanie wartości.
  • $sort: Sortuje wyniki.
  • $project: Wybiera lub modyfikuje pola w wynikach.

Przykład agregacji: Analiza zamówień

Załóżmy, że mamy kolekcję Order, która przechowuje zamówienia z polami customerId, amount i status. Chcemy znaleźć całkowitą kwotę zamówień dla każdego klienta, który ma zamówienia zrealizowane (status: "completed").

Schemat dla zamówień

				
					const mongoose = require('mongoose');

const orderSchema = new mongoose.Schema({
  customerId: mongoose.Schema.Types.ObjectId,
  amount: Number,
  status: String,
  date: Date
});

const Order = mongoose.model('Order', orderSchema);

				
			

Pipeline agregacyjny

Poniższy przykład wykorzystuje pipeline agregacyjny do uzyskania sumy zamówień dla każdego klienta o statusie „completed”:

				
					Order.aggregate([
  { $match: { status: 'completed' } },  // Filtruj zamówienia zrealizowane
  { $group: { _id: '$customerId', totalAmount: { $sum: '$amount' } } },  // Grupuj po customerId i sumuj amount
  { $sort: { totalAmount: -1 } }  // Sortuj po sumie zamówień malejąco
])
  .then(results => console.log('Suma zamówień dla klientów:', results))
  .catch(error => console.log('Błąd agregacji:', error));

				
			

Wyjaśnienie pipeline’u:

  1. $match: Filtruje dokumenty, pozostawiając tylko zamówienia o statusie „completed”.
  2. $group: Grupuje dokumenty na podstawie customerId, licząc sumę wartości amount dla każdego klienta.
  3. $sort: Sortuje wyniki malejąco po totalAmount.

Dzięki agregacji możemy analizować dane w bardzo zaawansowany sposób, co jest niezwykle przydatne przy raportach i statystykach.

Indeksy: Tworzenie i zarządzanie indeksami

Indeksy w MongoDB są strukturami danych, które poprawiają wydajność zapytań. Domyślnie MongoDB tworzy indeks _id dla każdego dokumentu, ale możemy tworzyć dodatkowe indeksy na innych polach, aby zoptymalizować wyszukiwanie.

Tworzenie indeksów w Mongoose

W Mongoose możemy dodać indeks bezpośrednio do schematu, używając opcji index. Załóżmy, że w schemacie Order chcemy utworzyć indeks na polach customerId i date dla szybszego wyszukiwania zamówień według klienta i daty.

Dodanie indeksu do schematu

				
					const orderSchema = new mongoose.Schema({
  customerId: mongoose.Schema.Types.ObjectId,
  amount: Number,
  status: String,
  date: Date
});

// Dodaj indeks na customerId i date
orderSchema.index({ customerId: 1, date: -1 });  // 1 oznacza indeks rosnący, -1 malejący
const Order = mongoose.model('Order', orderSchema);

				
			

Powyższy kod tworzy indeks kompozytowy (na wielu polach), który pozwala na szybsze wyszukiwanie dokumentów według customerId i date.

Rodzaje indeksów

MongoDB oferuje różne typy indeksów, które można tworzyć za pomocą Mongoose:

  • Indeks jednopolowy: Indeks na jednym polu, np. { email: 1 }.
  • Indeks kompozytowy: Indeks na wielu polach, np. { customerId: 1, date: -1 }.
  • Indeks unikalny: Umożliwia uniknięcie duplikatów, np. { email: 1, unique: true }.
  • Indeks tekstowy: Umożliwia pełnotekstowe wyszukiwanie, np. { content: 'text' }.

Przykład indeksu unikalnego

Załóżmy, że chcemy, aby pole email w kolekcji User było unikalne. Możemy dodać indeks unikalny:

				
					const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, unique: true },  // Indeks unikalny na polu email
  age: Number
});

const User = mongoose.model('User', userSchema);

				
			

Dzięki indeksowi unikalnemu MongoDB automatycznie zapobiegnie dodaniu dwóch dokumentów z takim samym adresem e-mail.

Zarządzanie indeksami

MongoDB automatycznie buduje indeksy, gdy są definiowane w schemacie, ale Mongoose oferuje także dodatkowe metody do zarządzania nimi.

Sprawdzanie indeksów

Aby sprawdzić, jakie indeksy są utworzone dla danej kolekcji, możemy użyć:

				
					User.collection.getIndexes()
  .then(indexes => console.log('Indeksy kolekcji User:', indexes))
  .catch(error => console.log('Błąd sprawdzania indeksów:', error));

				
			

Usuwanie indeksów

Aby usunąć wszystkie indeksy w kolekcji, możemy skorzystać z:

				
					User.collection.dropIndexes()
  .then(() => console.log('Wszystkie indeksy zostały usunięte'))
  .catch(error => console.log('Błąd usuwania indeksów:', error));

				
			

Możemy również usunąć konkretny indeks:

				
					User.collection.dropIndex('email_1')
  .then(() => console.log('Indeks email został usunięty'))
  .catch(error => console.log('Błąd usuwania indeksu:', error));

				
			

Kiedy używać indeksów?

Indeksy są bardzo przydatne, gdy:

  • Często wyszukujemy dane po konkretnym polu (np. email lub customerId).
  • Wyszukujemy dane na podstawie wielu pól (np. customerId i date).
  • Chcemy zapobiec duplikatom, używając indeksów unikalnych.

Należy jednak pamiętać, że nadmiar indeksów może zwiększyć wymagania pamięciowe i wydłużyć czas zapisu dokumentów. Warto tworzyć indeksy tylko na tych polach, które rzeczywiście przyspieszą zapytania.

Podsumowanie

Dzięki zaawansowanym operacjom Mongoose, takim jak agregacja i indeksowanie, możemy znacznie zwiększyć wydajność i elastyczność aplikacji:

  • Agregacja pozwala na złożone przetwarzanie danych, idealne do raportów i analiz.
  • Indeksy umożliwiają optymalizację zapytań, co znacząco przyspiesza dostęp do często wyszukiwanych danych.

Stosowanie tych funkcji pozwala na lepsze zarządzanie danymi i poprawia wydajność aplikacji w MongoDB. Agregacje są świetne do zaawansowanej analizy, natomiast indeksy są niezbędne dla aplikacji o wysokiej dostępności i dużym obciążeniu zapytań.