Merhaba arkadaşlar
Makale serisinin diğer yazıları için alttaki linkleri kullanabilirsiniz.
- Distributed Sistemlerde Consistency Kavramı ve Document DB Karşılaştırmaları (MongoDB Öğreniyoruz 1)
- MongoDB Kurulum ve CRUD İşlemleri (MongoDB Öğreniyoruz 2)
- MongoDB'de Embedded, Referenced Document ve Relation Kavramları (MongoDB Öğreniyoruz 3)
- MongoDB'de Detaylı Sorgulama/Querying (MongoDB Öğreniyoruz 4)
- MongoDB'de Index Kullanımı ve Sorgu Optimizasyonu (MongoDB Öğreniyoruz 5)
- MongoDB'de Aggregation Pipeline Stage Kullanımı (MongoDB Öğreniyoruz 6)
- MongoDB'de Aggregation Pipeline Operation Kullanımı ve Sorgu Optimizasyonu (MongoDB Öğreniyoruz 7)
- MongoDB' de Transaction Yönetimi (MongoDB Öğreniyoruz 8)
- MongoDB' de Role Tabanlı Yetkilendirme (MongoDB Öğreniyoruz 9)
- MongoDB'de Büyük Obje ve Dosyaların GridFS ile Yönetimi(MongoDB Öğreniyoruz 10)
MongoDb bütün işletim sistemlerine kurmak mümkün, resmi sayfasından Community Edition kuruluma ilgili bütün detaylara ulaşabilirsiniz. İnternette yapacağınız ufak bir araştırmayla Türkçe kurulum dokümanlarına da erişebilirsiniz.
-
Ayrıca isterseniz hiç MongoDb kurulumu yapmadan ilgili konu başlıklarında vereceğim linklerdeki eğitim amaçlı hazırlanmış online console'ları da kullanabilirsiniz.
-
Yada MongoDB Atlas'ın ücretsiz olan versiyonu için sayfasından hesap açarak kullanabilirsiniz. Atlas sayfasına şu linkten ulaşabilirsiniz.
-
Docker üzerinde çalışmak isterseniz Linux makineler için alttaki komutu çalıştırabilirsiniz.
port değiştirmek isterseniz ayrıca -p 27018:27017 eklemelisiniz. 27018 portunu istediğiniz bir portla değiştirebilirsiniz.
docker run -d --restart unless-stopped --name mongodb -e MONGO_INITDB_ROOT_USERNAME=adminuser -e MONGO_INITDB_ROOT_PASSWORD=Abc123 mongo:5.0-focal
- Windows makinelerde Docker ile çalıştırmak için alttaki komutu çalıştırabilirsiniz.
docker run -d --restart unless-stopped --name mongodb -e MONGO_INITDB_ROOT_USERNAME=adminuser -e MONGO_INITDB_ROOT_PASSWORD=Abc123 mongo:5.0-windowsservercore
- Son olarak özellikle transaction başlığında göreceğimiz bazı konular için sharded bir cluster'a ihtiyacımız olacak. Amacımız database administrator'lük olmadığı için detaylarına girip vakit kaybetmemek adına konfigürasyonu yapılmış hazır bir docker compose kurulumu yapacağız. Bu kurulumu yapmak zorunda değilsiniz anlatacağım konuları makaleyi takip ederek de rahatlıkla anlayabilirsiniz. Ancak tabii ki benim test edebilmek için bu versiyonu kurmam gerekiyor.
Bu kurulum için Bitnami image'ını ve compose-file'lını kullanacağız.
git clone https://github.com/bitnami/bitnami-docker-mongodb-sharded.git
cd bitnami-docker-mongodb-sharded
docker-compose -f docker-compose-multiple-shards.yml up -d
- Eğer Manuel olarak sharded bir cluster kurmak isterseniz şu sayfayı takip edebilirsiniz.
En son makaleye kadar bütün yapacaklarımızı MongopDB Shell ile yapacağız. Bu nedenle bunu da kurmamız gerekiyor.
Bunun için MongoDB sayfasındanişletim sisteminize göre indirip kurabilirsiniz. MongoDB Shell Node.js ile yazılmış Javascript ile tamamen uyumlu bir uygulamadır. Daha detaylı bilgi almak ve shell'in yeteneklerini öğrenmek için MongoDB Shell resmi sayfasını ziyaret edebilirsiniz.
Kurumunuzu yaptıktan sonra terminal, command prompt, powershell vb artık ne kullanıyorsanız alttaki komutla bağlanabilirsiniz. Bağlantı yaptıktan sonra alttaki gibi bir sonuç görüyor olmanız lazım.
mongosh "mongodb://localhost:27017"
# Current Mongosh Log ID: 62cc73d0d9b7e3f4b66fb82e
# Connecting to: mongodb://localhost:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.5.0
# Using MongoDB: 5.0.9
# Using Mongosh: 1.5.0
# For mongosh info see: https://docs.mongodb.com/mongodb-shell/
# [direct: mongos] test>
Ancak bu şekliyle örneğin veritabanlarını listelemeye alıştığımızda hata alırız çünkü login olmadık.
show databases
//MongoServerError: command listDatabases requires authentication
- Eğer MongoDB'yi doğrudan kurduysanız yani Docker kullanmadan kurduysanız, giriş yaptıktan sonra alttaki komutlarla admin kullanıcısı oluşturup MongoDb'yi yeniden başlatmalısınız.
use admin
db.createUser(
{
user: "adminuser",
pwd: "Abc123",
roles: [ { role: "userAdminAnyDatabase", db: "admin" },
{ role: "dbAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" } ]
}
)
Daha sonra alttaki komutla tekrar bağlanıp admin kullanıcımıza rollerini atamalıyız.
mongosh "mongodb://localhost:27017" -u "adminuser" -p "Abc123" --authenticationDatabase "admin"
Giriş yaptıktan sonra alttaki komutla adminuser kullanıcısına yetkilerini vermiş oluyoruz.
use admin
db.grantRolesToUser(
"myUserAdmin",
[ { role: "userAdminAnyDatabase", db: "admin" },
{ role: "dbAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" } ]
)
- Eğer MongoDB'yi Docker ile kurduysanız zaten environment variable (-e MONGO_INITDB_ROOT_USERNAME=adminuser -e MONGO_INITDB_ROOT_PASSWORD=Abc123) olarak kullanıcı adı şifre vermiştik.
mongosh "mongodb://localhost:27017" -u "adminuser" -p "abc123" --authenticationDatabase "admin"
- Eğer Docker compose ile cluster versiyonu kurduysanız zaten compose dosyasında kullanıcı adı ve şifre yazıyor.
mongosh "mongodb://localhost:27017" -u "root" -p "password123" --authenticationDatabase "admin"
Evet artık veri tabanlarını listeleyebiliriz.
show dbs
//admin 172.00 KiB
//config 4.03 MiB
MongoDB json kullandığı json dosyalarınızı düzenlemek için editör kullanabilirsiniz. Shell içinde edit komutu ile doğrudan kullandığınız editor ile çalışma yapabilirsiniz. Bunun için editörümüzü ayarlayalım.
config.set( "editor", "code" )
Eğitim boyunca tamamen MongoDB dokümanlarını takip edeceğiz. İlgili başlıklarda gerekli verililer de mevcut. Ayrıca isterseniz hiç MongoDb kurulumu yapmadan vereceğim linklerdeki eğitim amaçlı hazırlanmış online console'ları da kullanabilirsiniz.
Takip edeceğimiz dokümanları ana sayfası için şu linki ziyaret ediniz.
Ana başlıklar şu şekilde. Kırmızı kutu içindeki başlıklara kurguyu anlyacak kadar gireceğiz. Security kısmında ise kullanıcı ekleme ve role bazlı yetkşlendirme yapacak kadar gireceğiz. Amacımız bütün dokümanı uygulamak değil konsepti anlayıp yolumuz bulacak kadar öğrenmeye çalışacağız. Yani özetle balık tutmayı öğrenceğiz.
Developer bakış açısıyla devam edeceğiz yani amacımız administrative işler yapmak değil bu nedenle bol kod/query yazacağız. Son makalemizde Python ve Dotnet Core ile nasıl MongoDB kullanılıldığını da göreceğiz. Shell ile yaptıklarınızdan çok farklı birşey olmadığını şimdiden söyleyebilirim.
Evet artık çalışmaya başlayabiliriz. Bundan sonra yapacağımız bütün örneklerde kullanmak amacıyla bir database oluşturacağız.
use mymongodb
MongoDB'de database olulturmak için aktif database değiştirmek yeterli.
Yukarıda bahsettiğimiz gibi MongoDB'nin kullanıdı veri yapısı json. Bu nedenle eğer json hakkında bilginiz yoksa bir sonraki makaleye geçmeden önce biraz çalışma yapınız. Youtube dahil bir çok sitede kaynak bulabilirsiniz.
MongoDB'de standart json'daki veri tiplerinden daha fazlası vardır. Bir nevi json'ın extend edilmiş halidir. Bütün BSON tiplerini görmek için şu sayfayı ziyaret ediniz.
Şu linkten insertOne ve InsetMany fonksiyonlarını inceleyeceğiz.
Daha önce oluşturduğumuz mymongodb veritabanına geçiş yapıp devam ediyoruz.
- insertOne
Veri kaydetmek için bir collection'a (tablo) ihtiyacımız olacak. Bunun için sadece veri girişi yapacağımız collection adını vermemiz yeterli. Doküman kaydetmek için json kullanıyoruz. Süslü parantezle başlayıp biten alan dokümanımızı gösteriyor.
db.products.insertOne( { item: "card", qty: 15 } );
// [direct: mongos] mymongodb> db.products.insertOne( { item: "card", qty: 15 } );
// {
// acknowledged: true,
// insertedId: ObjectId("62cdd2893b4052b84cf2a4db")
// }
İleride daha detaylı göreceğiz ama şuan bilmesek de kaydettiğimiz dokümanı görmek için find fonksiyonunu kullanalım.
db.products.find()
//[
// { _id: ObjectId("62cdd2893b4052b84cf2a4db"), item: 'card', qty: 15 }
//]
MongoDb'de otomatik olarak _id adında bir alan oluşturulur bu alana unique olmak kaydıyla kendi değerimizi de yazabiliriz. Primary key olarak kullanılır.
db.products.insertOne( {_id:"unique_id", item: "pen", qty: 5 } );
// { acknowledged: true, insertedId: 'unique_id' }
Tekrar verileri kontrol edelim. Artık iki dokümanımız var.
db.products.find()
// [
// { _id: ObjectId("62cdd2893b4052b84cf2a4db"), item: 'card', qty: 15 },
// { _id: 'unique_id', item: 'pen', qty: 5 }
// ]
- insertMany
Aynı anda birden fazla dokümanı kaydetmek için kullanılır.
db.products.insertMany( [
{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps" , qty: 30 }
] );
// {
// acknowledged: true,
// insertedIds: {
// '0': ObjectId("62cdd61a3b4052b84cf2a4dc"),
// '1': ObjectId("62cdd61a3b4052b84cf2a4dd"),
// '2': ObjectId("62cdd61a3b4052b84cf2a4de")
// }
// }
- Bulk Write Eğer amacınız aynı ancak bir çok dokümanı eklemek, değiştirmek veya silmekse o zaman bu fonksiyonu kullanmalısınız.
Örnek bir bulkWrite fonksiyonu. Detaylar için şu sayfayı ziyaret ediniz.
db.characters.bulkWrite(
[
{ insertOne :
{
"document" :
{
"_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
}
}
},
{ insertOne :
{
"document" :
{
"_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
}
}
},
{ updateOne :
{
"filter" : { "char" : "Eldon" },
"update" : { $set : { "status" : "Critical Injury" } }
}
},
{ deleteOne :
{ "filter" : { "char" : "Brisbane" } }
},
{ replaceOne :
{
"filter" : { "char" : "Meldane" },
"replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
}
}
]
);
}
Update metotları için şu sayfayı kullanacağız.
Öncelikle değiştireceğimiz dokümanı bulmak gerekiyor. Bunu standart SQL'de where clause ile yapıyorduk burada onun yerine filter kullanıyoruz.
- updateOne
Önce birkaç doküman kaydedelim.
db.restaurant.insertMany( [
{ "_id" : 1, "name" : "Central Perk Cafe", "Borough" : "Manhattan" },
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "Borough" : "Queens", "violations" : 2 },
{ "_id" : 3, "name" : "Empire State Pub", "Borough" : "Brooklyn", "violations" : 0 }
] );
// { acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
Name alanına göre update yapalım. Görüleceği üzere bir adet doküman match etmiş ve bu dokümanın da bir alanı değişmiş. Bir diğer dikkat etmemiz gereken kısımda $set operatörü, aynı SQL'deki set gibi bu da ilgili alanın değerini değiştirmek içn kullanılır.
db.restaurant.updateOne(
{ "name" : "Central Perk Cafe" },
{ $set: { "violations" : 3 } }
);
// {
// acknowledged: true,
// insertedId: null,
// matchedCount: 1,
// modifiedCount: 1,
// upsertedCount: 0
// }
Olmayan bir kaydı değiştirmek isterken eğer kayıt yoksa eklenmesini istersek upset opsiyonunu kullanmalıyız.
db.restaurant.updateOne(
{ "name" : "olmayan kaydı update ediyorum" },
{ $set: { "violations" : 3 } },
{ upsert: true }
);
Verileri kontrol edelim. Görüleceği üzere en altta eklenen kayır görülebilir.
db.restaurant.find()
// [
// {
// _id: 1,
// name: 'Central Perk Cafe',
// Borough: 'Manhattan',
// violations: 3
// },
// {
// _id: 2,
// name: 'Rock A Feller Bar and Grill',
// Borough: 'Queens',
// violations: 2
// },
// {
// _id: 3,
// name: 'Empire State Pub',
// Borough: 'Brooklyn',
// violations: 0
// },
// {
// _id: ObjectId("62cde5b534e0f13b7eae76dd"),
// name: 'olmayan kaydı update ediyorum',
// violations: 3
// }
// ]
UYARI: Sharded distributed cluster yapısında bir dokümanı update ederken eğer upsert kullanmak isterseniz filtreye full sharded key'i de eklemek gerekiyor [kaynak].
Numeric bir alanın değerini arttırmak istiyorsak $inc operatörünü kullanmalıyız. Diğer Field Update operatörleri için şu sayfayı ziyaret ediniz.
Aynı anda adı Central Perk Cafe olan restaurantın hem violation değerini 3 arttırıp bir de aslında olmayan Closed alanını eklemiş oluyoruz.
db.restaurant.updateOne(
{ "name" : "Central Perk Cafe" },
{ $inc: { "violations" : 3}, $set: { "Closed" : true } }
);
Kontrol etmek için alttaki kodu çalıştırıyoruz.
db.restaurant.find({_id:1})
// [
// {
// _id: 1,
// name: 'Central Perk Cafe',
// Borough: 'Manhattan',
// violations: 6,
// Closed: true
// }
// ]
Dokümandan bir alanı çıkartmak için de $unset operatörünü kullanıyoruz.
db.restaurant.updateOne( { "name": "Central Perk Cafe" }, [{$unset: ["Closed"]}] );
// {
// acknowledged: true,
// insertedId: null,
// matchedCount: 1,
// modifiedCount: 1,
// upsertedCount: 0
// }
Kontrol ettiğimizde Closed alanının dokümandan silindiğini görebiliriz.
db.restaurant.find({"name": "Central Perk Cafe"})
// [
// {
// _id: 1,
// name: 'Central Perk Cafe',
// Borough: 'Manhattan',
// violations: 7,
// Review: true
// }
// ]
- updateMany
Birden fazla dokümanı değiştirmek için kullanılır. Örneğin violation değeri 2'den büyük olan restaurantlara review diye bir alan ekleyelim ve hepsinin violation değerini 1 arttıralım.
db.restaurant.updateMany(
{ violations: { $gt: 2 } },
{ $set: { "Review" : true }, $inc: { "violations" : 1} }
);
// {
// acknowledged: true,
// insertedId: null,
// matchedCount: 2,
// modifiedCount: 2,
// upsertedCount: 0
// }
Değişiklikliklere bakalım.
db.restaurant.find( { violations: { $gt: 2 } })
// [
// {
// _id: 1,
// name: 'Central Perk Cafe',
// Borough: 'Manhattan',
// violations: 7,
// Closed: true,
// Review: true
// },
// {
// _id: ObjectId("62cde5b534e0f13b7eae76dd"),
// name: 'olmayan kaydı update ediyorum',
// violations: 4,
// Review: true
// }
// ]
updateOne ile updateMany fonksiyonları arasında performans olarak bir fark yok denilebilir. Sadece updateMany'de multi opsiyonu true'dur ve tek bir doküman için hata olması durumunda updateOne tek doküman için daha faydalı bilgi verir.
Birde document DB'lerin en büyük eksikliklerinden biri de data integrity konusunda zayıf olmaları. Bu nedenle updateOne kullandığınızda sadece bir dokümanın değiştiğinden emin olursunuz.
Delete işlemleri için şu sayfayı kullanıyor olacağız. İlgili başlığa tıklayarak detayları görebilirsiniz.
- deleteOne
Daha önce restaurant collection'ınımıza kaydettiğimiz dokümanlardan birini _id'sine göre silelim. Bunun için öcelikle restaurantları sadece _id ve name alanlarını içerecek şekilde listeleyelim.
find fonksiyonunu içindeki boş süslü parantezler bir filtreleme yapmadığımızı gösteriyor. İkinci bölümde ise sadece name sütunu görmek istediğimizi söylemiş oluyoruz. 1 yerinde true da yazılabilirdi. _id sütnunu da istemiyor olsaydık name gibi yazıp değerini -1 dememiz yeterli olacaktı.
db.restaurant.find({}, {name:1})
// [
// { _id: 1, name: 'Central Perk Cafe' },
// { _id: 2, name: 'Rock A Feller Bar and Grill' },
// { _id: 3, name: 'Empire State Pub' },
// {
// _id: ObjectId("62cde5b534e0f13b7eae76dd"),
// name: 'olmayan kaydı update ediyorum'
// }
// ]
_id: ObjectId("62cde5b534e0f13b7eae76dd") olan kaydı silelim.
db.restaurant.deleteOne({_id:ObjectId("62cde5b534e0f13b7eae76dd")});
//{ acknowledged: true, deletedCount: 1 }
Tekrar listeyi kontrol edelim.
db.restaurant.find({}, {name:1})
// [
// { _id: 1, name: 'Central Perk Cafe' },
// { _id: 2, name: 'Rock A Feller Bar and Grill' },
// { _id: 3, name: 'Empire State Pub' }
// ]
- deleteMany
db.restaurant.deleteMany( { "_id" : { $gt : 1 } } );
//{ acknowledged: true, deletedCount: 2 }
test edecek olursak tek doküman kaldığını görebiliriz. İlgili
db.restaurant.find({}, {name:1})
// [ { _id: 1, name: 'Central Perk Cafe' } ]
Query metotları için şu sayfayı kullanacağız. Sayfa açıldığında örneklerin Compass üzerinde gösterildiğini görebilirsiniz.
Menüden Mongo Shell'i seçerek örnekleri görebilir hatta sanal shell üzerinde testlerinizi de yapabilirsiniz.
Query konusunda ayrıca tek başına detaylı göreceğimiz için burada basit bazı filtrelemeleri görüyor olacağız.
Öncelikle biraz kayır girelim.
db.inventory.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
// {
// acknowledged: true,
// insertedIds: {
// '0': ObjectId("62cf3797b8a5ff9793ca03e4"),
// '1': ObjectId("62cf3797b8a5ff9793ca03e5"),
// '2': ObjectId("62cf3797b8a5ff9793ca03e6"),
// '3': ObjectId("62cf3797b8a5ff9793ca03e7"),
// '4': ObjectId("62cf3797b8a5ff9793ca03e8")
// }
// }
- Bütün Kayıtları Çekmek
içerideki içi boş süslü parantezler bir filtreleme yapmadığımızı gösteriyor.
db.inventory.find( {} )
- Eşitlik Şartı ile Filtreleme
Herhangi bir alanın değerine göre filtre yapmak için tek yapmamız gereken alan için beklediğimiz değeri json formatında yazmak.
db.inventory.find( {status: "D" } )
// [
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e6"),
// item: 'paper',
// qty: 100,
// size: { h: 8.5, w: 11, uom: 'in' },
// status: 'D'
// },
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e7"),
// item: 'planner',
// qty: 75,
// size: { h: 22.85, w: 30, uom: 'cm' },
// status: 'D'
// }
// ]
- Query Operatörleri ile Filtreleme
Bütün Query Operatörleri için şu linki ziyaret ediniz.
Status alanının işereceği değerler için $in operatörü kullanılmış.
db.inventory.find( { status: { $in: [ "A", "D" ] } } )
// [
// kısaltıldı
// ,
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e5"),
// item: 'notebook',
// qty: 50,
// size: { h: 8.5, w: 11, uom: 'in' },
// status: 'A'
// },
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e6"),
// item: 'paper',
// qty: 100,
// size: { h: 8.5, w: 11, uom: 'in' },
// status: 'D'
// },
// kısaltıldı
// ]
- AND Operatörü Kullanımı
SELECT * FROM inventory WHERE status = "A" AND qty < 30
Standart SQL'de yukarıdaki gibi yazılmış bir ifadenin MongoDB'de yazımı şu şekildedir.
db.inventory.find( { status: "A", qty: { $lt: 30 } } )
// veya and operatörünü açıkça yazarak
db.inventory.find( { $and: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
// [
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e4"),
// item: 'journal',
// qty: 25,
// size: { h: 14, w: 21, uom: 'cm' },
// status: 'A'
// }
// ]
- OR Operatörü Kullanımı
SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")
Standart SQL'de yukarıdaki gibi yazılmış bir ifadenin MongoDB'de yazımı şu şekildedir.
/^p/ ifadesi bir reqular expression ifadesidir. p harfi ile başlayan kelimeleri içerir. Regex hakkında daha detaylı bilgi için şu sayfayı ziyaret ediniz.
db.inventory.find( {
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )
// [
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e4"),
// item: 'journal',
// qty: 25,
// size: { h: 14, w: 21, uom: 'cm' },
// status: 'A'
// },
// {
// _id: ObjectId("62cf3797b8a5ff9793ca03e8"),
// item: 'postcard',
// qty: 45,
// size: { h: 10, w: 15.25, uom: 'cm' },
// status: 'A'
// }
// ]
MongoDB'de CRUD işlemlerine hızlı bir giriş yapmış olduk. Bundan sonraki bir kaz yazıda CRUD işlemlerinin detaylarına gireceğiz.
İyi çalışmalar, görüşmek üzere.
Şimdi bu nereden çıktı diyebilirsiniz. Hani MongoDB'de schema zorunluluğu yoktu? Evet zorunluluk yok ancak seçenek olarak var. MongoDB json schema standartlarını destekler.
Amaç aslında verinin tutarlılığını sağlamak. Insert ve update işlemlerinde verinin belirtilen kurallara göre kaydedilip edilmediğini kontrol eder. Konu ile ilgili daha detaylı bilgi almak için şu sayfayı ve $jsonSchema operatörü için şu sayfayı ziyaret ediniz.
Schema collection create edilirken belirlenir. Örnek olarak ilgili sayfadan aldığımız aşağıdaki örneği test edelim.
Öncelikle collection'umuzu $jsonSchema operatörünü de kullanarak oluşturuyoruz.
db.createCollection("students", {
validator: {
$jsonSchema: {
bsonType: "object",
required: [ "name", "year", "major", "address" ],
properties: {
name: {
bsonType: "string",
description: "must be a string and is required"
},
year: {
bsonType: "int",
minimum: 2017,
maximum: 3017,
description: "must be an integer in [ 2017, 3017 ] and is required"
},
major: {
enum: [ "Math", "English", "Computer Science", "History", null ],
description: "can only be one of the enum values and is required"
},
gpa: {
bsonType: [ "double" ],
description: "must be a double if the field exists"
},
address: {
bsonType: "object",
required: [ "city" ],
properties: {
street: {
bsonType: "string",
description: "must be a string if the field exists"
},
city: {
bsonType: "string",
description: "must be a string and is required"
}
}
}
}
}
}
})
Daha sonra students collection'ını üzerinde kurallarımızı test ediyoruz.
use students
// gpa alanı double olması gerekirken biz integer verelim.
db.students.insertOne( {
name: "Alice",
year: Int32( 2019 ),
major: "History",
gpa: Int32( 3 ),
address: {
city: "NYC",
street: "33rd Street"
}
} )
// aiağıdaki gibi bir hata alırız.
// Uncaught:
// MongoServerError: Document failed validation
// Additional information: {
// failingDocumentId: ObjectId("62d18e283d35a3afc00610a0"),
// details: {
// operatorName: '$jsonSchema',
// schemaRulesNotSatisfied: [
// {
// operatorName: 'properties',
// propertiesNotSatisfied: [ { propertyName: 'gpa', details: [ [Object] ] } ]
// }
// ]
// }
// }
Schema'ların bir ilginç yönü de bir collection üzerinde schema zorunluğu olmasa da schema'ya uygun dokümanların filtrelenmesine olanak tanımasıdır.
db.inventory2.insertMany( [
{ item: "journal", qty: NumberInt(25), size: { h: 14, w: 21, uom: "cm" }, instock: true },
{ item: "notebook", qty: NumberInt(50), size: { h: 8.5, w: 11, uom: "in" }, instock: true },
{ item: "paper", qty: NumberInt(100), size: { h: 8.5, w: 11, uom: "in" }, instock: 1 },
{ item: "planner", qty: NumberInt(75), size: { h: 22.85, w: 30, uom: "cm" }, instock: 1 },
{ item: "postcard", qty: NumberInt(45), size: { h: 10, w: 15.25, uom: "cm" }, instock: true },
{ item: "apple", qty: NumberInt(45), status: "A", instock: true },
{ item: "pears", qty: NumberInt(50), status: "A", instock: true }
] )
Daha sonra bir schema oluşturalım
let myschema = {
required: [ "item", "qty", "instock" ],
properties: {
item: { bsonType: "string" },
qty: { bsonType: "int" },
size: {
bsonType: "object",
required: [ "uom" ],
properties: {
uom: { bsonType: "string" },
h: { bsonType: "double" },
w: { bsonType: "double" }
}
},
instock: { bsonType: "bool" }
}
}
ve bunu filtrede kullanalım.
db.inventory2.find( { $jsonSchema: myschema } )
// [
// {
// _id: ObjectId("62d18fe83d35a3afc00610a6"),
// item: 'apple',
// qty: 45,
// status: 'A',
// instock: true
// },
// {
// _id: ObjectId("62d18fe83d35a3afc00610a7"),
// item: 'pears',
// qty: 50,
// status: 'A',
// instock: true
// }
// ]
Mesela schema'ya uymayan bütün kayıtları update edelim.
db.inventory2.updateMany( { $nor: [ { $jsonSchema: myschema } ] }, { $set: { isValid: false } } )
Yada uymayan bütün dokümanları silelim.
db.inventory2.deleteMany( { $nor: [ { $jsonSchema: myschema } ] } )
Sharded bir cluster üzerinde çalışabilmek için node'larımız arasında proxy yapacak ve sharded cluster yapılarına özel komutları da çalıştırabilecek sunuclar ile uygulamamız arasında çalışacak mongos aracına ihtiyac vardır. Kurulumda bahsettiğimiz docker-compose aracı ile Bitnaminin sharded cluster image'larını kullanarak cluster ayağa kaldırdıysanız zaten bu sistem mongos üzerinden cluster ı yayına vermiş oluyor.
Mongosh ile bu port ile haberleştiğimizde zaten sharded cluster'a mongos üzerinden bağlanmış oluyoruz.
docker-compose-multiple-shards.yml dosyası içinde ilk image konfigürasyonunda mongodb-sharded container'ı 27017 portu üzerinden yayına verildiğini görebilirsiniz.
Cluster kurmak istemezseniz örnekleri yapmak için MongoDB Atlas free edition kullanabilirsiniz.
Sharded bir cluster'ı kendiniz manuel kurmak isterseniz alttak bazı kaynaklar verdim.
- MongoDB Resmi Sayfası
- Digital Ocean Sayfası
- Phonixnap Sayfası
- How to Forge Sayfası
- BMC sayfası - Pratik örnek
Shard ile ilgili lazım olabilecek kaynaklar
Ayrıca daha önce sharded olara kurmadığını yada standalone olarka kurduğunuz shared kurumunuzu distributed sharded cluster'a dönüştürmek de mümkün.
Artık çalışmaya başlayabiliriz. Görüleceği üzere
Öncelikle sharded sharded bir veritabanı oluşturmalıyız.
Tekrar hatıraltayım sh ile başlayan bu komutları çlaıştırabilmek için shard yapısının kurulu olduğu bir sisteme ihtiyacımız var.
Sharding metodlar için şu sayfayı ziyaret ediniz.
Sharded veritabanı oluşturuldu.
sh.enableSharding(shardeddb)
// sonuç
// {
// ok: 1,
// '$clusterTime': {
// clusterTime: Timestamp({ t: 1659444132, i: 4 }),
// signature: {
// hash: Binary(Buffer.from("18e8865a97acb4261abcc3ebea8c68d6fe7c1739", "hex"), 0),
// keyId: Long("7119176866014953490")
// }
// },
// operationTime: Timestamp({ t: 1659444132, i: 3 })
// }
use shardeddb
Şimdi sharded collection oluşturmadan önce shard key kavramını anlamamız gerekiyor. Shard key bir veya birden fazla field (compound key) ile oluşturulan ve dokümanların shard'lar arasındaki dağılımı belirleyen anahtardır.
MongoDB shard key değerlerini örtüşmeyecek (tekil/unique) shard key aralıklarına böler. Her aralık bir chunk ile ilişkilendirilir ve shard'lara (node/server) eşit olarak bölümlendirilir.
Doğru shard key seçimi için şu sayfayı ziyaret ediniz.
Mesela aşağıdaki örnekte yaş aralığı bir shard key olarak seçilmiş.
Tabii ki bu bölümlendirme işlemleri için kullanılan hash algoritmları, aralıklar vb bir şok şeye müdahele etmek mümkün. Konu ile ilgili oalrak resmi sayfasında bir çok bilgi mevcut.
Sharded collection oluşturmak için alttaki komut yeterli.
sh.shardCollection("shardeddb.people", { age: 1 } )
// sonuç
// {
// collectionsharded: 'shardeddb.people',
// ok: 1,
// '$clusterTime': {
// clusterTime: Timestamp({ t: 1659466863, i: 26 }),
// signature: {
// hash: Binary(Buffer.from("8170b9ee76e9383502bc40f2b718bacea3096a6c", "hex"), 0),
// keyId: Long("7119176866014953490")
// }
// },
// operationTime: Timestamp({ t: 1659466863, i: 22 })
// }
Eğer daha önce varolan bir collection'ı sharded collection yapmak istersek öncelikle index oluşturmalıyız.Biz halihazırda her colleciton'da otomatik olarak index oluşturulan _id alanını kullancağımız için buna gerek yok.
db.createCollection("unsharded")
db.unsharded.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
sh.shardCollection("shardeddb.unsharded", { _id: 1} )
// sonuç
// {
// collectionsharded: 'shardeddb.unsharded',
// ok: 1,
// '$clusterTime': {
// clusterTime: Timestamp({ t: 1659470259, i: 6 }),
// signature: {
// hash: Binary(Buffer.from("e7ff1b9065f78faf2691566bd05f53e96a56981c", "hex"), 0),
// keyId: Long("7119176866014953490")
// }
// },
// operationTime: Timestamp({ t: 1659470259, i: 2 })
// }
Shard durumu hakkında bilgi alalım.
sh.status()
// database: {
// _id: 'shardeddb',
// primary: 'shard1',
// partitioned: true,
// version: {
// uuid: UUID("657c5a0f-3fcb-4ce4-a654-698fd791db94"),
// timestamp: Timestamp({ t: 1659444132, i: 2 }),
// lastMod: 1
// }
// },
// collections: {
// 'shardeddb.people': {
// shardKey: { age: 1 },
// unique: false,
// balancing: true,
// chunkMetadata: [ { shard: 'shard1', nChunks: 1 } ],
// chunks: [
// { min: { age: MinKey() }, max: { age: MaxKey() }, 'on shard': 'shard1', 'last modified': Timestamp({ t: 1, i: 0 }) }
// ],
// tags: []
// },
// 'shardeddb.unsharded': {
// shardKey: { _id: 1 },
// unique: false,
// balancing: true,
// chunkMetadata: [ { shard: 'shard1', nChunks: 1 } ],
// chunks: [
// { min: { _id: MinKey() }, max: { _id: MaxKey() }, 'on shard': 'shard1', 'last modified': Timestamp({ t: 1, i: 0 }) }
// ],
// tags: []
// }
// }
// }
UYARI: Sharded distributed cluster yapısında bir dokümanı update ederken eğer upsert kullanmak isterseniz filtreye full sharded key'i de eklemek gerekiyor [kaynak].
Bundan sonra artık yukarıda gordüğümüz gibi CRUD işlemleri yapılabilir. Ancak sharded replicated bir yapıda yazma ve okuma yaparken read concern, write concern ve read preferences gibi dikkat etmemiz gereken konular var. Bu konu başlıklarını da transaction konusu ile yakın ilişki olduğu için ve bu makaleyi de çok fazla uzattığımız için ilgili yazıda işlemeyi uygun gördüm. Ayrıca aggregation başlıklarında da gerekli yerlerde bilgiler verildi.
Umarım faydalı olmuştur.
Makale serisinin diğer yazıları için alttaki linkleri kullanabilirsiniz.
- Distributed Sistemlerde Consistency Kavramı ve Document DB Karşılaştırmaları (MongoDB Öğreniyoruz 1)
- MongoDB Kurulum ve CRUD İşlemleri (MongoDB Öğreniyoruz 2)
- MongoDB'de Embedded, Referenced Document ve Relation Kavramları (MongoDB Öğreniyoruz 3)
- MongoDB'de Detaylı Sorgulama/Querying (MongoDB Öğreniyoruz 4)
- MongoDB'de Index Kullanımı ve Sorgu Optimizasyonu (MongoDB Öğreniyoruz 5)
- MongoDB'de Aggregation Pipeline Stage Kullanımı (MongoDB Öğreniyoruz 6)
- MongoDB'de Aggregation Pipeline Operation Kullanımı ve Sorgu Optimizasyonu (MongoDB Öğreniyoruz 7)
- MongoDB' de Transaction Yönetimi (MongoDB Öğreniyoruz 8)
- MongoDB' de Role Tabanlı Yetkilendirme (MongoDB Öğreniyoruz 9)
- MongoDB'de Büyük Obje ve Dosyaların GridFS ile Yönetimi(MongoDB Öğreniyoruz 10)
- https://www.mongodb.com/docs/manual/reference/insert-methods/
- https://www.mongodb.com/docs/manual/tutorial/update-documents/
- https://www.mongodb.com/docs/manual/tutorial/remove-documents/
- https://www.mongodb.com/docs/manual/tutorial/query-documents/
- https://www.mongodb.com/docs/manual/reference/operator/query/#std-label-query-selectors
- https://www.mongodb.com/docs/manual/reference/method/sh.status/