Kurs wystartował kolejny raz.
Przeszedłem instalacje MongoDB, podstawowe query z mongo shell i aplikacji Javowej.
Poznałem Spark Micro Web Framework i FreeMarket templaty.
Jak narazie OK, niezbyt trudne cwiczenia do przejscia w jedną noc.
Założyłem projekt github na potrzeby szkolenia https://github.com/piczmar/MongoDB4JavaDev/
2 czesc kursu
to podstawy CRUD i zapoznanie z Mongo Shell - interaktywna konsola dostepu do bazy, ktora jest interpretorem JavaScript.
Przydatne skroty klawiszowe:
- przywolanie ostatniej komendy: strzalka do gory
- przeskoczenie na poczatek linii: CTRL+A
- przeskoczenie na koniec linii: END
- dokonczenie polecenia/metody: TABrepli
Przydatne komendy:
help - wyswietla liste komend
help keys - wyswietla skroty klawiszowe
Mozna wykonywac pliki json z konsoli, np stworzmy plik o nazwie popAvg.json ktory chcemy wykonac na bazie o nazwie week5
use week5
db.zips.aggregate([{
$group:{
_id:'$state',
average_pop:{$avg:'$pop'}
}
}])
plik mozemy uruchomic:
mongo < popAvg.json
Zapoznanie z podstawami JavaScript, np definiowanie obiektow i odwolywanie sie do ich wlasnosci:
x = {a:"test"} // tworzy nowy obiekt
x.a // odwolanie do atrybutu a jako literalu
z="a"
x[z] //odwolanie do atrybutu jako wartosci slownikowej (ang. dictionary) klucz : wartość
Mongo zapisuje obiekty w formacie BSON
Podstawowe typy danych:
NumberInt - integer 32bity
NumerLong - integer 64bity
ISODate - date, new Date() tworzy ten typ danych
Zapisywanie obiektow:
db - zmienna trzymajaca aktualna baze
db.nazwa - kolekcja o nazwie 'nazwa' w bazie, zeby do niej zapisac nalezy wykonac metode insert np:
db.nazwa.insert(doc)
Wyszukiwanie:
db.people.find() - zwraca wszystkie obiekty, jesli podamy kryteria wyniki beda przefiltrowane, np:
db.people.find({name:"John"})
db.scores.find({score:200}).pretty() - wyswietla sformatowany wynik
db.people.findOne() - zwraca pierwszy lepszy rekord z kolekcji jesli nie podamy kryteriow, drugi argument po kryteriach prezycuje jakie pola chcemy w wyniku
> db.people.findOne({name:"Smith"})
{ "_id" : ObjectId("513ba7e947631d66ce571e99"), "name" : "Smith" }
> db.people.findOne({name:"Smith"},{"name":true, "_id":false})
{ "name" : "Smith" }
Wypisanie obiektu jako json:
var obj = db.people.findOne()
printjson(obj)
Wyszukiwanie za pomoca wyrazen:
db.scores.find({score:{$gt:10, $lte:20}}) - znajdz rekordy ktorych 'score'>10 && 'score'<20
Kryteria uzywaja porownania leksygraficznego tzn ze jesli w tej samej kolekcji pole jest typu string dla jednego rekordy a typu numerycznego dla innego i w kryterium uzywamy porownania dla stringow to rekordy w ktorych to pole nie jest stringiem sa w ogole pomijane.
Mozna tez przefiltrowac rekordy ktore maja dany atrybut, np:
db.people.find({profession: {$exists: true}}) - zwraca tylko te rekordy ktore maja atrybut 'profession'
lub atrybut okreslonego typu:
db.people.find({profession: {$type: 2}}) - rekordy z atrybutem 'profession' typu string, numery dla typow sa zdefiniowane w specyfikacji BSON
lub uzyc wyrazenie regularne:
db.people.find({name: {$regex: "m$"}}) - tylko rekordy ktorych 'name' konczy sie na litere 'm'
db.people.find({name: {$regex:'q'}})- rekordy ktorych 'name' zawiera litere 'q'
Unia OR:
db.people.find( { $or: [{name:"Adam"}, {profession:{$lte:"programmer"}}] })
Koniunkcja AND:
db.people.find( { $and: [{name:{$gt:"C"}}, {name:{$regex:"a"}}]}) - mozna to zapisac prosciej jako:
db.people.find( { name:{$gt:"C", $regex:"a"} })
Kryteria dla kolekcji
{ "_id" : ObjectId("513bb7374802e675111ba1c9"), "name" : "Howard", "favourites" : [ "pretzels", "beer" ] }
db.accounts.find({favourites:{$in:["pretzels", "chease"]}}) - wrzystkie rekordy zawierajace w polu 'favourites' wartosci 'pretzels' lub 'chease'
db.accounts.find({favourites:{$all:["pretzels", "chease"]}}) - wrzystkie rekordy zawierajace w polu 'favourites' wartosci 'pretzels' i 'chease' w dowolnej kolejnosci
Kryteria dla zagnieżdżonych dokumentow:
{ product : "Super Duper-o-phonic", price : 100000000000, reviews : [ { user : "fred", comment : "Great!" , rating : 5 }, { user : "tom" , comment : "I agree with Fred, somewhat!" , rating : 4 } ], ... }
db.catalog.find({price: {$gt:10000}, "reviews.rating": {$gte:5}}) - zwraca wszystkie produkty z price > 10000 i rating >=5
metoda find() zwraca kursor ktory ma metody: hasNext() i next() przydatne do nawigowania po wynikach, np:
> cur = db.people.find(); null; // null jako druga komenda zapobiega drukowaniu wyniku
> cur.limit(5); null; // limit do 5 rekordow
> cur.skip(2); null; // pomiń 2 recordy z wyniku
> cur.sort({name: -1}); null; // sortuj wynik po 'name' w odwrotnym porządku leksykograficznym
> cur.hasNext()
> cur.next()
Liczenie recordow:
db.scores.count({type:"essay", score: {$gt:90}})
Update:
db.people.update({name:"Adam"}, {profession: "seller"}) - update bierze 2 argumenty, 1: query, 2: nowe wartosci pól, pola pominiete zostana usunięte z dokumentu
np.:
{ "_id" : "Texas", "population" : 2500000, "land_locked" : 1 }
db.foo.update({_id:"Texas"},{population:30000000})
wynik: { "_id" : "Texas", "population" : 30000000 }
Inny sposob ktory modyfikuje tylko wybrane pola:
db.foo.update(_id:"Texas", {$set: {population: 30000}})
lub inkrementacja:
db.foo.update(_id:"Texas", {$inc: {population: 1}})
Jesli nie podamy kryterium to domyslnie zostanie updatowany pierwszy znaleziony a nie wszystkie jak to jest w SQL. Zeby updatowac wszystkie trzeba podac dodatkowy argument {mutil:true}
db.people.update({}, {$set:{title:"Dr"}}, {multi : true})
db.people.update({}, {$set:{title:"Dr"}}, {upsert : true}) //updatuje istniejace dokumenty lub utworzy nowy jesli nie znalazl dokumentow do updatowania
Iindywidualny dokument jest updatowany atomowo, ale Mongo nie wspiera izolowanych tranzakcji przy updacie wielu dokumentow na raz.
Usuwanie pol:
db.foo.update(_id:"Texas", {$unset: {population: 1}})
Operatory tablicowe:
> db.arrays.insert({_id:0, a:[1, 2, 3, 4]}) //tworzymy nowy obiekt z tablicą 'a'
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 1, 2, 3, 4 ] }
> db.arrays.update({_id:0}, {$set: {"a.2":5}}) //zmien element tbalicy o indeksie 2
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 1, 2, 5, 4 ] }
> db.arrays.update({_id:0}, {$push:{a:6}}) //dodaj nowy element na koniec tablicy
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 1, 2, 5, 4, 6 ] }
> db.arrays.update({_id:0}, {$pop:{a:1}}) //usun element z konca tablicy
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 1, 2, 5, 4 ] }
> db.arrays.update({_id:0}, {$pop:{a:-1}}) // usun element z poczatku tablicy
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 2, 5, 4 ] }
> db.arrays.update({_id:0}, {$pushAll:{a:[7,8,9]}}) // dodaj kilka elem. na koniec tablicy
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 2, 5, 4, 7, 8, 9 ] }
> db.arrays.update({_id:0}, {$pullAll:{a:[7,8,9]}}) //usun wszystkie elementy jak podane w liscie
^[db.arrays.findOne()
{ "_id" : 0, "a" : [ 2, 5, 4 ] }
> db.arrays.update({_id:0}, {$pull:{a:5}}) // usun pojedynczy element
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 2, 4 ] }
> db.arrays.update({_id:0}, {$addToSet:{a:5}}) // dodaj nowy element jesli jeszcze nie istnieje
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 2, 4, 5 ] }
> db.arrays.update({_id:0}, {$addToSet:{a:5}}) //dodaj nowy element jesli jeszcze nie istnieje
> db.arrays.findOne()
{ "_id" : 0, "a" : [ 2, 4, 5 ] }
db.runCommand({getLastError: 1})
przyklad wyniku:
{
"err" : "E11000 duplicate key error index: test.people.$_id_ dup key: { : \"Smith\" }",
"code" : 11000,
"n" : 0,
"connectionId" : 3,
"ok" : 1
}
To samo co w konsoli mozemy zrobic przy użyciu Java API, demo dostepne na githubie.
W dalszej czesci kursu bedziemy budowac aplikacje webowa. Zasa dzialania zostala opisana na filmie:
Porzyteczne narzedzia do profilowania w Mongo to:
z konsoli Mongo:
db.getProfilingStatus() - zwtaca aktualny status logowania
db.setProfilingLevel(1,3) - poziom logowania 1, trwajace dluzej niz 3ms, dostepne poziomy:
0 - logowanie wylaczone
1 - pokazuj tylko wolne operacje
2- pokazuj wszystkie operacje
Logi sa zapisywane do db.system.profile i moga byc przeszukiwane np:
db.system.profile.find({millis:{$gt: 1000}}).sort({ts:-1})
programy ktore sa instalowane razem z mongo:
mongotop - dostarcza statystyki na poziomie kolekcji mowiace ile Mongo spedza czasu na operacjach zapisu/odczytu
mongostat -statustyki operacji mongo zwlaszcza pozyteczne - wykorzystanie indeksow
Tydzien 5:
Agregacje w Mongo.
W SQL odpowiednikiem jest GROUP BY.
Zalozmy ze mamy kolekcje car zawierajaca dokumenty:
{name: 'car', price: '12}
{name: 'car', price: '2'}
> db.manuf.aggregate([{$group: { _id:'$name', num_car:{$sum:1}}}])
{ "result" : [ { "_id" : "car", "num_car" : 2 } ], "ok" : 1 }
dostajemy w wyniku liczbe dokumentow z name='car'
jesli chcielibysmy zsumowac ceny w grupie:
> db.manuf.aggregate([{$group: { _id:'$name', prices:{$sum: '$price'}}}])
Elementy pipline aggregacji:
$project - wybiera ktore atrybuty chcemy w wyniku
$match - filtruje kolekcje po jakims kryterium, jak WHERE w SQL
$group - grupuje wynik poprzedniego kroku pipeline
$sort - jak ORDER BY w SQL, sortuje wynik
$skip - omija iles poczatkowych rekordow
$limit - ogranicza wynik do iluś rekordow.
$inwind - normalizuje dane, jesli dokument ma tablice 3 rekordow to inwind utworzy 3 oddzielne dokumenty z jednym elementem tablicy
Compound grouping by multiple group:
mamy dokumenty:
{ "_id" : ObjectId("5154ba3f7b1fda1c6b22429b"), "category" : "drive", "name" : "car", "price" : 12 }
{ "_id" : ObjectId("5154ba437b1fda1c6b22429c"), "category" : "drive", "name" : "car", "price" : 23 }
{ "_id" : ObjectId("5154bf547b1fda1c6b22429d"), "category" : "drive", "name" : "bike", "price" : 10 }
{ "_id" : ObjectId("5154c1247b1fda1c6b22429e"), "category" : "swim", "name" : "canoo", "price" : 100 }
{ "_id" : ObjectId("5154c12f7b1fda1c6b22429f"), "category" : "swim", "name" : "boat", "price" : 101 }
db.manuf.aggregate([{$group:{_id:{name:'$name',categ:'$category'},sum:{$sum:1}}}])
dostaniemy:
{
"result" : [
{
"_id" : {
"name" : "boat",
"categ" : "swim"
},
"sum" : 1
},
{
"_id" : {
"name" : "canoo",
"categ" : "swim"
},
"sum" : 1
},
{
"_id" : {
"name" : "bike",
"categ" : "drive"
},
"sum" : 1
},
{
"_id" : {
"name" : "car",
"categ" : "drive"
},
"sum" : 2
}
],
"ok" : 1
}
Inne wyrazenia uzywane w grupowaniu:
$sum
$avg
$min
$max
$push
$addToSet
$first - uzywane z sort, daje pierwszy dokument
$last - uzywane z sort, daje ostatni dokument
Przyklady zastosowania niektorych operatorow na githubie.
Ograniczenia aggregacji:
- rozmiar wyniku ograniczony do 16MB (jako że wynik jest pojedynczym dokumentem)
- nie mozna uzyc wiecej niz 10% pamieci maszyny
- sharding: kolekcje sa na rozych nodach klastra, po pierwszym pipe $sort lub $group wynik musi byc zwrocony na klienta (tam gdzie dziala process 'mongos')
Jesli z powodu tych ograniczen nie mizna uzyc agregacji mozemy zamiast tego uzyc:
- mapreduce
- hadoop (jest konektor do mongo)
Tworzenie klastra na pojedynczej maszynie - koniecznie na roznych portach, normalnie stworzylibysmy na roznych serwerach:
1. tworzymy foldery danych dla 3ch replik:
mkdir c:\data\rs1
mkdir c:\data\rs2
mkdir c:\data\rs3
mongod --replSet rs1 --logpath "1.log" --dbpath c:\data\rs1 --port 27017
mongod --replSet rs1 --logpath "2.log" --dbpath c:\data\rs2 --port 27018
mongod --replSet rs1 --logpath "3.log" --dbpath c:\data\rs3 --port 27019
re
wszystie sa czescia tego samego zbiory replik rs1
Tworzymy konfiguracje dla zbioru w pliku init_replica.js:
config = { _id: "rs1", members:[
{ _id : 0, host : "localhost:27017", priority:0, slaveDelay:5},
{ _id : 1, host : "localhost:27018"},
{ _id : 2, host : "localhost:27019"} ]
};
rs.initiate(config);
rs.status();
Plik ten ladujemy do jednego z wezlow:
mongo --port 27018 < init_replica.js
Teraz laczymy sie do jednego z nodow i drukujemy status
mongo --port 27018
rs.status()
Mozemy sie polaczyc do jednego z SECONDARY node i sprawdzic ze nie mozemy do nich pisac, tylko mozna pisac do PRIMARY.
Jesli chcemy czytac z SECONDARY musimy najpierw powiadomic go o tym i po polaczeniu sie do niego wywolac:
rs.slaveOK()
rs.isMaster() - sprawdzenie czy node do ktoredo sie zalogowalismy jest MASTERem
Jak sprawdzic co sie dzieje podczas replikacji? Mamy w tym celu kolekcje oplog.rs:
use local
show collections
Tydzien 6. Sharding
Co to jest sharding? Jest to dzielenie kolekcji na wiele serwerów.
Po polaczeniu do mongo konsoli na domyslnym porcie ('mongo') mozemy sprawdzic status sharda:
sh.status()
oraz informacje o shardowanej kolekcji:
db.grades.stats()
Kazdy dokument miec shard key.
Shard key jest niezmienny.
Indexy musza sie zaczynac od shard key, np jesli jest shard key 'student_id'
to pozostale indeksy np:
(student_id, class)
(student_id, name)
Przy zapytaniach jesli nie uzyjemy shard key to zapytanie bedzie rozpropagowane na wszystkie nody w klastrze zamiast do poprawnego sharda.
Egzamin koncowy zdany na 95%, chyba powinienem czuć sie MongoDB expertem, jednak czuje ze ta wiedza szybko wyparuje jeśli nie będę jej używać.
Pięknie!
OdpowiedzUsuńI co z ta wiedzą po 3 latach?