MongoDB a Redis

Cześć.

Dziś wpis nawiązujący do zakończonej kilkanaście dni temu serii o Redisie i Pythonie. Wspomniany projekt to 10 artykułów prowadzących czytelnika od podstaw po zaawansowane zagadnienia w temacie wykorzystania nosql’owej bazy danych o nazwie Redis z poziomu języka Python. W czasie pracy nad tym projektem uświadomiłem sobie, że Redis, mimo, że jest szalenie popularny, to nie jedynym wiodącym rozwiązaniem na rynku. Ta luka w spójności przekazu trochę mi przeszkadzała więc dziś ją uzupełniam.

Zaczynajmy.

MongoDB

MongoDB, podobnie jak Redis, jest nosql’ową bazą danych. Pierwsza stabilna wersja pojawiła się w 2009 roku i powoli ale skutecznie zdobywała sympatyków. W przeciwieństwie do Redisa, który jest bazą typu klucz-wartość, Mongo jest bazą dokumentową. Dane składowane są jako dokumenty w stylu JSON, co umożliwia aplikacjom bardziej naturalne ich przetwarzanie, przy zachowaniu możliwości tworzenia hierarchii oraz indeksowania.

Mongo posiada możliwość tworzenia konkretnych baz, w których możemy tworzyć kolekcje a do nich dodawać owe dokumenty.

Zapis danych

W Redisie możemy wybrać jakiego typu danych użyjemy. Chcemy po prostu zbiór elementów? Wybieramy set. Chcemy pojedynczą wartość? Wybieramy string. Chcemy słownik? Wybieramy hash. I tak dalej…. W Mongo natomiast możemy stworzyć kolekcję a w niej nasze dokumenty. I to tyle. Więc jak widać mamy tu mniej elastyczności. Ale nim weźmiesz to za wadę – doczytaj do końca.

Żeby dodać dokument do kolekcji w Mongo możemy posłużyć się biblioteką pymongo. Instalujemy ją standardowo pip’em:

1
pip install pymongo

A samo podstawienie Mongo załatwi nam docker:

1
$ docker run --name some-mongo -p 27017:27017 -d mongo

I w sumie te dwa polecenia pozwalają nam już na eksperymenty.

Zacznijmy więc od wypełnienia kolekcji o nazwie test_collection:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pymongo import MongoClient
from pymongo.database import Database, Collection

client = MongoClient("localhost", 27017)

test_db: Database = client.test

sample_collection: Collection = test_db.test_collection

sample_collection.insert_many({
  'number': x,
  'sample_value': 'test ' + str(x)
} for x in range(0, 20))

W kodzie wyżej łączymy się z Mongo i używając metody insert_many dodajemy wygenerowane dane. Aby zobaczyć efekt, można skorzystać z klienta mongod – czyli odpowiednika redis-cli. Pierw się do niego podłączamy:

1
$ docker exec -it some-mongo  mongo

i już możemy wykonać:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
> use test
switched to db test
> db.test_collection
test.test_collection
> db.test_collection.find()
{ "_id" : ObjectId("5fe2380316ebbb487a0b213e"), "number" : 0, "sample_value" : "test 0" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b213f"), "number" : 1, "sample_value" : "test 1" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2140"), "number" : 2, "sample_value" : "test 2" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2141"), "number" : 3, "sample_value" : "test 3" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2142"), "number" : 4, "sample_value" : "test 4" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2143"), "number" : 5, "sample_value" : "test 5" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2144"), "number" : 6, "sample_value" : "test 6" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2145"), "number" : 7, "sample_value" : "test 7" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2146"), "number" : 8, "sample_value" : "test 8" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2147"), "number" : 9, "sample_value" : "test 9" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2148"), "number" : 10, "sample_value" : "test 10" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2149"), "number" : 11, "sample_value" : "test 11" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b214a"), "number" : 12, "sample_value" : "test 12" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b214b"), "number" : 13, "sample_value" : "test 13" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b214c"), "number" : 14, "sample_value" : "test 14" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b214d"), "number" : 15, "sample_value" : "test 15" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b214e"), "number" : 16, "sample_value" : "test 16" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b214f"), "number" : 17, "sample_value" : "test 17" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2150"), "number" : 18, "sample_value" : "test 18" }
{ "_id" : ObjectId("5fe2380316ebbb487a0b2151"), "number" : 19, "sample_value" : "test 19" }

Czyli najpierw wybieramy bazę, potem kolekcję a potem wykonujemy metodę find. Już nawet na postawie sposobu pracy z konsolą można zauważyć, że Mongo jest zdecydowanie bardziej obiektowe niż Redis, co raczej można uznać za zaletę.

Ogólnie kolekcje można w jakimś stopniu porównać do tabeli w SQLu. Co sprawia że powyższe pobranie danych można byłoby zapisać jako:

1
SELECT * FROM test_collection

Czekaj, stop!

