Dawid Panfil

MongoDB w kilku krokach

Ze względu na to, że brakuje artykułów technicznych w języku polskim, to uznałem, że uzupełnię w miarę możliwości tę lukę. Będę poruszać tematy, które aktualnie mam na tapecie w pracy. Bez różnicy na poziom skomplikowania czy zaawansowane czy proste – tak jak dzisiejszy temat.

Na pierwszy ogień MongoDB, którego potrzebuje do aplikacji napisanej w nodejs (swoją drogą: świat się kończy – piszę backend w JS). Mongo to nic innego jak baza danych z tym, że jest nierelacyjna i trzymamy w niej dokumenty w postaci… w sumie w żadnej postaci co wrzucimy to tam mamy zapisane w BSON – czyli JSON w postaci binarnej. MongoDB wpasuje się idealnie w popularny termin NOSQL.

Instalacja

Na stronie https://docs.mongodb.com/manual/installation/ jest idealnie opisana instalacja. Jednak, abyś nie skakał z strony na stronę poniżej masz jak zainstalować MongoDB na Ubuntu.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org

Uruchomienie:

sudo service mongod start

Jeśli wszystko jest okej, to w pliku var/log/mongodb/mongod.log znajdziemy podobną linijkę:

2019-05-09T09:03:10.925+0200 I CONTROL [initandlisten] MongoDB starting : pid=11924 port=27017 dbpath=/var/lib/mongodb 64-bit host=soffee-dell

Przy okazji mamy tutaj podany port na którym pracuje nasz demon. Domyślnie 27017

Restart

 service mongod restart

Wyłączenie

service mongod stop

No to jeszcze jakieś GUI

Można w pisać wszystko w konsoli, ale jednak fajnie mieć dodatkowy podgląd. Na lubianym przez nas wszystkich serwisie stackoverflow najbardziej polecanym programem jest Robo 3T. No i z tego programu będziemy korzystać. Teoretycznie jest to wersja 30dniowa, ale co się stanie po tych 30 dniach jeszcze nie wiem 😉

Podstawy wykorzystywania MongoDb

Żeby nie było tak prosto, komendy piszemy w bashu, a przez GUI tylko sprawdzamy, czy wszystko się zgadza. Także pierwsze co to potrzebujemy wejść do klienta. Także wpisz mongo i gotowe.

mongo

Ja na początek dostałem jakieś tam warningi związane z domyślną konfiguracją, ale to nas w tym momencie nie interesuje, także pomijamy to i zabieramy się do zarządzania bazami danych. Zobaczmy jakie mamy bazy na początek:

show dbs

po zatwierdzeniu komendy dostaniemy listę tabel wraz z tym ile miejsca zajmują.

admin             0.000GB
config            0.000GB
local             0.000GB

Teraz wypadałoby stworzyć własną bazę. Jako przykład stworzymy sobie bazę, które będzie obsługiwała chat.

 
> use chat
switched to db chat

Automatycznie stworzy nam nową bazę chat.

Kolekcje

Zauważyłem, że utworzy bazę chat dopiero gdy dodamy kolekcję danych. Kolekcję bym porównał do czegoś w stylu tabeli w SQL. Lepszy przykład to segregator do którego wpinamy różne kartki i dokumenty.

Kolekcje można tworzyć na dwa sposoby. Prostszy to po prostu dodajemy do niej pierwszy wpis:

 db.rooms.insert({ private: 1, owner: 1, name: "Prywatny pokój"}) 

Jak widzisz przekazujemy zwykły obiekt json do kolekcji i koniec. Tak jak pisałem wkładamy kartkę do segregatora. Drugi sposób na utworzenie kolekcji jest to polecenie:

db.createCollection(<name>, { options } )

Jest tam kilka ciekawych opcji, dlatego polecam zapoznać się z tym linkiem. Jednak jak już podałem ten sposób kolekcji, to też go użyjemy. Skoro mamy pokój, to stworzymy sobie teraz użytkowników.

db.createCollection("users")

Stwórz sobie kilka kolekcji na różne sposoby, aby sprawdzić działanie.

Operacje na kolekcjach

