📌 Ne Zaman Kullanılır?
- ✅ Esnek schema, hızlı prototip, real-time, IoT, CMS, log toplama
- ⚠️ İlişkisel veri için SQL daha iyi (JOIN yoğun sorgular)
- ❌ Transaction-ağırlıklı iş (banka, finans), karmaşık ilişkisel veri
Önerilen Kullanım: Node.js + Mongoose + MongoDB (esnek veri modeli) Alternatifler: PostgreSQL (JSONB ile esnek), DynamoDB (AWS), Firebase Firestore
MongoDB
NoSQL Nedir? Ne Zaman Kullanılır? (What is NoSQL & When to Use?)
NoSQL (Not Only SQL): Esnek şema, yapılandırılmamış/yarı yapılandırılmış veri depolama.
Kullanım alanları / Use cases:
- Hızlı değişen veri yapıları (startup MVP, prototip)
- Real-time analytics, log toplama
- IoT sensor verileri
- İçerik yönetim sistemleri (CMS)
- Sosyal medya uygulamaları (kullanıcı profilleri, yorumlar)
Kurulum (Installation)
Ubuntu
# MongoDB 7.0 GPG key ve repo ekle
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
echo "deb [ 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
sudo apt update
sudo apt install mongodb-org -y
sudo systemctl start mongod
sudo systemctl enable mongodDocker
docker run -d \
--name mongo-dev \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret123 \
-p 27017:27017 \
mongo:7
# mongosh ile baglan / Connect with mongosh
docker exec -it mongo-dev mongosh -u admin -p secret123mongosh CLI Komutları (CLI Commands)
// Veritabani islemleri / Database operations
show dbs // Veritabanlarini listele
use myapp // Veritabanina gec (yoksa olusturur)
db.dropDatabase() // Veritabanini sil
// Koleksiyon islemleri / Collection operations
show collections // Koleksiyonlari listele
db.createCollection("users") // Koleksiyon olustur
db.users.drop() // Koleksiyonu sil
// Yardimci / Helpers
db.stats() // Veritabani istatistikleri
db.users.countDocuments() // Dokuman sayisi
db.users.estimatedDocumentCount() // Tahmini sayi (hizli)CRUD İşlemleri (CRUD Operations)
// INSERT
db.users.insertOne({
name: "Ali Yilmaz",
email: "ali@test.com",
age: 28,
tags: ["admin", "editor"],
address: { city: "Istanbul", country: "TR" },
createdAt: new Date()
});
db.users.insertMany([
{ name: "Ayse Demir", email: "ayse@test.com", age: 32, tags: ["user"] },
{ name: "Mehmet Kaya", email: "mehmet@test.com", age: 25, tags: ["user", "editor"] }
]);
// FIND (SELECT)
db.users.find(); // Tum dokumanlar
db.users.find({ age: { $gt: 25 } }); // age > 25
db.users.find({ tags: "admin" }); // tags icinde "admin"
db.users.find({ "address.city": "Istanbul" }); // Nested field
db.users.findOne({ email: "ali@test.com" }); // Tek dokuman
// Projeksiyon / Projection (belirli alanlar)
db.users.find({ age: { $gt: 20 } }, { name: 1, email: 1, _id: 0 });
// Siralama, limit, skip / Sort, limit, skip
db.users.find().sort({ age: -1 }).limit(10).skip(20);
// UPDATE
db.users.updateOne(
{ email: "ali@test.com" },
{ $set: { age: 29, "address.city": "Ankara" } }
);
db.users.updateMany(
{ age: { $lt: 30 } },
{ $set: { status: "young" }, $inc: { loginCount: 1 } }
);
// DELETE
db.users.deleteOne({ email: "mehmet@test.com" });
db.users.deleteMany({ status: "inactive" });Query Operators
// Karsilastirma / Comparison
db.users.find({ age: { $gt: 25 } }); // greater than
db.users.find({ age: { $gte: 25 } }); // greater than or equal
db.users.find({ age: { $lt: 30 } }); // less than
db.users.find({ age: { $lte: 30 } }); // less than or equal
db.users.find({ age: { $ne: 25 } }); // not equal
db.users.find({ age: { $in: [25, 28, 32] } }); // in list
db.users.find({ age: { $nin: [25, 28] } }); // not in list
// Mantiksal / Logical
db.users.find({ $and: [{ age: { $gt: 20 } }, { age: { $lt: 30 } }] });
db.users.find({ $or: [{ city: "Istanbul" }, { city: "Ankara" }] });
db.users.find({ age: { $not: { $gt: 30 } } });
// Regex
db.users.find({ name: { $regex: /^ali/i } });
db.users.find({ email: { $regex: "test\\.com$" } });
// Element / Varlik
db.users.find({ phone: { $exists: true } });
db.users.find({ age: { $type: "int" } });Aggregation Pipeline
// Basit gruplama / Simple grouping
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: {
_id: "$userId",
totalSpent: { $sum: "$total" },
orderCount: { $sum: 1 },
avgOrder: { $avg: "$total" }
}},
{ $sort: { totalSpent: -1 } },
{ $limit: 10 }
]);
// Lookup (SQL JOIN benzeri / SQL JOIN equivalent)
db.orders.aggregate([
{ $lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "user"
}},
{ $unwind: "$user" },
{ $project: {
_id: 0,
orderId: "$_id",
total: 1,
userName: "$user.name",
userEmail: "$user.email"
}}
]);
// Tarih bazli gruplama / Date-based grouping
db.orders.aggregate([
{ $group: {
_id: {
year: { $year: "$createdAt" },
month: { $month: "$createdAt" }
},
revenue: { $sum: "$total" },
count: { $sum: 1 }
}},
{ $sort: { "_id.year": 1, "_id.month": 1 } }
]);$unwind -- Array Açma (Array Unwinding)
// { _id: 1, name: "Ali", scores: [85, 92, 78] }
// $unwind sonrasi: 3 ayri dokuman (scores: 85), (scores: 92), (scores: 78)
db.students.aggregate([
{ $unwind: "$scores" },
{ $group: { _id: "$name", avgScore: { $avg: "$scores" } } }
]);
// Bos array'leri koruma
db.orders.aggregate([
{ $unwind: { path: "$items", preserveNullAndEmptyArrays: true } }
]);$facet -- Çoklu Pipeline (Multiple Pipelines)
Tek sorgu içinde birden fazla aggregation pipeline çalıştırır.
db.products.aggregate([
{ $facet: {
priceStats: [
{ $group: { _id: null, avg: { $avg: "$price" }, min: { $min: "$price" }, max: { $max: "$price" } } }
],
byCategory: [
{ $group: { _id: "$category", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
],
topExpensive: [
{ $sort: { price: -1 } }, { $limit: 5 }, { $project: { name: 1, price: 1 } }
]
}}
]);$bucket / $bucketAuto -- Aralık Gruplama (Range Grouping)
// Manuel aralik
db.users.aggregate([
{ $bucket: {
groupBy: "$age",
boundaries: [0, 18, 30, 45, 60, 120],
default: "Diger",
output: { count: { $sum: 1 } }
}}
]);
// Otomatik esit dagilim
db.orders.aggregate([
{ $bucketAuto: { groupBy: "$total", buckets: 4, output: { count: { $sum: 1 } } } }
]);$addFields / $set ve $merge
// Alan ekleme / guncelleme ($addFields ve $set ayni islevi gorur)
db.orders.aggregate([
{ $addFields: {
totalWithTax: { $multiply: ["$total", 1.18] },
itemCount: { $size: "$items" }
}}
]);
// Sonucu baska koleksiyona yaz (ETL / materialize view)
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: {
_id: { year: { $year: "$createdAt" }, month: { $month: "$createdAt" } },
totalRevenue: { $sum: "$total" },
orderCount: { $sum: 1 }
}},
{ $merge: { into: "monthly_reports", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
]);Aggregation Performans (Aggregation Performance)
// 1. $match ve $project pipeline'in basina koy (index kullanimi)
// 2. Buyuk veri setlerinde allowDiskUse
db.orders.aggregate([...], { allowDiskUse: true });
// 3. explain() ile analiz
db.orders.explain("executionStats").aggregate([...]);Multi-document Transactions
MongoDB 4.0+ sürümlerinde multi-document transaction desteği bulunur. Replica set gerektirir.
mongosh ile Transaction
const session = db.getMongo().startSession();
session.startTransaction({ readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } });
try {
const accounts = session.getDatabase("bank").accounts;
accounts.updateOne({ name: "Ali", balance: { $gte: 500 } }, { $inc: { balance: -500 } }, { session });
accounts.updateOne({ name: "Ayse" }, { $inc: { balance: 500 } }, { session });
session.commitTransaction();
} catch (error) {
session.abortTransaction();
} finally {
session.endSession();
}Node.js ile Transaction
const session = await mongoose.startSession();
try {
await session.withTransaction(async () => {
const product = await Product.findOneAndUpdate(
{ _id: productId, stock: { $gte: quantity } },
{ $inc: { stock: -quantity } },
{ session, new: true }
);
if (!product) throw new Error("Yetersiz stok");
await Order.create([{ userId, productId, quantity, total: product.price * quantity }], { session });
});
} finally {
session.endSession();
}writeConcern ve readConcern
| Seviye | Açıklama |
|---|---|
readConcern: "local" | Varsayılan. Yerel node'dan oku (rollback riski) |
readConcern: "majority" | Çoğunluk tarafından onaylanan veriyi oku |
readConcern: "snapshot" | Transaction boyunca tutarlı snapshot |
writeConcern: { w: "majority" } | Çoğunluk node'a yazıldı |
writeConcern: { w: "majority", j: true } | Çoğunluk + journal'a yazıldı |
Transaction Best Practices
- Kısa tut: 60 saniyeyi geçmemeli, uzun işlemler lock'a neden olur
- Retry logic: TransientTransactionError için otomatik tekrar mekanizması kur
- Gereksiz yere kullanma: Tek doküman işlemleri zaten atomiktir
- Replica set gerektirir: Standalone'da çalışmaz (test için single-node replica set kur)
İndeks (Indexes)
// Tekli indeks / Single field index
db.users.createIndex({ email: 1 }); // 1 = ascending, -1 = descending
// Benzersiz indeks / Unique index
db.users.createIndex({ email: 1 }, { unique: true });
// Bilesik indeks / Compound index
db.orders.createIndex({ userId: 1, createdAt: -1 });
// Text index (full-text search)
db.products.createIndex({ name: "text", description: "text" });
db.products.find({ $text: { $search: "hydraulic press" } });
// TTL index (otomatik silme / auto-delete)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 });
// Indeksleri goster / List indexes
db.users.getIndexes();
// Indeks sil / Drop index
db.users.dropIndex("email_1");Compound Index Sırası -- ESR Kuralı (ESR Rule)
Compound index alan sırası performansı doğrudan etkiler:
- Equality: Eşitlik koşulları ilk sıraya (
status: "active") - Sort: Sıralama alanları ikinci sıraya (
createdAt: -1) - Range: Aralık sorguları son sıraya (
age: { $gt: 25 })
// Sorgu: status = "active", age > 25, createdAt'e gore sirala
db.users.find({ status: "active", age: { $gt: 25 } }).sort({ createdAt: -1 });
// Dogru (ESR): { status: 1, createdAt: -1, age: 1 }
// Yanlis: { age: 1, status: 1, createdAt: -1 } // range once = sort icin index kullanilamazPartial Index
// Sadece aktif kullanicilar icin index (index boyutunu kucultur)
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { isActive: true } }
);
// Not: Sorgu partial filter'a uymazsa index kullanilmazWildcard Index
// Schemaless alanlar icin (metadata gibi)
db.products.createIndex({ "metadata.$**": 1 });
// Tum alanlari indexle (dikkatli kullan)
db.logs.createIndex({ "$**": 1 });Hashed Index
// Esitlik sorgulari ve shard key icin (range desteklemez)
db.users.createIndex({ email: "hashed" });
sh.shardCollection("myapp.users", { email: "hashed" });İndeks Kullanım Analizi -- explain() (Index Usage Analysis)
db.users.find({ email: "ali@test.com" }).explain("executionStats");
// IXSCAN = index kullaniliyor, COLLSCAN = full scan (kotuye isaret)
// totalDocsExamined >> nReturned ise index verimli degil
// Tum indexlerin kullanim istatistikleri
db.users.aggregate([{ $indexStats: {} }]);Schema Validation
MongoDB 3.6+ ile koleksiyonlara schema validation kuralları eklenebilir.
$jsonSchema ile Validation
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email", "age"],
properties: {
name: { bsonType: "string", minLength: 2, maxLength: 100 },
email: { bsonType: "string", pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" },
age: { bsonType: "int", minimum: 0, maximum: 150 },
status: { bsonType: "string", enum: ["active", "inactive", "banned"] },
tags: { bsonType: "array", items: { bsonType: "string" }, uniqueItems: true }
}
}
},
validationLevel: "strict", // strict (varsayilan) | moderate (mevcut uyumsuzlara dokunma)
validationAction: "error" // error (varsayilan) | warn (log'a yaz, izin ver)
});Mevcut Koleksiyona Validation Ekleme
db.runCommand({
collMod: "users",
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string" },
email: { bsonType: "string" }
}
}
},
validationLevel: "moderate",
validationAction: "error"
});Schema Design Patterns
Embedding (Gömme) -- 1:few ilişkiler için
// Adres kullanici icine gomulu / Address embedded in user
{
_id: ObjectId("..."),
name: "Ali Yilmaz",
email: "ali@test.com",
addresses: [
{ type: "home", city: "Istanbul", street: "Istiklal Cad. 42" },
{ type: "work", city: "Istanbul", street: "Levent Plaza 5" }
]
}Referencing (Referanslama) -- 1:many veya many:many için
// Siparis ayri koleksiyonda, user'a referans verir
// Order in separate collection, references user
{
_id: ObjectId("..."),
userId: ObjectId("user_id_here"),
items: [
{ productId: ObjectId("prod_1"), quantity: 2, price: 29.99 },
{ productId: ObjectId("prod_2"), quantity: 1, price: 49.99 }
],
total: 109.97,
createdAt: ISODate("2025-01-15")
}Kural / Rule: Birlikte okunan veri gömülür, bağımsız büyüyen veri referanslanır. (Data read together should be embedded; independently growing data should be referenced.)
Change Streams
Change Streams, MongoDB 3.6+ ile gelen real-time veri değişikliklerini dinleme mekanizmasıdır. Replica set gerektirir.
Node.js ile Change Stream
const changeStream = Order.watch([
{ $match: { operationType: { $in: ['insert', 'update'] } } }
], { fullDocument: 'updateLookup' });
changeStream.on('change', (change) => {
console.log('Degisiklik:', change.operationType, change.fullDocument);
if (change.operationType === 'insert') sendNotification(change.fullDocument);
});Python ile Change Stream
resume_token = None
try:
with db.orders.watch(full_document='updateLookup') as stream:
for change in stream:
print(f"Islem: {change['operationType']}")
resume_token = stream.resume_token # Kesintiden devam icin kaydet
except Exception:
if resume_token:
with db.orders.watch(resume_after=resume_token) as stream:
for change in stream:
print(f"Devam: {change['operationType']}")Kullanım Senaryoları (Use Cases)
- Bildirim sistemi: Yeni sipariş geldiğinde kullanıcıya bildirim gönder
- Cache invalidation: Veri değiştiğinde Redis cache'i otomatik temizle
- Veri senkronizasyonu: Değişiklikleri Elasticsearch veya başka sisteme aktar
- Audit log: Tüm değişiklikleri ayrı bir koleksiyona kaydet
Mongoose (Node.js) ile Kullanım (Mongoose Usage)
const mongoose = require('mongoose');
// Baglanti / Connection
mongoose.connect('mongodb://admin:secret123@localhost:27017/myapp?authSource=admin');
// Schema ve Model tanimlama / Define Schema & Model
const userSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true },
age: { type: Number, min: 0, max: 150 },
tags: [String],
address: {
city: String,
country: { type: String, default: 'TR' }
},
isActive: { type: Boolean, default: true },
createdAt: { type: Date, default: Date.now }
});
userSchema.index({ email: 1 });
userSchema.index({ 'address.city': 1, age: -1 });
const User = mongoose.model('User', userSchema);
// CRUD
// Create
const user = await User.create({
name: 'Ali Yilmaz', email: 'ali@test.com', age: 28, tags: ['admin']
});
// Read
const users = await User.find({ age: { $gt: 25 } }).sort({ name: 1 }).limit(10);
const single = await User.findOne({ email: 'ali@test.com' });
const byId = await User.findById('64a1b2c3d4e5f6...');
// Update
await User.findByIdAndUpdate(user._id, { $set: { age: 29 } }, { new: true });
await User.updateMany({ isActive: false }, { $set: { tags: [] } });
// Delete
await User.findByIdAndDelete(user._id);
await User.deleteMany({ isActive: false });Virtuals -- Hesaplanan Alanlar (Computed Fields)
userSchema.virtual('fullName').get(function () {
return `${this.firstName} ${this.lastName}`;
});
// Virtual'larin JSON ciktisinda gorunmesi icin
userSchema.set('toJSON', { virtuals: true });Middleware / Hooks
// pre('save') -- kaydetmeden once
userSchema.pre('save', async function (next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 12);
}
next();
});
// post('save') -- kaydettikten sonra
userSchema.post('save', function (doc) {
console.log(`Kaydedildi: ${doc.email}`);
});
// pre('findOneAndUpdate') -- update oncesi
userSchema.pre('findOneAndUpdate', function (next) {
this.set({ updatedAt: new Date() });
next();
});
// pre('deleteOne') -- silmeden once iliskili verileri temizle
userSchema.pre('deleteOne', { document: true, query: false }, async function () {
await Order.deleteMany({ userId: this._id });
});Populate -- İlişkisel Veri Çekme (Relational Data Fetching)
// Basit populate
const order = await Order.findById(orderId).populate('userId', 'name email');
// Nested (deep) populate
const order = await Order.findById(orderId)
.populate({
path: 'userId',
select: 'name email',
populate: { path: 'companyId', select: 'name' }
})
.populate('items.productId', 'name price');
// Kosullu populate
const orders = await Order.find()
.populate({ path: 'userId', match: { isActive: true }, select: 'name email' });Discriminators -- Schema Inheritance
const eventSchema = new mongoose.Schema(
{ message: String, timestamp: Date },
{ discriminatorKey: 'type' }
);
const Event = mongoose.model('Event', eventSchema);
const ClickEvent = Event.discriminator('ClickEvent',
new mongoose.Schema({ element: String, url: String })
);
const PurchaseEvent = Event.discriminator('PurchaseEvent',
new mongoose.Schema({ productId: mongoose.Schema.Types.ObjectId, amount: Number })
);
// Tum event'leri sorgula
const allEvents = await Event.find(); // Her iki tip de gelir
const purchases = await PurchaseEvent.find(); // Sadece satin almaPlugins
// Timestamps (yerlesik)
const postSchema = new mongoose.Schema(
{ title: String, content: String },
{ timestamps: true } // createdAt + updatedAt otomatik
);
// Pagination (mongoose-paginate-v2)
const mongoosePaginate = require('mongoose-paginate-v2');
postSchema.plugin(mongoosePaginate);
const result = await Post.paginate({ status: 'published' }, { page: 2, limit: 10 });
// Kendi plugin'ini yaz
function softDeletePlugin(schema) {
schema.add({ deletedAt: { type: Date, default: null } });
schema.methods.softDelete = function () {
this.deletedAt = new Date();
return this.save();
};
schema.pre(/^find/, function () {
if (!this.getQuery().deletedAt) this.where({ deletedAt: null });
});
}
userSchema.plugin(softDeletePlugin);PyMongo (Python) ile Kullanım (PyMongo Usage)
from pymongo import MongoClient
from datetime import datetime
# Baglanti / Connection
client = MongoClient('mongodb://admin:secret123@localhost:27017/')
db = client['myapp']
users = db['users']
# Create
user_id = users.insert_one({
'name': 'Ali Yilmaz',
'email': 'ali@test.com',
'age': 28,
'tags': ['admin'],
'created_at': datetime.now()
}).inserted_id
# Read
all_users = list(users.find({'age': {'$gt': 25}}).sort('name', 1).limit(10))
one_user = users.find_one({'email': 'ali@test.com'})
# Update
users.update_one({'email': 'ali@test.com'}, {'$set': {'age': 29}})
users.update_many({'age': {'$lt': 20}}, {'$set': {'status': 'young'}})
# Delete
users.delete_one({'email': 'ali@test.com'})
# Aggregation
pipeline = [
{'$match': {'age': {'$gt': 20}}},
{'$group': {'_id': '$address.city', 'count': {'$sum': 1}}},
{'$sort': {'count': -1}}
]
results = list(users.aggregate(pipeline))Güvenlik (Security)
Kimlik Doğrulama -- SCRAM Authentication
// Admin kullanici olustur
use admin
db.createUser({
user: "appAdmin",
pwd: "guvenliSifre123!",
roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" }
]
});
// Uygulama kullanicisi (sinirli yetki)
use myapp
db.createUser({
user: "appUser",
pwd: "appSifre456!",
roles: [{ role: "readWrite", db: "myapp" }]
});Built-in Roller (Built-in Roles)
| Rol | Açıklama |
|---|---|
read | Koleksiyonları okuma yetkisi |
readWrite | Okuma + yazma yetkisi |
dbAdmin | Index, istatistik, validation yönetimi |
userAdmin | Kullanıcı ve rol oluşturma/yönetme |
clusterAdmin | Replica set ve sharding yönetimi |
root | Tüm yetkiler (dikkatli kullan) |
Custom Role Oluşturma (Custom Role Creation)
db.createRole({
role: "analyticsReader",
privileges: [
{ resource: { db: "myapp", collection: "orders" }, actions: ["find", "aggregate"] },
{ resource: { db: "myapp", collection: "products" }, actions: ["find"] }
],
roles: []
});TLS/SSL Bağlantı (TLS/SSL Connection)
# mongod.conf
net:
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongodb.pem
CAFile: /etc/ssl/ca.pem
# Baglanma
mongosh "mongodb://localhost:27017/myapp" --tls --tlsCAFile /etc/ssl/ca.pemField-Level Encryption (Client-Side)
const client = new MongoClient('mongodb://localhost:27017', {
autoEncryption: {
keyVaultNamespace: 'encryption.__keyVault',
kmsProviders: { local: { key: masterKey } }, // Uretimde AWS/Azure/GCP KMS kullan
schemaMap: {
'myapp.users': {
bsonType: 'object',
properties: {
ssn: { encrypt: { bsonType: 'string', algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' } }
}
}
}
}
});Injection Korunma (Injection Prevention)
// YANLIS -- kullanici girdisi dogrudan sorguya
const user = await db.users.findOne({ name: req.query.name });
// { $gt: "" } girilirse tum kullanicilar doner!
// DOGRU -- sanitize et
const sanitize = require('mongo-sanitize');
const user = await db.users.findOne({ name: sanitize(req.query.name) });
// DOGRU -- tip kontrolu
if (typeof req.query.name !== 'string') return res.status(400).json({ error: 'Gecersiz' });
// $where ve $function kullanmaktan kacin (kod injection riski)Atlas Güvenlik (Atlas Security)
- IP Whitelist: Sadece belirli IP adreslerinden erişime izin ver
- VPC Peering: Atlas cluster'ı özel ağ üzerinden bağla
- Audit Log: Tüm veritabanı işlemlerini denetim için kaydet
- Encryption at Rest: Veriler diskte şifrelenir (AES-256)
MongoDB Atlas
Atlas, MongoDB'nin yönetilen bulut veritabanı hizmetidir (managed service).
Cluster Oluşturma
- cloud.mongodb.com adresinden hesap oluştur
- M0 Free Tier: Ücretsiz, 512 MB, öğrenme ve prototip için ideal
- Cloud provider seç (AWS, GCP, Azure) ve region belirle
- Kullanıcı + şifre oluştur, IP whitelist'e ekle
Connection String
mongodb+srv://kullanici:sifre@cluster0.xxxxx.mongodb.net/myapp?retryWrites=true&w=majorityAtlas Search
Lucene tabanlı full-text arama motoru. Text index'ten çok daha güçlü.
db.products.aggregate([
{ $search: {
index: "default",
text: {
query: "kablosuz kulaklik",
path: ["name", "description"],
fuzzy: { maxEdits: 1 }
}
}},
{ $project: { name: 1, price: 1, score: { $meta: "searchScore" } } },
{ $limit: 10 }
]);
// Autocomplete
db.products.aggregate([
{ $search: { index: "autocomplete_index", autocomplete: { query: "kulo", path: "name" } } },
{ $limit: 5 }, { $project: { name: 1 } }
]);Atlas Charts
- Doğrudan Atlas cluster'a bağlanır (ETL gerekmez)
- Bar, line, pie, heatmap, geo grafik tipleri
- Dashboard paylaşma ve iframe ile gömme desteği
Sık Kullanılan Komutlar (Quick Reference)
| Command | Description |
|---|---|
show dbs | Veritabanlarını listele |
show collections | Koleksiyonları listele |
db.collection.find().pretty() | Okunaklı çıktı |
db.collection.countDocuments({}) | Doküman say |
db.collection.getIndexes() | İndeksleri listele |
db.collection.explain().find({}) | Sorgu planını göster |
db.serverStatus() | Sunucu durumunu göster |
mongodump --db myapp --out backup/ | Yedek al |
mongorestore --db myapp backup/myapp/ | Geri yükle |
İpuçları ve Best Practices
Embedding vs Referencing Karar Ağacı (Decision Tree)
| Kriter | Embedding | Referencing |
|---|---|---|
| İlişki tipi | 1:1, 1:few | 1:many, many:many |
| Erişim | Her zaman birlikte okunuyor | Bağımsız erişim gerekiyor |
| Boyut | Küçük, sınırlı büyüme | Sınırlardan bağımsız büyüme |
| Güncelleme | Nadir güncellenir | Sık güncellenir |
| Doküman limiti | 16 MB altında | 16 MB'ı aşabilir |
ObjectId'den Tarih Çıkarma (Extracting Date from ObjectId)
const id = ObjectId("65a1b2c3d4e5f6a7b8c9d0e1");
id.getTimestamp(); // ISODate("2024-01-12T...")
// createdAt alani olmadan olusturma tarihine erisSchema Design Patterns
- Bucket Pattern: Zaman serisi verileri tek dokümanda grupla (IoT, log)
- Computed Pattern: Sık hesaplanan değerleri önceden sakla (toplam, ortalama)
- Subset Pattern: Büyük dokümanlardan sık erişilen alt kümeyi ana dokümanda tut
- Outlier Pattern: Büyük array'ler için ek dokümanlara taşma (
hasOverflow: true)
Performans İpuçları
// 1. Projection -- sadece gerekli alanlari cek
const users = await User.find({ isActive: true }, { name: 1, email: 1 });
// 2. lean() -- plain JS object doner, Mongoose overhead yok (sadece okuma icin)
const users = await User.find({ isActive: true }).lean();
// 3. Compound index ESR kuralini uygula (Equality -> Sort -> Range)
// 4. estimatedDocumentCount -- buyuk koleksiyonlarda O(1)
const count = await User.estimatedDocumentCount();
// 5. bulkWrite -- tek seferde coklu islem (round-trip azalt)
await User.bulkWrite([
{ insertOne: { document: { name: "Yeni", email: "yeni@test.com" } } },
{ updateOne: { filter: { email: "ali@test.com" }, update: { $set: { age: 30 } } } },
{ deleteOne: { filter: { email: "eski@test.com" } } }
]);
// 6. Cursor -- buyuk veri setlerini bellek tasirmadan isle
const cursor = User.find({ isActive: true }).cursor();
for await (const user of cursor) {
await processUser(user);
}