Podoba Ci się to co tworzę? Jeśli tak to zapraszam Cię do zapisania się na newsletter:

Aby potwierdzić swoją subskrypcję, odbierz pocztę i kliknij w link potwierdzający:) jeśli maila nie ma to poczekaj chwile i/lub sprawdź folder spam/inne/oferty itp :)

Aby potwierdzić swoją subskrypcję, odbierz pocztę i kliknij w link potwierdzający:) jeśli maila nie ma to poczekaj chwile i/lub sprawdź folder spam/inne/oferty itp :)
a w ramach prezentu otrzymasz całkowicie za darmo, dwa dokumenty PDF „6 (nie zawsze oczywistych) błędów popełnianych podczas nauki programowania” który jest jednym z efektów ponad siedmioletniej pracy i obserwacji rozwoju niejednego programisty oraz „Wstęp do testowania w Pythonie”, będący wprowadzeniem do biblioteki PyTest.
Jeśli to Cię interesuje to zapraszam również na swoje social media.

Jak i do ewentualnego postawienia mi kawy :)

Pobieranie danych

Wyżej użyliśmy funkcji find do pobrania wszystkich elementów kolekcji. Zazwyczaj jednak chcemy mieć większy wpływ na to co pobieramy i tutaj Mongo zostawia Redisa w tyle. Ale po kolei. Możemy pobrać np. konkretne elementy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pymongo import MongoClient
from pymongo.database import Database, Collection
from pymongo.collection import ObjectId

client = MongoClient("localhost", 27017)

test_db: Database = client.test

sample_collection: Collection = test_db.test_collection

some_val = sample_collection.find_one({'number': 10})

"""{'_id': ObjectId('5fe2380316ebbb487a0b2148'), 'number': 10, 'sample_value': 'test 10'}"""

some_values = sample_collection.find({'number': {'$in': [1, 5]}})

"""[{'_id': ObjectId('5fe2380316ebbb487a0b213f'), 'number': 1, 'sample_value': 'test 1'},
{'_id': ObjectId('5fe2380316ebbb487a0b2143'), 'number': 5, 'sample_value': 'test 5'}]"""


sample_collection.find_one(ObjectId('5fe2380316ebbb487a0b2148'))

"""{'_id': ObjectId('5fe2380316ebbb487a0b2148'), 'number': 10, 'sample_value': 'test 10'}"""

korzystając z generowanego przez Mongo ObjectId. Ale to nie koniec. Dodajmy inne dane do kolekcji:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from pymongo import MongoClient
from pymongo.database import Database, Collection

client = MongoClient("localhost", 27017)

test_db: Database = client.test

sample_collection: Collection = test_db.test_collection

data = [
  {
      'name': 'Mateusz',
      'surname': 'Mazurek',
      'born_year': 1991,
      'tags': ['blogger', 'programista', 'techlead'],
      'skills': {
          'Python': 6,
          'SQL': 6,
          'MongoDB': 1
      }
  },
  {
      'name': 'Karol',
      'surname': 'Marks',
      'born_year': 1989,
      'tags': ['blogger', 'programista'],
      'skills': {
          'Python': 3,
          'SQL': 2,
          'MongoDB': 4
      }
  },
  {
      'name': 'Daniela',
      'surname': 'Kowalska',
      'born_year': 1996,
      'tags': ['blogger', 'hr'],
      'skills': {
          'Python': 0,
          'SQL': 0,
          'MongoDB': 0
      }
  }
]

sample_collection.insert_many(data)

I zerknij na taki kawałek kodu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from pymongo import MongoClient
from pymongo.database import Database, Collection

client = MongoClient("localhost", 27017)

test_db: Database = client.test

sample_collection: Collection = test_db.test_collection

print(list(sample_collection.find({'skills.Python': {'$gt': 2}})))

"""[
 {
    '_id':ObjectId('5fe23f6ac6cb7927c96d6a4f'),
    'name':'Mateusz',
    'surname':'Mazurek',
    'born_year':1991,
    'tags':[
       'blogger',
       'programista',
       'techlead'
    ],
    'skills':{
       'Python':6,
       'SQL':6,
       'MongoDB':1
    }
 },
 {
    '_id':ObjectId('5fe23f6ac6cb7927c96d6a50'),
    'name':'Karol',
    'surname':'Marks',
    'born_year':1989,
    'tags':[
       'blogger',
       'programista'
    ],
    'skills':{
       'Python':3,
       'SQL':2,
       'MongoDB':4
    }
 }
]
"""


print(list(sample_collection.find({'tags': {'$all': ['hr']}})))

"""[
 {
    '_id':ObjectId('5fe23f6ac6cb7927c96d6a51'),
    'name':'Daniela',
    'surname':'Kowalska',
    'born_year':1996,
    'tags':[
       'blogger',
       'hr'
    ],
    'skills':{
       'Python':0,
       'SQL':0,
       'MongoDB':0
    }
 }
]"""