Teraz skoro wiemy jak stworzyć kolekcję to sprawdźmy jakie faktycznie nam się stworzyły. Wykorzystujemy do tego polecenie show collections

> show collections
rooms
users
messages
test1
test2
test3

No okej mamy jakieś kolekcje, to teraz pousuwajmy śmieci. W moim wypadku zostaną tylko rooms, users i messages. Doprowadź swoją bazę do tego samego stanu poleceniem:

db.nazwa_kolekcji.drop()

i dla przykładu:

> db.test1.drop()
true

Operacje na dokumentach w kolekcjach

Dodawanie dokumentów

Jak wspominałem wcześniej kolekcje mają bliżej nieokreślone dokumenty. Które można dodawać, usuwać, wyszukiwać czy modyfikować. Dodawanie już poznałeś:

> db.rooms.insert({ private: 1, owner: 1, name: "Prywatny pokój"}) 

Dodajmy sobie jeszcze z dwa pokoje:

> var rooms = [ { owner: 2, name: "Główny" }, { owner: 1, name: "offtopic"} ] 
> db.rooms.insert(rooms)
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

Jak widzisz użyłem tutaj składni Js oraz przekazałem jako parametr tablicę obiektów, które jak widać różnią się od naszego pierwszego pokoju.

Wyszukiwanie danych

Zobaczmy teraz jakie mamy rekordy w kolekcji rooms. Służy do tego polecenie find(), które przyjmuje opcjonalnie paramtery. Jeśli wywołamy find bez parametrów to dostaniemy wszystkie rekordy:

> db.rooms.find()
{ "_id" : ObjectId("5cd401cf2eea32212e1f0892"), "private" : 1, "owner" : 1, "name" : "Prywatny pokój" }
{ "_id" : ObjectId("5cd409635151dfffb5deb809"), "owner" : 2, "name" : "Główny" }
{ "_id" : ObjectId("5cd409635151dfffb5deb80a"), "owner" : 1, "name" : "offtopic" }

Nie jest zbyt czytelnie. Dodajmy na koniec po kropce .pretty();

> db.rooms.find().pretty()
{
	"_id" : ObjectId("5cd401cf2eea32212e1f0892"),
	"private" : 1,
	"owner" : 1,
	"name" : "Prywatny pokój"
}
{
	"_id" : ObjectId("5cd409635151dfffb5deb809"),
	"owner" : 2,
	"name" : "Główny"
}
{
	"_id" : ObjectId("5cd409635151dfffb5deb80a"),
	"owner" : 1,
	"name" : "offtopic"
}

Od razu lepiej 🙂 Tylko, że spójrz na id. Jest to losowy unikany ciąg znaków, w wypadku pokoi jest to dla nas przydatne, bo trudniej będzie znaleźć pokój prywatny. Jednak na liście użytkowników wykorzystaliśmy sobie zwykłe numerki 1, 2. Oczywiście to nie jest żadna relacja i w tym miejscu może być dosłownie wszystko, jednak to pole użyjemy do tego jaki użytkownik może moderować pokój. Także sprawdzimy sobie jakimi pokojami zajmuje się użytkownik 1 i użytkownik 7, który nie istnieje.

> db.rooms.find({owner: 1}).pretty()
{
	"_id" : ObjectId("5cd401cf2eea32212e1f0892"),
	"private" : 1,
	"owner" : 1,
	"name" : "Prywatny pokój"
}
{
	"_id" : ObjectId("5cd409635151dfffb5deb80a"),
	"owner" : 1,
	"name" : "offtopic"
}
> db.rooms.find({owner: 7}).pretty()
> 

W pierwszym wypadku dostaliśmy dwa dokumenty, a w drugim nic. Jeśli potrzebujesz więcej informacji na temat wyszukiwania danych to zapoznaj się z tą lekturą.

Aktualizowanie dokumentów

Skoro mamy już za sobą dodawanie i wyświetlanie dokumentów, to czas coś edytować. Zlitujmy się nad tym userem 7 i dajmy mu pokój offtopic. Do tego służy polecenie:

db.nazwa_kolekcji.update(<kryteria>, <zmodyfikowny obiekt>)

