MongoDB - Pytania Rekrutacyjne dla Backend Developera [2026]
MongoDB to najpopularniejsza dokumentowa baza danych NoSQL. Jeśli przygotowujesz się do rozmowy na stanowisko Backend Developer, ten przewodnik zawiera 44 pytania rekrutacyjne z odpowiedziami - od CRUD po agregacje i modelowanie danych.
Spis treści
- Podstawy MongoDB
- Operacje CRUD
- Indeksy
- Aggregation Pipeline
- Modelowanie danych
- Replikacja i Sharding
- MongoDB z Node.js
- Zobacz też
Podstawy MongoDB
Czym jest MongoDB i czym różni się od relacyjnych baz danych?
Odpowiedź w 30 sekund: MongoDB to dokumentowa baza danych NoSQL przechowująca dane w elastycznych dokumentach JSON-like (BSON). W przeciwieństwie do SQL nie wymaga sztywnego schematu, obsługuje zagnieżdżone struktury danych i skaluje się horyzontalnie przez sharding.
Odpowiedź w 2 minuty:
Porównanie MongoDB z relacyjnymi bazami danych przedstawia następująca tabela:
| Cecha | MongoDB (NoSQL) | SQL (relacyjne) |
|---|---|---|
| Model danych | Dokumenty (BSON) | Tabele i wiersze |
| Schemat | Elastyczny | Sztywny |
| Relacje | Embedding/Referencing | JOIN |
| Skalowanie | Horyzontalne (sharding) | Wertykalne |
| Transakcje | Od v4.0 (multi-doc) | Pełne ACID |
| Zapytania | MQL (MongoDB Query) | SQL |
Kiedy MongoDB:
- Dane mają zmienną strukturę
- Potrzeba szybkiego prototypowania
- Duże ilości danych do zapisu
- Dane hierarchiczne/zagnieżdżone
Kiedy SQL:
- Złożone relacje między danymi
- Transakcje krytyczne (finanse)
- Raportowanie i analityka
- Dane wymagają spójności
// Dokument MongoDB
{
_id: ObjectId("507f1f77bcf86cd799439011"),
name: "Jan Kowalski",
email: "jan@example.com",
address: {
city: "Warszawa",
street: "Marszałkowska 1"
},
orders: [
{ product: "Laptop", price: 3000 },
{ product: "Mouse", price: 50 }
]
}
Czym jest BSON i czym różni się od JSON?
Odpowiedź w 30 sekund: BSON (Binary JSON) to binarny format serializacji używany przez MongoDB. Rozszerza JSON o dodatkowe typy danych (Date, ObjectId, Binary, Decimal128) i jest zoptymalizowany pod kątem szybkości parsowania i rozmiaru. JSON jest tekstowy, BSON binarny.
Odpowiedź w 2 minuty:
Główne różnice między JSON a BSON przedstawia poniższa tabela:
| Cecha | JSON | BSON |
|---|---|---|
| Format | Tekstowy | Binarny |
| Typy danych | 6 (string, number, bool, null, array, object) | 20+ (+ Date, ObjectId, Binary, itp.) |
| Parsowanie | Wolniejsze | Szybsze |
| Rozmiar | Mniejszy dla prostych danych | Może być większy (metadata) |
Dodatkowe typy BSON:
{
_id: ObjectId("507f1f77bcf86cd799439011"), // 12-byte unique ID
createdAt: ISODate("2024-01-15T10:30:00Z"), // Date
price: NumberDecimal("19.99"), // Decimal128
data: BinData(0, "base64data"), // Binary
count: NumberLong(9999999999) // 64-bit integer
}
MongoDB automatycznie konwertuje JSON → BSON przy zapisie i BSON → JSON przy odczycie.
Operacje CRUD
Jak wstawiać i wyszukiwać dokumenty w MongoDB?
Odpowiedź w 30 sekund:
Insert: insertOne() dla jednego dokumentu, insertMany() dla wielu. Find: find() zwraca kursor, findOne() jeden dokument. Filtrowanie przez obiekt query z operatorami ($eq, $gt, $in, $and, $or).
Odpowiedź w 2 minuty:
Podstawowe operacje wstawiania i wyszukiwania dokumentów przedstawia poniższy kod:
// === INSERT ===
// Jeden dokument
db.users.insertOne({
name: "Jan Kowalski",
email: "jan@example.com",
age: 30
});
// Wiele dokumentów
db.users.insertMany([
{ name: "Anna", email: "anna@example.com", age: 25 },
{ name: "Piotr", email: "piotr@example.com", age: 35 }
]);
// === FIND ===
// Wszystkie dokumenty
db.users.find();
// Z filtrem
db.users.find({ age: 30 });
// Jeden dokument
db.users.findOne({ email: "jan@example.com" });
// === OPERATORY PORÓWNANIA ===
db.users.find({ age: { $gt: 25 } }); // age > 25
db.users.find({ age: { $gte: 25, $lte: 35 } }); // 25 <= age <= 35
db.users.find({ name: { $in: ["Jan", "Anna"] } }); // name IN [...]
db.users.find({ email: { $ne: null } }); // email != null
// === OPERATORY LOGICZNE ===
db.users.find({
$and: [
{ age: { $gte: 25 } },
{ email: { $exists: true } }
]
});
db.users.find({
$or: [
{ age: { $lt: 20 } },
{ age: { $gt: 60 } }
]
});
// === PROJEKCJA (wybór pól) ===
db.users.find(
{ age: { $gt: 25 } },
{ name: 1, email: 1, _id: 0 } // Tylko name i email
);
// === SORTOWANIE I PAGINACJA ===
db.users.find()
.sort({ age: -1 }) // Malejąco
.skip(10) // Pomiń 10
.limit(5); // Zwróć 5
Jak aktualizować i usuwać dokumenty?
Odpowiedź w 30 sekund:
Update: updateOne(), updateMany() z operatorami $set, $unset, $inc, $push, $pull. Delete: deleteOne(), deleteMany(). Opcja upsert: true tworzy dokument jeśli nie istnieje.
Odpowiedź w 2 minuty:
Operacje aktualizacji i usuwania dokumentów z użyciem różnych operatorów:
// === UPDATE ===
// Aktualizuj jedno pole
db.users.updateOne(
{ email: "jan@example.com" },
{ $set: { age: 31 } }
);
// Wiele pól
db.users.updateOne(
{ _id: ObjectId("...") },
{
$set: { name: "Jan Nowak", status: "active" },
$inc: { loginCount: 1 },
$currentDate: { lastLogin: true }
}
);
// Aktualizuj wiele dokumentów
db.users.updateMany(
{ status: "inactive" },
{ $set: { archived: true } }
);
// === OPERATORY TABLICOWE ===
// Dodaj element do tablicy
db.users.updateOne(
{ _id: ObjectId("...") },
{ $push: { tags: "premium" } }
);
// Dodaj wiele elementów
db.users.updateOne(
{ _id: ObjectId("...") },
{ $push: { tags: { $each: ["vip", "verified"] } } }
);
// Usuń element z tablicy
db.users.updateOne(
{ _id: ObjectId("...") },
{ $pull: { tags: "inactive" } }
);
// === UPSERT ===
db.users.updateOne(
{ email: "new@example.com" },
{ $set: { name: "New User", createdAt: new Date() } },
{ upsert: true } // Utwórz jeśli nie istnieje
);
// === DELETE ===
db.users.deleteOne({ email: "jan@example.com" });
db.users.deleteMany({ status: "deleted" });
// Usuń wszystkie
db.users.deleteMany({});
Indeksy
Czym są indeksy w MongoDB i jak je tworzyć?
Odpowiedź w 30 sekund:
Indeksy to struktury danych przyspieszające wyszukiwanie. Bez indeksu MongoDB skanuje całą kolekcję. Tworzy się je przez createIndex(). Typy: single field, compound, multikey (tablice), text, geospatial. Indeksy przyspieszają odczyt kosztem wolniejszego zapisu.
Odpowiedź w 2 minuty:
Przykłady tworzenia różnych typów indeksów oraz narzędzia do analizy wydajności zapytań:
// === TWORZENIE INDEKSÓW ===
// Single field index
db.users.createIndex({ email: 1 }); // 1 = ascending, -1 = descending
// Unique index
db.users.createIndex({ email: 1 }, { unique: true });
// Compound index (złożony)
db.users.createIndex({ lastName: 1, firstName: 1 });
// Text index (wyszukiwanie pełnotekstowe)
db.articles.createIndex({ title: "text", content: "text" });
// TTL index (auto-usuwanie)
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 } // Usuń po 1h
);
// Partial index (tylko pasujące dokumenty)
db.users.createIndex(
{ email: 1 },
{ partialFilterExpression: { status: "active" } }
);
// === ANALIZA WYDAJNOŚCI ===
db.users.find({ email: "jan@example.com" }).explain("executionStats");
// Sprawdź czy używa indeksu
// "stage": "IXSCAN" = używa indeksu
// "stage": "COLLSCAN" = skan całej kolekcji (źle!)
// === ZARZĄDZANIE INDEKSAMI ===
db.users.getIndexes(); // Lista indeksów
db.users.dropIndex("email_1"); // Usuń indeks
db.users.dropIndexes(); // Usuń wszystkie (poza _id)
Kiedy tworzyć indeksy:
- Pola często używane w zapytaniach (find, sort)
- Pola w warunkach WHERE
- Pola używane do JOIN ($lookup)
Kiedy unikać:
- Kolekcje z częstymi zapisami
- Pola o niskiej selektywności (np. boolean)
- Małe kolekcje (< 1000 dokumentów)
Aggregation Pipeline
Czym jest Aggregation Pipeline i jak go używać?
Odpowiedź w 30 sekund:
Aggregation Pipeline to framework do przetwarzania danych przez sekwencję etapów. Każdy etap ($match, $group, $project, $sort, $lookup) transformuje dokumenty i przekazuje wynik do następnego. Odpowiednik GROUP BY, JOIN i subqueries z SQL.
Odpowiedź w 2 minuty:
Agregacja danych przez sekwencję etapów - od filtrowania przez grupowanie po sortowanie i łączenie kolekcji:
// === PODSTAWOWA AGREGACJA ===
db.orders.aggregate([
// 1. Filtruj (jak WHERE)
{ $match: { status: "completed" } },
// 2. Grupuj (jak GROUP BY)
{
$group: {
_id: "$customerId",
totalOrders: { $sum: 1 },
totalSpent: { $sum: "$amount" },
avgOrder: { $avg: "$amount" }
}
},
// 3. Filtruj grupy (jak HAVING)
{ $match: { totalSpent: { $gt: 1000 } } },
// 4. Sortuj
{ $sort: { totalSpent: -1 } },
// 5. Limit
{ $limit: 10 }
]);
// === $LOOKUP (JOIN) ===
db.orders.aggregate([
{
$lookup: {
from: "customers", // Kolekcja do połączenia
localField: "customerId", // Pole w orders
foreignField: "_id", // Pole w customers
as: "customer" // Nazwa wyniku
}
},
{ $unwind: "$customer" }, // Rozpakuj tablicę
{
$project: {
orderNumber: 1,
amount: 1,
customerName: "$customer.name"
}
}
]);
// === $UNWIND (rozwijanie tablic) ===
// Dokument: { name: "Jan", tags: ["js", "node", "mongo"] }
db.users.aggregate([
{ $unwind: "$tags" },
{ $group: { _id: "$tags", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
]);
// Wynik: najpopularniejsze tagi
// === $PROJECT (transformacja) ===
db.users.aggregate([
{
$project: {
fullName: { $concat: ["$firstName", " ", "$lastName"] },
yearOfBirth: { $year: "$birthDate" },
isAdult: { $gte: ["$age", 18] }
}
}
]);
Modelowanie danych
Kiedy używać embedding a kiedy referencing?
Odpowiedź w 30 sekund: Embedding (zagnieżdżanie): gdy dane są często odczytywane razem, relacja 1:few, dane rzadko się zmieniają. Referencing (referencje): gdy dokumenty są duże, relacja 1:many lub many:many, dane często aktualizowane niezależnie.
Odpowiedź w 2 minuty:
Porównanie dwóch podstawowych strategii modelowania relacji w MongoDB:
// === EMBEDDING (zagnieżdżanie) ===
// Dobre dla: 1:few, dane czytane razem
{
_id: ObjectId("..."),
name: "Jan Kowalski",
addresses: [
{ type: "home", city: "Warszawa", street: "..." },
{ type: "work", city: "Kraków", street: "..." }
]
}
// ✅ Jeden odczyt = wszystkie dane
// ❌ Limit 16MB na dokument
// ❌ Duplikacja przy many-to-many
// === REFERENCING (referencje) ===
// Dobre dla: 1:many, many:many, duże dokumenty
// Kolekcja: users
{
_id: ObjectId("user1"),
name: "Jan Kowalski"
}
// Kolekcja: orders
{
_id: ObjectId("order1"),
userId: ObjectId("user1"), // Referencja
products: [
{ productId: ObjectId("prod1"), quantity: 2 }
],
total: 150
}
// ✅ Brak limitu rozmiaru
// ✅ Dane aktualizowane niezależnie
// ❌ Wymaga $lookup (JOIN) lub wielu zapytań
Wzorce modelowania:
| Relacja | Strategia | Przykład |
|---|---|---|
| 1:1 | Embedding | User + Profile |
| 1:few | Embedding | Post + Comments (kilka) |
| 1:many | Referencing | User + Orders (setki) |
| many:many | Referencing | Users + Groups |
Replikacja i Sharding
Czym jest Replica Set i jak działa?
Odpowiedź w 30 sekund: Replica Set to grupa serwerów MongoDB utrzymujących te same dane dla wysokiej dostępności. Składa się z Primary (obsługuje zapisy) i Secondary (repliki). Przy awarii Primary automatycznie wybierany jest nowy (election). Read preference pozwala czytać z Secondary.
Odpowiedź w 2 minuty:
Architektura Replica Set zapewnia wysoką dostępność przez replikację danych między wieloma serwerami:
┌─────────────────────────────────────────────┐
│ REPLICA SET │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ PRIMARY │───▶│SECONDARY│ │SECONDARY│ │
│ │ (write) │ │ (read) │ │ (read) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └──────────────┴──────────────┘ │
│ Replikacja │
└─────────────────────────────────────────────┘
Read Preference:
// Czytaj z Primary (domyślne)
db.users.find().readPref("primary");
// Czytaj z Secondary (eventual consistency)
db.users.find().readPref("secondary");
// Preferuj Secondary, fallback do Primary
db.users.find().readPref("secondaryPreferred");
Write Concern:
// Potwierdzenie zapisu
db.users.insertOne(
{ name: "Jan" },
{ writeConcern: { w: "majority", j: true } }
);
// w: "majority" = większość nodów potwierdziła
// j: true = zapisano do journal (dysk)
MongoDB z Node.js
Jak używać Mongoose z Node.js?
Odpowiedź w 30 sekund:
Mongoose to ODM (Object Document Mapper) dla MongoDB. Definiuje schematy, walidację, middleware (hooks), virtual fields i metody. Schema → Model → Document. Połączenie przez mongoose.connect(), operacje przez metody modelu.
Odpowiedź w 2 minuty:
Mongoose definiuje schematy z walidacją, middleware i metodami dla bezpiecznej pracy z MongoDB:
const mongoose = require('mongoose');
// === POŁĄCZENIE ===
mongoose.connect('mongodb://localhost:27017/myapp');
// === SCHEMA ===
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Imię jest wymagane'],
trim: true,
minlength: 2
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, 'Nieprawidłowy email']
},
age: {
type: Number,
min: 0,
max: 120
},
role: {
type: String,
enum: ['user', 'admin', 'moderator'],
default: 'user'
},
createdAt: {
type: Date,
default: Date.now
}
});
// === VIRTUAL FIELDS ===
userSchema.virtual('isAdult').get(function() {
return this.age >= 18;
});
// === MIDDLEWARE (HOOKS) ===
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
// === METODY ===
userSchema.methods.comparePassword = async function(candidate) {
return bcrypt.compare(candidate, this.password);
};
// === MODEL ===
const User = mongoose.model('User', userSchema);
// === UŻYCIE ===
// Create
const user = await User.create({
name: 'Jan',
email: 'jan@example.com',
age: 30
});
// Read
const users = await User.find({ age: { $gte: 18 } });
const user = await User.findById(id);
const user = await User.findOne({ email: 'jan@example.com' });
// Update
await User.findByIdAndUpdate(id, { age: 31 }, { new: true });
// Delete
await User.findByIdAndDelete(id);
Zobacz też
- Kompletny Przewodnik - Rozmowa Node.js Backend Developer - pełny przewodnik
- SQL Pytania Rekrutacyjne - porównanie z SQL
- NestJS Pytania Rekrutacyjne - integracja z NestJS
- Express.js Pytania Rekrutacyjne - backend z Express
Ten artykuł jest częścią serii przygotowującej do rozmów rekrutacyjnych na stanowisko Backend Developer. Sprawdź nasze fiszki MongoDB z 44 pytaniami i odpowiedziami do nauki.
Chcesz więcej pytań rekrutacyjnych?
To tylko jeden temat z naszego kompletnego przewodnika po rozmowach rekrutacyjnych. Uzyskaj dostęp do 800+ pytań z 13 technologii.
