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). MongoDB 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 MongoDB
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 w MongoDB
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 w MongoDB?
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.