czyli nasze polecenie będzie wyglądało następująco:

> db.rooms.update({name: "offtopic"}, {owner: 7})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.rooms.find({owner: 7})
{ "_id" : ObjectId("5cd409635151dfffb5deb80a"), "owner" : 7 }

No i mamy problem, nadpisało nam cały dokument, a nie zaktualizowało jedno pole. I tutaj dochodzimy do czegoś takiego jak operatory. Jeśli mnie posłuchałeś i zerknąłeś jak wyszukiwać w bardziej zaawansowany sposób, to już wiesz o czym teraz napisałem.

Operatory

Operatory to takie magiczne słówka zaczynające się od znaku $. I informują mongo co chcemy zrobić. Przykładowo zmienić tego ownera. Do tego wykorzystujemy operator $set.

Przywróć sobie dokument do pierwotnego offtopic do pierwotnego wyglądu

{
name: "offtopic",
owner: 1 
}

i teraz poprawnie edytujemy pole owner do wartości 7:

> db.rooms.update ( {name: "offtopic" } , { $set: {  owner: 7  } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.rooms.find({owner: 7})
{ "_id" : ObjectId("5cd409635151dfffb5deb80a"), "owner" : 7, "name" : "offtopic" }

Super, teraz jest tak jak chcieliśmy. Takich operatorów można znaleźć bardzo dużo, np autoinkrementowanie wartości. Uznajmy, że liczmy ile osób jest zalogowanych do pokoju. Także dodajemy sobie pole users: 0 do pokoju offtopic i następnie symulujemy wejście przez update i powiększenie tego pola. Wykorzystamy sobie operator $inc na polu users:

> db.rooms.update({name: "offtopic"}, {$inc: { users: 1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.rooms.find({owner: 7})
{ "_id" : ObjectId("5cd409635151dfffb5deb80a"), "owner" : 7, "name" : "offtopic", "users" : 1 }
> db.rooms.update({name: "offtopic"}, {$inc: { users: 1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.rooms.find({owner: 7})
{ "_id" : ObjectId("5cd409635151dfffb5deb80a"), "owner" : 7, "name" : "offtopic", "users" : 2 }

Jak widzisz, podwójne wykonanie tego samego zapytania dało nam dwóch użytkowników na kanale. Oczywiście operatory można łączyć, wszystkie dostępne znajdziesz w dokumentacji. Warto zapoznać się z nimi, z pewnością ułatwią w przyszłości pracę.

Usuwanie dokumentów

Do tego służy nam polecenie remove. Do którego wrzucamy sobie argumenty wyszukiwania jako parametr. Dla przykładu zdecydowaliśmy (no dobra ja zdecydowałem), że nie chcemy mieć żadnych pokoi prywatnych. Także usuwamy wszystkie które mają pole private na 1:

> db.rooms.remove({private: 1})
WriteResult({ "nRemoved" : 1 })

Sprawdź sobie. Dany pokój znikł.

Co jeszcze można zrobić z dokumentami?

Możemy je policzyć:

> db.rooms.count()
2

Także możemy robić wszystko to co wyżej pokazałem na bardziej skomplikowanych dokumentach i to tylko za pomocą jednej kropeczki:

> db.rooms.update({name: "offtopic"}, {$set: {owner: { id: 7, name: "dawid", email: "foo@bar.bas" } }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.rooms.find({"owner.id": 7})
{ "_id" : ObjectId("5cd409635151dfffb5deb80a"), "owner" : { "id" : 7, "name" : "dawid", "email" : "foo@bar.bas" }, "name" : "offtopic", "users" : 2 }

Podstawowa rzecz, paginacja:

> db.rooms.find().limit(2).skip(2)

I wiele wiele wiele innych rzeczy, które można znaleźć w dokumentacji, której nie będziemy przepisywać, bo i po co.

Wszystko to co pokazałem, staram się pokazywać w najprostszy sposób – nie zawsze zgodny z ogólnymi praktykami. Także pamiętaj, aby uzupełniać wiedzę, którą Ci przekazuje w moich artykułach. Jeśli będziesz miał z czymś problem to zachęcam do komentowania.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *