MongoDB i noSQL - rlip/java GitHub Wiki

Mongo

wady: problemy z tranzakcjami + shardning ograniczenie joiny duże powtarzanie danych duże zapotrzebowanie na ram (podbiera ram, dopóki jest dostępne), na poziome infrastruktury trzeba ograniczyć maksymalny rozmiar 16 MB

https://docs.mongodb.com/manual/reference/operator/query-comparison/


mongo // włączmay
show dbs // lista baz
use snacktime // przełączenie na baz snacktime
show collections // lista kolekcji
db.companies.drop() // usuwa baze companies
db.stats() // statystki
mongosh --port 27017 // połaczenie z konsola
sudo systemctl stop mongodb// w ubuntu

mongod --help //można m.in. zmienić path do danych i loga, za pomocą eval można wykonać jakis js lub coś zwrócić

Find


db.passengers.find() // znajduje wszystko (zwraca kursor)
db.passengers.find().pretty()  // j.w. z formatowaniem
db.passengers.find().count()  // liczba
db.passengers.find().toArray() // wszystko jako array
db.passengers.find().forEach(data => {printjson(data.age)})
db.passengers.findOne({"name" : "Scott Tolib"}).hobbies //pierwszy losowy znaleziony, spełniający warunek
db.flightData.find({"status.description": "on-time"})

//sortowanie 1 - asc, -1 - desc
db.movies.find().sort({"rating.average": -1})
db.movies.find().sort({"rating.average": 1, runtime: -1}) //więcej kolumn

//skip & limit
najpierw sortuje, potem skip, potem limit, niezależnie od kolejności napisania
db.movies.find().sort({"rating.average": 1, runtime: -1}).skip(100).limit(10)

// pola
db.passengers.find({},{name: 1}).pretty() //zwraca id i name
db.passengers.find({},{name: 1, _id: 0}).pretty() //zwraca tylko name
//szuka z genres: Drama, a wyświetla puste lub genre: Horror
db.movies.find({genres: "Drama"}, {genres: {$elemMatch: {$eq: "Horror}}})
//slice wyswietla tylko 2 elementy z array, można też dać [1, 2], wtedy są 2 pomijając 1
db.movies.find({genres: "Drama"}, {genres: {$slice: 2}, name: 1}})

db.FavouriteSnack.find({"snack": "cupcake"}, {preptime: {$nin:["10m"]}}) 
db.FavouriteSnack.find({preptime: {$nin:["10m"]}}) // not in preptime 10min
db.flightData.find({distance: {$gt: 100}})
db.flightData.findOne({distance: {$gt: 100}})

// logiczne
db.movies.find({$or:[{"rating.average": {$lt: 5 }}, {"rating.average": {$gt: 9.3 }}]}).count()
//and można analogicznie jak wyżej lub tak, tylko trzeba uważać żeby
// nie dawać niżej tej sammej nazwy bo się pierwsza nadpisze
db.movies.find({"rating.average": {$gt: 9}, genres: "Drama"}).count() 
db.movies.find({"runtime": {$not: {$eq: 60 }}}).count()  //można też $neq

//exists & type & regex 
db.users.find({age: {$exists: true}}) //mające pole age (false nie ma)
db.users.find({age: {$exists: true, $ne: null}}) //bez nuli
db.users.find({age: {$type: "number"}})
db.movies.find({summary: {$regex: /musical/}}).count() // index wydajniejszy, wydajniej jest użyć znaku początku frazy ^
db.foo.find({$expr: {$gt: [{$sum: ["$field1", "$field2"]}, 50)}})
db.products2.find({$expr: {$gt: [{$multiply: ["$price", "$qty"]}, 500] }}) // produkty których price*qty > 500
db.sales.find({$expr: {$gt:["$voulume", "$target"]}}) // related query, pole voulume > target

// where (mało wydajna)
db.foo.find({$where: function(){return this.field1 + this.field2 > 50}})

//pokaż takie, które mają (if volume jest większe od 190 to odejmniej
// od volume 20, else volume) większe od target
db.sales.find({$expr: {$gt: [{$cond: {if: {$gte: ["volume", 190]}, then: {$subtract: ["volume", 20]}, else: "$voulume"}}, "target"]}})

//size & all
db.users.find({"hobbies": {$size: 2}}) //co ma 2 hobbies
db.movies.find({genres: ["Drama", "Horror"]}) //dokładnie co ma tylko "Drama", "Horror" w tej kolejności
db.movies.find({genres: {$all: ["Drama", "Horror"]}}) //co ma w sobie, bez względu na order

//elemMatch
//to znajdzie userów którzy mają min. 1 hobby.title sports, i w min. jednym
//hobby  frequency > 3, ale nie koniecznie tym samym hobby
// and trzeba użyć, gdy mamy te same parametry, dla różnych nie trzeba używać and
db.users.find({$and:[{"hobbies.title": "Sports"}, {"hobbies.frequency": {$gt: 3 }}]}).pretty()
//tak znajdzie osoby, które mają hobby z tytle Sports i frequency > 3
db.users.find({hobbies: {$elemMatch: {title: "Sports", frequency: {$gte: 3}}}}).pretty()
db.products.find({}, {warehouses : {$elemMatch: {qty : {$lt : 10}}}})

Create


db.FavouriteSnack.insertOne({snack: "popcorn"}) //dodaje jeden
db.FavouriteSnack.insertMany([{snack: "popcorn"}, {snack: "chocolate"}])
db.FavouriteSnack.insert() // tak też można ale nie polecane

//domyśnie jest oredered insert - dodaje tak długo, przerwie dodawanie jak będzie błąd. Żeby pominiął błędy trzeba wyłączyć ordered: 
db.FavouriteSnack.insertMany([{snack: "popcorn"}, {snack: "chocolate"}], {ordered: false})
// w:1 - domyślne, w: 0 - bez czekania na potwierdzenie i id
// j: false - domyślne, j: true - z dziennikiem - zwiększa bezpieczeństwo
// wtimeout: 200 - po ilu m.sek. ma odrzucić zapis jeśli się od tego czasu nie zapisze 
db.FavouriteSnack.insertOne({snack: "popcorn"}, {writeConcern: {w: 1, j: false, wtimeout:200}})

Update, Replace, Unset, Rename, dodanie do tablicy


db.FavouriteSnack.updateOne({snack: "chocolate"},{$set: {snack: "cupcake"}}) // podnieni wymienione, resztę zostawi
db.FavouriteSnack.updateMany({preptime: "45m"},{$set: {preptime: "50m"}}) // jw
db.flightData.update({_id: ObjectId("6040dac6cb6f9b25d3d1c66e")}, {delayed: false}); //to zamienia całość!
db.flightData.replace({_id: ObjectId("6040dac6cb6f9b25d3d1c66e")}, {delayed: false}) // też zamienia
db.flightData.replaceFirst({_id: ObjectId("6040dac6cb6f9b25d3d1c66e")}, {delayed: false}) // zamienia pierwszy

//inclementuje age o 2 (minus deklementuje), do tego ustawia isSporty
db.users.updateOne({_id: ObjectId("604225a2f1fe58168f5ff260")}, {$inc: {age: 2}, $set: {isSporty: true}}})

//min zmienia wartość, jeśli nowa wartość będzie mniejsza niż obecna, max odwrtonie
db.users.updateOne({_id: ObjectId("604225a2f1fe58168f5ff260")}, {$min: {age: 28}})
//mul mnoży
db.users.updateOne({_id: ObjectId("604225a2f1fe58168f5ff260")}, {$mul: {age: 2}})

//usuwa pole isSporty - wartość w polu dowolna
db.users.updateMany({}, {$unset:{ isSporty: ""} })
//zmiana nazwę (nie wartość) pola
db.users.updateMany({}, {$rename:{ age: "totalAge"} })

//insert (jeśli nie istniueje) or update - trzeba ustawić upsert:true
db.users.updateOne({name: "Maria"}, {$set: {age: "29", hobbies: [{title: "Good food"}]} }, {upsert:true})

//do pozycji które mają element w którym zarówno hobbies.title = "Sports" i hobbies.frequency >= 3,
//dodajmey nowe pole hobbies.highFrequency = true, pozostawiając poprzednie
db.users.updateMany({hobbies: {$elemMatch: {title: "Sports", frequency: {$gte: 3}}}}, {$set: {"hobbies.$.highFrequency": true}})

//zminiejsza frequency dla każdego elementu w array dla totalAge >= 30
db.users.updateMany({totalAge: {$gte: 30}}, {$inc: {"hobbies.$[].frequency": -1}})

//dodanie arrayFilter, żeby dodać warunek, który będzie dodawać goodFrequency do
//pozycji w tablicy, które go spełniają
db.users.updateMany({"hobbies.frequency": {$gt: 2}}, {$set: {"hobbies.$[el].goodFrequency": true}}, {arrayFilters: [{"el.frequency": {$gt:2}}]})

//do tabicy obiektów addresses dodaje pole country=PL, obiekt ten musi spełniać spełnia warunek że jego city = Warszawa, 
db.users.update({}, {$set: {"addresses.$[elm].country": "PL"}}, {arrayFilters: [{"elm.city": "Warszawa"}]})

//dodanie do tablicy nowych elementów, dodatkowo je stortuje po frequency malejąco - push
//można zamist $push dać $addToSet, to nie pozwoli na duplikaty, wielkość liter ma znaczenie, $each - dodaje więcej wartości na raz
db.users.updateMany({name: "Anna"}, {$push: {hobbies: {$each: [{title: "Good Wine2", frequency: 5}, {title: "Hiking3", frequency:4}], $sort: {frequency: -1}}}})

//usuwa określony element z array - pull - podaje warunek, może użyć pullAll - wtedy podaje się w tablicy Wartości do usunięcia
db.users.updateMany({name: "Anna"}, {$pull: {hobbies: {title: "Hiking3"}}})

//usuwa pierwszy element z array (z 1 - uwuwa pierwszy) - pop
db.users.updateMany({name: "Anna"}, {$pop: {hobbies: -1}})

Delete

db.FavouriteSnack.deleteOne({snack: "popcorn"}) //usuwa 1 losowo spełniający warunek
db.FavouriteSnack.deleteMany({})

db.users.drop() // usuwa kolekcję i wszystkie dane
db.dropDatabase()

findAndModify

znajduje i modyfikuje - dzieki temu nie zieni się wartość pamiędzy odczytem a zapisem

db.counters.findAndModify({query: {counterName: "userIdCounter"}, update: {$inc: {"value": 1}}, new: true})
//większa pole value o 1, których counterName = userIdCounter. Flaga new oznacza czy ma zostać zwrócona poprzenia wartość, czy nowa

//import //drop - kasuje, bez tego dodaje mongoimport tv-shows.json -d movieData -c movies --jsonArray --drop

Typy

string(utf-8) timestamp, object array null (inne)


db.numbers.insertOne({a: NumberInt(1)})
typeof db.numbers.findOne().a

Typy numeryczne

db.persons.stats()
do.persons.insertOne({age: NumberInt("29")}) //int32, zależy od języka, w js domyślnie double
//NumberLong - 64bit int
//NumberDecimal - 128bit decimal - bez błędów zaokrąglania

Agregacje

$match = WHERE $group = GROUP BY $match = HAVING $project = SELECT $addFields = porzuca jakieś pola $sort = ORDER BY np. {$sort : {liczbFilmow : -1}} $limit = LIMIT $sum = SUM $count = COUNT $unwind - rozbijanie tablic $facet - podagregacje $sample - próbka losowych danych $lookup - left outer join

{ $unwind: "hobbies" }, // wypłaszcza tablicę - daje każdy element - kopiując resztę
// $push - dodaje elementy do array, addToSet - dodaje tylko unikalne

{ $group: { _id: { age: "$age" }, allHobbies: { $push: "$hobbies" } } }
//średni czas dla każdego z języków
db.movies.aggregate(
[
    {$group :
        {
                _id : "$original_language",
                średniaDługośćFilmu : {$avg : "$runtime"}
//liczbaFilmów: {$count: 1} //albo $sum: 1 w starszej wersji
        }
    }
])


// $slice - wycina z array,
 { $project: { _id: 0, examScore: { $slice: ["$examScores", 2, 1] } } }

//filter - ogranicza
scores: { $filter: { input: '$examScores', as: 'sc', cond: { $gt: ["$$sc.score", 60] } } }

// bucket - grupuje w podanych zakresach
// jest też sort, skip i limit
// $out - mozna zapisac od razu w nowej kolekcji
//filtrowanie, grupowanie i sortowanie
db.persons.aggregate([
    { $match: { gender: 'female' } },  //filtruje - przepuszcz tylko takie
    { $group: { _id: { state: "$location.state" }, totalPersons: { $sum: 1 } } },
    { $sort: { totalPersons: -1 } }
]).pretty();

//wyświetla tytuł i pierwszy element z tablicy genres
db.movies.aggregate([
{ $match: {directed_by: "George Lucas"}},
{ $project: {title: "$title", main : {$arrayElemAt : ["$genres", 0]}}} 
]
, {explain: true} // z tą linijką plan zapytania

)
//sredni zysk per reżyser plus sortowanie i limit
db.movies.aggregate(
[
    {$project : {directed_by : 1, zysk : {$subtract : ["$revenue.Amount","$budget.Amount"]}, revenue : 1, budget : 1}},
//tym rozbijemy tablicę reżyserów i będzie osobna kopia całości dla każdej pozycji w tabeli
{$unwind: "$directed_by"}, 
    {$group :
        {
            _id : "$directed_by",
            średniZysk : {$avg : "$zysk"}
        }
    },
    {$sort : {średniZysk : -1}},
    {$limit: 5}
])
// dwie agregacje w 1 zapytaniu
db.movies.aggregate(
[
{$facet : 
    {
       unikalneJęzyki : [{$group : {_id : "$original_language"}}],
       unikalneGatunki :  [{$unwind : "$genres"},{$group : {_id : "$genres"}}]
    }
}
])

// konwersje - działają od wersji 4
db.persons.aggregate([
    {
      $project: {
        _id: 0,
        gender: 1,
        birthdate: { $convert: { input: '$dob.date', to 'date'} }, //konwercja daty, można też date: {$toDate: "$dob.date"}
        age: "$dob.age",
        fullName: {
          $concat: [
            { $toUpper: { $substrCP: ['$name.first', 0, 1] } }, //pierwsza litera imienia będzie duża
            {
              $substrCP: [
                '$name.first',
                1,
                { $subtract: [{ $strLenCP: '$name.first' }, 1] } //reszta nazwy
              ]
            },
            ' ',
            { $toUpper: { $substrCP: ['$name.last', 0, 1] } },  //analogicznie z nazwiskiem
            {
              $substrCP: [
                '$name.last',
                1,
                { $subtract: [{ $strLenCP: '$name.last' }, 1] }
              ]
            }
          ]
        }
      }
    }
  ]).pretty();

//Lista unikalnych gatunków posortowanych malejąco po ilości filmów w każdym gatunku
db.movies.aggregate(
[
    {$unwind : "$genres"},
    {$group : {
            _id : "$genres",
            liczbaFilmow : {$sum : 1}
        }},
    {$sort : {liczbaFilmow : -1}}
])
// Lista występów aktorów per gatunek filmowy posortowana malejąco


lookup

widoki

db.getCollectionInfos({type : "view"}) tak można je znaleźć. W pipeline można podejrzewć jak został stworzony.

db.createView(
"unikalneGatunki",    
"movies",
[
    {$unwind : "$genres"},
    {$group : {_id : "$genres"}}
]   
)

mapReduce

depricated, raczej zaleca się agregacje, choć można użyć do dużych agregacji

var map = function(){
    emit(this.original_language, 1)
}
//en : [1,1,1,1,1,1,1,1,1..........]
//fr : [1,1,1,1,1,1,1,1,1..........]
var reduce = function(key, values){
    var multi = 1
    if(key = 'fr'){
        multi = 4
    }
    return Array.sum(values) * multi
}

db.movies.mapReduce(
map,
reduce,
{
  out : {inline : 1}  
})

//--------------------
// Policzyć sumę przychodu per gatunek filmowy
var map = function(){
    var me = this;
    this.genres.forEach(function(genre){
	emit(genre, me.revenue.Amount)
})
}

var reduce = function(key, values){
    return Array.sum(values)
}

db.movies.mapReduce(
map,
reduce,
{
  out : {inline : 1}  
})

można też użyć finalize

var map = function(){
    var self = this;
    this.genres.forEach(function(genre){
        emit(genre, { przychod: self.revenue.Amount, count : 1})
    })
}
var reduce = function(key, values){
    var sumaPrzychodu = 0;
    var sumaCount = 0
    values.forEach( x=> {
      sumaPrzychodu = sumaPrzychodu + x.przychod,
      sumaCount = sumaCount + x.count  
    })
    
    return { przychod: sumaPrzychodu, count : sumaCount}
}
var finalizer = function(key,reducedValue){
    return reducedValue.przychod / reducedValue.count
}
db.movies.mapReduce(
map,
reduce,
{
  finalize : finalizer,
  out : {inline : 1}  
})

Indexy

zapytanie, które zwraca większość wyników kolekcji, jest z indexem wolniejsze niż bez, index na parę przyspiesza też wyszukiwanie na 1 element, na 2 już nie index przyspiesza i zmiejsza zużycie pamięciu przy sotrowaniu jak mamy index na name, i zwracamy bez id, to pobranie samego name jest bardzo szybkie możemy też dodawać indexy na array, można tworzyć je w tle Można dać hinta, ale nie zawsze pomaga .hint(nazwa_indexu_lub_cały_index)

db.indexTest.stats()
db.contacts.getIndexes()
db.contacts.explain("executionStats").find({"dob.age": {$gt:60}}) //spraw. wydajności
// .totalDocsExaminated - liczba przeszukiwanych elementów
//executionsStages.saveState - licczba wstrzymań
//executionsStages.restoreState - liczba wznowień
db.contacts.createIndex({"dob.age": 1}) // tworzy index, 1 - rosnąco, -1 - malejąco
db.contacts.createIndex({"dob.age": 1, gender: 1}) // index na parę
db.contacts.createIndex({"email": 1}, {unique: true}) // unikalny
db.indexTest.createIndex({"job.$**" : 1}) // indeksuje z wildcardem - tworzy osobne indeksy dla każdego z pól wewnątrz job
db.indexTest.createIndex({"$**" : 1}) // pojedyńcze indexy na wszystko, można dodać wyjątki - czyli oprócz jakich pól


db.contacts.explain().find({"dob.age": 35}).sort({gender: 1}
db.contacts.explain("allPlanExecution").. // szczegóły dla wszystkich planów
db.rating.createIndex({age: 1), backgroud: true}) // bez blokowania cokelkcji

db.contaxts.dropIndex("nazwa_bądz_warunek")

//partial index - jak nie potrzebujemy całej wartości indexu
db.contacts.createIndex({"dob.age": 1}, {partialFilterExpression: {gender: "male"}})
//pratial unique - tylko na elementy, które mają email
db.users.createIndex({email: 1}, {unique: true, partialFilterExpression: {email: {$exist: true}}})

//ttl index - usuwa elementy po czasie
db.sessions.createIndex({jakaesPole: 1}, {expireAfterSeconds: 10})

//text index - przydatny do szukania - rozdziela słowa, usuwa mało znaczne i przyspiesza szukanie
// może być tylko jeden na kolekcję, ale może być na więcej niż 1 polu
db.products.createIndex({description: "text"})
db.movies.createIndex("$**" : "text"} // wildcard na wszystkiepola
db.products.find({$text: {$search: "book"}})
db.products.find({$text: {$search: "book -t-shirt"}}) //book, ale bez t-sirt
db.products.find({$text: {$search: "\"awesome book\""}}, score: {$meta: "textScore"}).sort({ score: { $meta: "textScore" }})

db.products.find({$text: {$search: "awesome book"}, {score: {$meta: "textScore"}}})
.sort({score: {$meta: "textScore"}) //wyszukuje i daje punkty, jak bardzo się zgadza

//można ustawić język, który ma związek z tym jakie słowa będą nieuwzgędniane
//można też ustawić wagę, dzięki czemu np. description będzie 10 razy ważniejszcze 
//przy liczeniu score
db.products.createIndex({title: "test", description: "text"},
 {default_language: "german", weights: {title: 1, description: 10}})

//statysyki indexów - od ostatniego restartu
db.indexTest.aggregate( [ { $indexStats: { } } ] )
// zwraca indexy które nie były użyte - od ostatniego restartu
db.indexTest.aggregate([{$indexStats: {}}, {$match : {"accesses.ops" : 0}}])

Cache

mongo dodaje do cache jaki index ma być użyty, możemy dadać tam swoje ustawienie, ale skasuje się to po restarcie bazy

db.indexTest.aggregate([{$planCacheStats : {}}])
db.indexTest.getPlanCache().clear()
db.runCommand(
    {
        planCacheSetFilter: "indexTest",
        query: {a:10},
        indexes: [{a:1}]
    }
)

Wykonywanie js

mongo creditrating.js

conn = new Mongo();
db = conn.getDB("credit");

for (let i = 0; i < 1000000; i++) {
    db.ratings.insertOne({
        "person_id": i + 1,
        "score": Math.random() * 100,
        "age": Math.floor(Math.random() * 70) + 18 
    })
}

Tranzakcje

const session = db.getMongo().startSession()
session.startTransaction()
const usersColl - session.getDatabase("blog").users
usersColl.deleteMany()
session.commitTransaction();

Schema Validation

Mamy 3 poziomy walidacji: off - wyłączona, strict - domyślnia - każdy insert i update, moderate - stare - mogą nie spełniać warunku, a nowe, lub te dlaa których było poprawnie już nie Mamy 2 akcje dla dokumentów niespełniających: error - dokument nie zostanie zapisany, warn - zostanie zapisany, ale jest ostrzerzenie. W starej konsoli jest wyjaśnienie czego dotyczy błąd, a w nowej jest tylko info, że jest błędne

//zmina walidacji istniejącej kolekcji
db.runCommand({   
  collMod: 'posts',
  validator: { (jak niżej) },
  validationAction: 'warn'  // będzie ostrzerzenie a nie błąd przy dodawaniu
})

// Dodanie walidacji do kolekcji

db.createCollection("products3", {
    validator: {
    $and: [
        { name: { $type: "string"}},
        { price: { $gt: 10 }}, 
        { price: { $type: "numeric" }}, 
    ]
}
});

//albo

db.createCollection("posts", {
    validator: {
        $jsonSchema: { 
            bsonType: "object",
            required: ["title", "comments"],
            properties: {
                title: {
                    bsonType: "string",
                    description: "must be a string and required"
                },
                comments: {
                    bsonType: "array",
                    description: "must be an array and required",
                    items: {
                        bsonType: "object",
                        required: ["text", "author"],
                        properties: {
                            text: {
                                bsonType: "string",
                                description: "must be a string and required"
                            },
                            author: {
                                bsonType: "objectId",
                                description: "must be a objectId and required"
                            }
                        }
                    }
                },
            }
        }
    }
})

schemaVallidation

Replica set

Proces posiadania tych samych danych na kilku serwerach i synchronizacji. Mamy serwer primary i serwery secondary. rs.

  • ustawiamy priorytet - w jakiej kolejności ma być wybierany primary server
  • jest arbiter - taki będzeł, który nie ma danych - więc może być słaby, mniejsze bezpieczeństwo
  • można ustawić, które serwery są liczone do kworum (większości) czyli mają vote, a które nie. Może być max 7 serwerów co mają vote. A 50 w całym replika secie.
  • węzeł hidden - można jak np., chcemy wydzielić jeden do jakiiś raportów, a żeby aplikacja tam n ie uderzała
// Wyłączyć działające mongod
// Przygotować katalogi na dane:
- db
- db2
- db3

//Uruchimić 3 instancje mongod
mongod --port 27017 --dbpath db --replSet rs1 --oplogSize 3
mongod --port 27018 --dbpath db2 --replSet rs1 --oplogSize 3
mongod --port 27019 --dbpath db3 --replSet rs1 --oplogSize 3
mongosh --port 27017
rs.initiate()
rs.status()
rs.config()
rs.printReplicationInfo()
rs.printSecondaryReplicationInfo()
rs.add("localhost:27018")
rs.add("localhost:27019")

/// zmiana priority
var cfg = rs.config()
4. Zmienić priorytet na 10 dla serwer od indeksie 0

cfg.members[0].priority = 10

5. Wczytać konfiguracje ze zmiennej

rs.reconfig(cfg)

Sharding

Rozdzielenie danych na kilka serwerów (shard). Jest Query router - wie gdzie są dane. Można zrobić ręcznie, albo automatycznie. sh.

Uruchomić z flagą shardsvr
mongod --port 27017 --dbpath db --replSet rs1 --oplogSize 3 --shardsvr
mongod --port 27018 --dbpath db2 --replSet rs1 --oplogSize 3 --shardsvr
mongod --port 27019 --dbpath db3 --replSet rs1 --oplogSize 3 --shardsvr

------------------------------
rs2:
1. Stworzyć katalog na dane "rs2db"
2. Uruchimić instancje rs2
mongod --port 27027 --dbpath rs2db --replSet rs2 --oplogSize 3 --shardsvr
3. Zainicjalizować repl set
rs.initiate()

------------------------------
cfg:
1. Stworzyć katalog na dane "cfgdb"
2. Uruchimić instancje cfgrs
mongod --port 27037 --dbpath cfgdb --replSet cfgrs --oplogSize 3 --configsvr
3. Zainicjalizować repl set
rs.initiate()
------------------------------
mongos

mongos --port 27050 --configdb "cfgrs/localhost:27037"

------------------------------
Połączyć się z mongosem (QR):
mongosh --port 27050
sh.status()
//dodajemy shardy
sh.addShard("rs1/localhost:27017,localhost:27018,localhost:27019")
sh.addShard("rs2/localhost:27027")

sh.shardCollection("test.indexTest",{a:1}) //shardowanie po polu a 
sh.status() /// tu można zobaczyć wynik, trzeba poczekań aż balancing porozrzuca dane

-----
sh.splitAt("test.indexTest", {a : 66})  // tak można ręczenie rodzielić dane

GridFS

Służy do przechowywania plików w mongoDB, raczej dla małych rozwiązań. Są one dzielone na 256kB.

ściągnąć mongodb-database-tools-ubuntu2004-x86_64-100.6 

mongofiles put <nazwa_pliku>   //dodanie pliku
db.fs.files.find()  // tu jest plik, dodają się 2 kolekcje fs.files i fs.chunks do aktualnej bazy danych
mongofiles get <nazwa_pliku> --local <nowa_nazwa_pliku>  // tak można pobrać
mongofiles find 

Monitoring

można użyć mongostats z mongodb-database-tools

db.aggregate([  //na bazie admin
{$currentOp : {}},
{$sort : {microsecs_running : -1}}
])
db.oplog.rs.find({op : {$ne : "n"}}) // oplog bez pustej komendy do mognor robi co 10 sekund

Profilowanie

db.system.profile ma domyślnie 1 mb, ale można to sobie zwiększyć

db.setProfilingLevel(2)
db.system.profile.find()
db.setProfilingLevel(0) // wyłaczenie

NoSQL

bazy kolumnowe np. cassandra

Tabla jest odwrócona o 90 stopni - dzięki temu zrobienie jakieś avg na tabeli co ma dużo rekordów wymaga tylko 1 przeszukiwania dysku i 1 odczytu. Minus jest przy zapisie i odczycie - wtedy jest więcej przeszukań i odczytów/zapisów