print(list(sample_collection.find({'tags': {'$in': ['hr', 'techlead']}})))

"""[
 {
    '_id':ObjectId('5fe23f6ac6cb7927c96d6a4f'),
    'name':'Mateusz',
    'surname':'Mazurek',
    'born_year':1991,
    'tags':[
       'blogger',
       'programista',
       'techlead'
    ],
    'skills':{
       'Python':6,
       'SQL':6,
       'MongoDB':1
    }
 },
 {
    '_id':ObjectId('5fe23f6ac6cb7927c96d6a51'),
    'name':'Daniela',
    'surname':'Kowalska',
    'born_year':1996,
    'tags':[
       'blogger',
       'hr'
    ],
    'skills':{
       'Python':0,
       'SQL':0,
       'MongoDB':0
    }
 }
]"""

i zauważ jak fajny sposób zapytań udostępnia Mongo. Możesz łatwo pobrać te dokumenty, które np. mają jakąś konkretną wartość w konkretnym polu obiektu. W Redisie tego tak zgrabnie nie zrobisz.

Ale to nie wszystko! Mongo pozwala agregować dane. Czyli coś co w Redisie musiałbyś sobie napisać w LUA, tu masz od tak dostępne:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pymongo import MongoClient
from pymongo.database import Database, Collection

client = MongoClient("localhost", 27017)

test_db: Database = client.test

sample_collection: Collection = test_db.test_collection

print(list(sample_collection.aggregate([
  {
      '$match': {
          'skills.Python': {'$gt': 2}
      }
  },
  {
      '$group': {
              '_id': None,
              'average_python_skill': {
                  '$avg': '$skills.Python'
              }
          }
  }
])))

"""
[{'_id': None, 'average_python_skill': 4.5}]
"""

i pozwala wyliczyć np. średną wartość konkretnego pola dla elementów spełniających konkretny warunek. Tutaj wyliczamy średni skill pythonowy dla osób które mają go większego niż 2.

To w końcu Redis czy Mongo lepsze?

Praktycznie cały ten artykuł to zachwyt nad Mongo. Ale ostatnie 10 artykułów to zachwyt nad Redisem. To w końcu które rozwiązanie lepsze? Odpowiedź może być tylko jedna… Oba! Bo to zależy od tego do czego chcesz go wykorzystać. I wiem że brzmi to bardzo poprawnie politycznie, ale te bazy są tak różne, że tylko szaleniec próbowałby jednoznacznie wskazać lepsze rozwiązanie.

Zmierzając do brzegu

MongoDB nie używam komercyjnie i podstaw tej bazy nauczyłem się na potrzeby tego wpisu. Nie żałuję ani chwili poświęconej na to, bo to bardzo interesujące rozwiązanie. Jeśli sądzisz, że warto wspomnieć o jakimś ficzerze Mongo, którego po prostu być może nie znam, to nie krepuj się, zostaw komentarz.

Dzięki za wizytę,
Mateusz Mazurek
Mateusz M.

Pokaż komentarze

  • Artykuł lekki, dość rzeczowy. Oprócz technologii warto w przyszłości wspomnieć o porównaniu także przypadków użycia.

    Drobna uwaga: nie wplatajmy polityki czy jak niektórzy mogliby powiedzieć: ideologii do tematów technologicznych, bo jest to jednak mało profesjonalne.

    Pozdrawiam i życzę czasu i siły na kolejne artykuły.

Ostatnie wpisy

Podsumowanie: maj, czerwiec, lipiec i sierpień 2024

Oj daaawnoo mnie tu nie było. Ale wakacje to był czas dużej liczby intensywnych wyjazdów i tak naprawdę, dopiero jakoś… Read More

2 miesiące ago

Podsumowanie: kwiecień 2024

Cześć! Zapraszam na krótkie podsumowanie kwietnia. Wyjazd do Niemiec A dokładniej pod granicę z Francją. Chrześnica miała pierwszą komunię. Po… Read More

6 miesięcy ago

Podsumowanie: luty i marzec 2024

Ostatnio tygodnie były tak bardzo wypełnione, że nie udało mi się napisać nawet krótkiego podsumowanie. Więc dziś zbiorczo podsumuję luty… Read More

7 miesięcy ago

Podsumowanie: styczeń 2024

Zapraszam na krótkie podsumowanie miesiąca. Książki W styczniu przeczytałem "Homo Deus: Historia jutra". Książka łudząco podoba do wcześniejszej książki tego… Read More

9 miesięcy ago

Podsumowanie roku 2023

Cześć! Zapraszam na podsumowanie roku 2023. Książki Zacznijmy od książek. W tym roku cel 35 książek nie został osiągnięty. Niemniej… Read More

10 miesięcy ago

Podsumowanie: grudzień 2023

Zapraszam na krótkie podsumowanie miesiąca. Książki W grudniu skończyłem czytać Mein Kampf. Nudna książka. Ciekawsze fragmenty można by było streścić… Read More

11 miesięcy ago