This is an old revision of the document!


Laboratorul 11 - MongoDB

Obiective

  • Comenzi MongoDB
  • Operații DML
  • Indexare
  • Funcții
  • Agregarea datelor

Comenzi MongoDB

Pornire MongoDB

Pentru a porni baza de date MongoDB se va folosi demonul mongod(mongod).

Procesul demon mongod poate porni cu următorii paramterii:

  • --config <filename> specifică un fisier de configurare
  • --port <port> pentru a specifica portul pe care asculta procesul demon, implicit 27017
  • --bind_ip <hostnames|ipaddresses|Unix domain socket paths> pentru a specifica addresa de
  • --dbpath <path> calea către directorul unde se vor stoca fișierele de stocare.

Exemplu utilizare mongodb

mongod --port 27018 --bind_ip 192.100.10.10 --dbpath /data

Ex. 1. Să creeze un director pe discul Student numit mongo_data. Să se pornească baza de date cu folosind ca director de stocare noul director creat. Restul valorilor rămân cele implicite.

Conectare MongoDB

Pentru a va conecta la un server MongoDB, se folosește interpretorul în linie de comandă mongo (Documentație).

Exemplu de utilizare:

# se conectează implicit la localhost pe portul 27017
mongo
 
# se conectează la hostname folosind portul implicit 217017
mongo --host hostname
 
# se conectează implicit la localhost folosind portul port
mongo --port 27017
 
# se conectează la hostname folosind portul port
mongo --host hostname:27017
 
mongo --host hostname --port 27017

Ex. 2. Conectați-vă la MongoDB.

Comenzi CLI MongoDB

Pentru a vedea baza de date curentă se folosește comanda db.

Pentru a vedea toate bazele de date existente se folosește comanda show dbs.

Pentru a vă conecta la o baza de date sau a crea o nouă bază de date se folosește comanda use DATABASE_NAME. În cazul în care se folosește comanda use pentru a crea o bază de date, aceasta va deveni persistentă numai dacă se crează o colecție sau se inserează o înregistrare.

Pentru a vedea toate colecțiile se folosește comanda show collections.

Puteți folosi una din următoarele metode pentru a crea o nouă colecție:

  • db.createCollection(“COLLECTION_NAME”), unde COLLECTION_NAME este numele noii colecții
  • inserați o înregistrare și colecția se va crea automat.

Pentru a șterge o colecție se folosește comanda db.COLLECTION_NAME.drop().

Pentru a șterge o baza de date se folosește comanda db.dropDatabase(). Trebuie să fiți conectați la baza de date (folosing use) pentru a o șterge.

Numele bazelor de date este case sensitive.

Ex. 3. Creați o bază de date numită faculty care să conțină colecția students.

Operații DML

Inserarea datelor

Pentru a adăuga date în MongoDB puteți sa folosiți una din metodele:

insert()

Comanda Insert are următoare sintaxa

db.collection.insert(
   <document or array of documents>,
   {
     writeConcern: <document>,
     ordered: <boolean>
   }
)

Un document dintr-o colecție este în format BSON:

{
    "student": 
        {
            "firstname": "Ion", 
            "lastname": "Popescu"
        },
    "an": 4,
    "grupa": "342C3",
    "materii": 
        [
            {"nume": "BD2",  "an": 4}, 
            {"nume": "Comp", "an": 4}, 
            {"nume": "SO2",  "an": 4}
        ],
    "cunostinte": ["SQL", "Java", "PL/SQL"]
}

Pentru a insera documentul în colecția students:

db.students.insert(
{
    "student": 
        {
            "firstname": "Ion", 
            "lastname": "Popescu"
        },
    "an": 4,
    "grupa": "342C3",
    "materii": 
        [
            {"nume": "Comp", "an": 4}, 
    	    {"nume": "BD2" , "an": 4}, 
            {"nume": "SO2" , "an": 4}
        ],
    "cunostinte": ["SQL", "Java", "PL/SQL"]
}
)

Pentru a va ușura munca puteți sa va creați variabile la nivelul consolei. Dacă se dorește inserarea mai multor înregistrari, se poate declara un vector.

var stud = {"student": { "firstname": "Andrei", "lastname": "Ionescu"}, "an": 4, "grupa": "341C4", "materii": [{"nume": "BD2", "an": 4}, {"nume": "IA", "an": 4},{"nume": "IAUT", "an": 4}], "cunostinte": ["C", "C++", "SQL"] }
 
db.students.insert(stud)
 
var studs = [
    {"student": { "firstname": "George", "lastname": "Popescu"}, "an": 4, "grupa": "341C2", "materii": [{"nume": "BD2", "an": 4}, {"nume": "IOCLA", "an": 2}], "cunostinte": ["Python", "SQL"]  },
    {"student": { "firstname": "Georgiana", "lastname": "Petrescu"}, "an": 4, "grupa": "341C2", "materii": [{"nume": "BD2", "an": 4}, {"nume": "SCAD", "an": 4}], "cunostinte": ["Python", "SQL"]  },
    {"student": { "firstname": "Valentina", "lastname": "Vasilescu"}, "an": 3, "grupa": "331CA", "materii": [{"nume": "BD", "an": 3}, {"nume": "RL", "an": 3}, {"nume": "APD", "an": 2}], "cunostinte": ["Java", "C++", "SQL"] },
    {"student": { "firstname": "Grigore", "lastname": "Ionescu"}, "an": 4, "grupa": "342C2", "materii": [{"nume": "BD2", "an": 4}, {"nume": "VLSI", "an": 4}, {"nume": "SRIC", "an": 4}, {"nume": "SO", "an": 3}], "cunostinte": ["C", "Python", "SQL", "Ruby"] },
    {"student": { "firstname": "Andrei", "lastname": "Popescu"}, "an": 3, "grupa": "332CA", "materii": [{"nume": "CN1", "an": 2}, {"nume": "CN2", "an": 2}, {"nume": "RL", "an": 3}], "cunostinte": ["C", "Python", "SQL", "Ruby"] },
    {"student": { "firstname": "Ana", "lastname": "Georgescu"}, "an": 4, "grupa": "342C5", "materii": [{"nume": "BD2", "an": 4}, {"nume": "UBD", "an": 4}, {"nume": "SRIC", "an": 4}, {"nume": "SO", "an": 3}], "cunostinte": ["C", "Python", "SQL", "Ruby"], "sef": true },
]
 
db.students.insert(studs)

Ex. 4. Inserați în baza de date faculty în colecția students folosind structura JSON de mai sus doi colegi și pe dumneavoastră.

**mongoimport**

Pentru a insera un fișier în format CSV sau JSON se folosește comanda mongoimport. Parametrii comenzii sunt:

  • --host=<hostname><:port> - specifică numele host-ului și portul. Dacă nu se specifică se vor lua valorile implicite
  • --port=<port> - specifică portul. Dacă nu se specifică se va lua valoarea implicită
  • --db=<databasename> - specifică numele bazei de date unde se vor insera datele
  • --collection=<collection> - specifică numele colecției care va stoca datele
  • --fields=<field1[,field2]> - specifică câmpurile din fișier care for fi inserate în colecție
  • --columnsHaveTypes - se utilizează dacă se dorește specificarea tipurilor de date pentru coloanele din lista de coloane specificată cu opțiunea --fields
  • --type=<json|csv|tsv> - specifică formatul fișierului cu date
  • --file=<filename> - specifică numele fișierului cu date
mongoimport --host=192.100.10.2 --port=28018 --db=BD --collection=students --type=json --file=data_dump.json
 
mongoimport --host=192.100.10.2 --port=28018 --db=BD --collection=students --columnsHaveTypes --fields="name.string(),birthdate.date(2006-01-02),contacted.boolean(),followerCount.int32(),thumbnail.binary(base64)" --type=csv --file=data_dump.csv

Ex. 5. Să se insereze fișierul bd2_mongo care conține baza de date în format JSON. Noua bază de date se va numi BD2 și colecția se va numi documents.

Selectarea datelor

Select

În MongoDB operația de selecție a datelor este find() (Documentație).

Sintaxa comenzii este:

db.collection.find(query, projection)

Unde

  • query reprezintă lista de filtre
  • projection reprezintă câmpurile care vor fi afișate

Dacă parametrul query lipsește, atunci se selectează toate documentele. Dacă parametrul projection lipsește atunci se afișează toate câmpurile documentului.

Comanda find() face paginație, arătând în consolă 10 înregistrări.

Să se selecteze toți studenții din colecția students, baza de date faculty

db.students.find()

  • Dacă se dorește un întoarcerea unei singur document se folosește findOne()
db.students.findOne()
  • Dacă se dorește un întoarcerea unui număr n de documente se folosește limit(n)
db.students.find().limit(3)
  • Dacă se dorește sortarea rezultatelor după valoare unui anumit câmp se folosește sort({label_1: order, label_2: order,…, label_n:order}) unde order este 1 pentru ascendent și -1 pentru descendent.
db.students.find().sort({"grupa": 1})
  • Dacă se dorește afișarea într-un mod citibil în consolă se folosește pretty()
db.students.find().pretty()
  • pretty, limit și sort se pot folosi împreună
db.students.find().sort({"student.firstname": 1}).limit(3).pretty()

Ex. 6. Să se selecteze primele 4 rezultate ale unei cereri care întoarce toți studenți ordonați descrescător după nume.

Filtrarea cererilor

Pentru a filtra rezultatele se folosește parametru query.

Pentru comparație avem:

  • : - egalitate
  • $ne - diferit de (not equal)
  • $gt - mai mare
  • $gte - mai mare sau egal
  • $lt - mai mic
  • $lte - mai mic sau egal
  • $in - căutare într-o listă
  • $all - căutare cu egalitate pe toate elementele dintr-o listă
db.students.find({"student.firstname": "Ion"})
db.students.find({an: {$gte: 4}})
db.students.find({"materii.nume": {$in: ['SCAD', 'IA']} })
db.students.find({"grupa": /^341/ })
db.students.find({"cunostinte": {$all: [/^J/, /^C/ ]}})

Operatori logici

  • $or - sau logic
  • $and - și logic
  • $not - negare logic
db.students.find({$or:  [{"student.lastname": "Ionescu"}, {"cunostinte": /^C/}]})
db.students.find({$and: [{"student.lastname": "Ionescu"}, {"cunostinte": /^C/}]})

Operatori utili pentru vectori

  • $size pentru a verifica dimensiune
  • 0, 1, 2,… pentru poziționare (indexare vectorilor începe de la 0)
db.students.find({"cunostinte": {$size: 2}})
db.students.find({"cunostinte.2": "SQL"})
db.students.find({"materii.0.nume": "BD2"})

Pentru a verifica existența unui câmp se folosește $exists

db.students.find({"sef": true})
db.students.find({"sef": {$exists: true}})
db.students.find({"sef": false})
db.students.find({"sef": {$exists: false}})

Acești operatori pot să fie folosiți împreună.

Ex. 7. Explicați ce fac cererile din acest subcapitol.

Ex. 8. Să se selecteze toți studenții din anul 4 care au restanțe și știu limbajele de programare C și SQL.

Proiecția

Pentru a proiecta doar anumite câmpuri se se folosește parametrul projection. În cazul în care nu se folosește filtrare se pun acolade fără goale pentru query.

db.students.find({}, {"student.firstname": 1})

O cerere cu projection va întoarce doar câmpurile specificate și _id. Dacă se dorește să se elimine câmpul _id atunci proiecția trebuie să conțină și “_id”: 0

db.students.find({}, {"_id": 0, "student.firstname": 1})

Ex. 9. Să se selecteze studenții care nu sunt șefi și au cunoștințe de “Python” pe a doua poziție din vectorul de cunoștințe. Afișați doar numele, prenumele și vectorul de cunoștințe.

Modificarea datelor

Pentru a modifica datele se poate folosi una din comenzile:

  • db.collection.update(<query>, <update>, <options>)​ (Documentație)

Unde:

  • query - filtrează datele
  • update - setează date care trebuie modificate
  • options - diferite opțiuni, e.g.:
    • upsert: <boolean> - crează un nou document în cazul în care nu se găsește nici un document cu filtrul
    • multi: <boolean> - modifică mai multe documente

Pentru a seta o valoare se folosește $set:

db.students.update({"student.firstname": "George"}, {$set: {"grupa": "342C1"}})
db.students.update({an: 4, "student.firstname": "Grigore"}, {$set: {cunostinte: ["C++"]}})

Folosind set se suprascrie valoarea pentru câmp. În cazul în care dorim sa modificăm o structură de tip vector folosiți $push. Dacă se dorește să se adauge mai multe valori, se poate folosi $push împreună cu $each.

db.students.update({an: 4, "student.firstname": "Grigore"}, {$push: {"cunostinte": "C"}})
db.students.update({an: 4, "student.firstname": "Grigore"}, {$push: {"cunostinte":{$each: ["Python", "SQL", "Ruby"]}}})

Dacă se dorește să se modifice valoare unui element dintr-o listă sau a unui câmp dintr-un document imbricat care se găsește în listă se poate folosi $. Trebuie să selectați și valoare/câmpul care se dorește să fie modificat în query:

db.students.update({"student.firstname": "Grigore", "cunostinte": "C"}, {$set: {"cunostinte.$": "Java"}})
db.students.update({"student.firstname": "Grigore", "materii.nume": "SRIC"}, {$set: {"materii.$.nume": "SPG"}})
db.students.update({"student.firstname": "Grigore", "materii.nume": "SPG"}, {$set: {"materii.$.nume": "SRIC", "materii.$.an":4}})

Ex. 10. Să se adauge la toți studenți de anul 3 materiile EGC și LFA (nume si an) folosind o singură comandă.

Alte funcții care modifică datele sunt:

  • db.collection.updateOne(<query>, <update>, <options>)​
  • db.collection.updateMany(<query>, <update>, <options>)​
  • db.collection.updateMany(<query>, <update>, <options>)​

Ștergerea datelor

Pentru a șterge un document dintr-o colecție se folosește comanda:

Unde

  • query - filtrează datele
  • justOne - boolean pentru a șterge unul (true) sau mai multe (false – implicit) documente
 db.students.remove({"an": {$lt: 4}})

Pentru a șterge toate documentele se folosește db.collection.remove({})

db.students.remove({})

Alte comenzi care pot fi utilizare pentru a șterge documente dintr-o colecție sunt:

  • db.collection.deleteOne(<filter>)​
  • db.collection.deleteMany(<filter>)​

Ex. 11. Să se șteargă doar un student care are materia BD2.

Indexare

MongoDB suport diferite tipuri de indecși. Indecși sunt utilizați pentru a crește performanțele operațiilor de căutare. Indecși sunt creați la nivel de colecție. Pentru a crea un index se folosește comanda createIndex(). La creare se specifică numele câmpului (i.e., label) și modul de ordonare a indexului (i.e., order)

  • Indecși simpli db.collection.createIndex({“label”: order})
  • Indecși compusi db.collection.createIndex({“label_1”: order_1, “label_2”: order_2, …})
  • Indecși pe documente imbricate: db.collection.createIndex({“label_1.nested_label”: order_1, “label_2.nested_label”: order_2, …})
  • Indecși geo-spațiali db.collection.createIndex({“label_1.nested_label”: “2d”})
  • Indecși textuali (o colecție poate să aibă un singur index textual, dar acesta poate fi compus)db.collection.createIndex({“label_1”: “text”, “label_2”: “text”, …})
  • Indecși hashed (utili pentru sharding): db.collection.createIndex({“label”: “hashed”})

Pentru următoarele exemple conectați-vă la baza de date BD2 creată anterior cu mongoinport.

Ex. 12. Analizați schema documentelor și înțelegeți structura

db.documents.createIndex({author: 1})
db.documents.createIndex({gender: 1, age: 1})
db.documents.createIndex({"words.word": 1})
db.documents.createIndex({"geoLocation": "2d"})
db.documents.createIndex({"lemmaText": "text"}) 
db.documents.createIndex({"date": "hashed"})

Ex. 13. Descrieți tipul indecșilor construiți anterior.

Pentru a vedea toți indecși se folosește comanda db.collection.getIndexes()

Pentru a șterge un index se folosește comanda db.collection.dropIndexes(“index_name”)

Pentru a șterge toți indecși (cu excepția indexului de pe cheia primară) se folosește comanda db.collection.dropIndexes()

Utilizare indecși geo-spațiali

  • Pentru a găsi documentele de lângă un punct p=[x, y] se folosește $near
  • Pentru a găsi documentele care se găsesc în diferite figuri geometrice se folosește $within:
    • Pentru un cerc cu centrul într-un punct p=[x, y] și o rază r se folosește $center
    • Pentru un dreptunghi definit prin punctele din stânga jos și dreapta sus se folosește $box
    • Pentru un poligon dat de o listă puncte se folosește $polygon
db.documents.find( { geoLocation: { $near: [25, 25] } }, {geoLocation: 1} )
db.documents.find( { geoLocation: { $within: { $center: [[25, 10], 3] } } }, {geoLocation: 1} )
db.documents.find( { geoLocation: { $within: { $box: [[25, 40], [30, 45]] } } }, { geoLocation: 1 } )
db.documents.find( { geoLocation: { $within: { $polygon: [[20, 40], [40, 50], [30, 30]] } } },  { geoLocation: 1 } )

Utilizare indecși textuali

Pentur a utiliza un index textual se folosește următoare sintaxă:

{
  $text:
    {
      $search: <string>,
      $language: <string>,
      $caseSensitive: <boolean>,
      $diacriticSensitive: <boolean>
    }
}

Unde

  • $search - este șirul de cuvinte căutat sau fraza căutată
  • $language - este limba pentru cuvintele căutate
  • $caseSensitive - dacă se caută case sensitive sau nu
  • $diacriticSensitive - dacă se ține cont de diacritice sau nu
# Caută un cuvânt
db.documents.find( { $text: { $search: "coffee", $language: "english", $caseSensitive: false, $diacriticSensitive: false } } ,  { lemmaText: 1 } )
 
# Caută mai multe cuvinte (se folosește operatorul or)
db.documents.find( { $text: { $search: "coffee cup", $language: "english", $caseSensitive: false, $diacriticSensitive: false } } ,  { lemmaText: 1 } )
 
# Caută o frază
db.documents.find( { $text: { $search: "\"heaven tonight\"", $language: "english", $caseSensitive: false, $diacriticSensitive: false } } ,  { lemmaText: 1 } )
 
# Caută o frază cu un scor de relevanță
db.documents.find( { $text: { $search: "\"heaven tonight\"", $language: "english", $caseSensitive: false, $diacriticSensitive: false } } ,  { score: { $meta: "textScore" }, lemmaText: 1, _id: 0})

Ex. 14. Să se găsească toate documentele care se află într-un dreptunghi dat de punctele [20, -100] și [40, 90], care să conțină cuvintele tech și engineering. Cererea să se folosească de indexul textual. Afișeți câmpul rawText. Să se refacă cererea utilizând alt câmp din document.

Funcții

MongoDB permite dezvoltatorilor sa scrie funcții folosind limbajul JavaScript.

Ex. 15. Să se scrie o funcție care primește un query pentru filtrare și împarte lemmaText în cuvinte. Funcția va întoarce un vector.

tokenization = function(q){
    var cursor = db.documents.find(q,{"_id": 0, lemmaText: 1});
    var tokens = Array();
    cursor.forEach(function(elem){
        tokens = tokens.concat(elem["lemmaText"].split(" "));
    });
    return tokens;
}
 
// utilizate
var q = {gender: "male"}
tokens = tokenization(q)

Ex. 16. Să se scrie o funcție numită countWords care primește un query pentru filtrare și numără aparițiile unui cuvânt. Funcția va întoarce un obiect de forma {word_1: count, word_2: count, …}. Folosiți funcția tokenization.

Funcții stocate

Funcțiile pot fi stocate la nivelul MongoDB. Pentru acest lucru se folosește o colecție specială numită system.js. Stocarea se face cu comanda: db.system.js.save(_id: “function_name”, value: “function_body”). Pentru a lucra cu funcțiile stocare (a le încărca în sesiune) se folosește comanda db.loadServerScripts().

db.system.js.save(
   {
     _id: "tokenization",
     value : function(q){
                var cursor = db.documents.find(q,{"_id": 0, lemmaText: 1});
                var tokens = Array();
                cursor.forEach(function(elem){
                    tokens = tokens.concat(elem["lemmaText"].split(" "));
                });
                return tokens;
            }
   }
)
 
db.loadServerScripts()

Ex. 17. Salvați și apoi încărcați în sesiune funcția countWords.

Agregarea datelor

În MongoDB putem să agregăm datele folosind mai multe metode:

  • Funcții de agregare
  • Modulul nativ de Aggregation Pipeline
  • MapReduce

Funcții de agregare

MongoDB oferă utilizatorilor două funcții pentru agregare:

1. Count: db.collection.count(query)

// Cate documente au lungimea textului lematizat 10
db.documents.count({lemmaTextLength: 10})

2. Distinct: db.collection.distinct(field, query)​

// afiseaz varstele distincte
db.documents.distinct("age")
 
// afiseaz varstele distincte pentru female
db.documents.distinct("age", {gender: "female"}).sort()

Ex. 18. Afișați toate cuvintele distincte. Folosiți câmpul words.

Aggregation Pipeline

Pentru agregare, MongoDB oferă și Aggregation Pipeline (Documentație).

db.collection.aggregate(
    { $match: <query> },
    { $unwind: <array> },
    { $project: <projection>},
    { $group: <aggregation_group, aggregation_functions> },
    { $sort: <sorting_fields> },
)

Unde:

  • $match - filtrează documentele de intrare după un **query**
  * $unwind - procesează elementele unui vector
  • $project - proiecția
  * $group - face grupuri și a aplica funcții agregare
  • $sort - reordonează documentele de intrare după o cheie

<note important>Contează ordinea în care sunt folosiți operatorii.</note>

<color red>Ex. 19.</color> Să se utilizeze Aggregation Pipeline pentru a calcula numărul de apariții ale cuvintelor.

<code javascript>
db.documents.aggregate([
    { $match: q },

{ $project: { words: { $split: [“$lemmaText", " "]}}},
    { $unwind: “$words" },
    { $group: { _id: “$words", counts: { $sum: 1 } } } ]) </code>

Ex. 20. Să se utilizeze Aggregation Pipeline pentru a calcula numărul de apariții ale cuvintelor folosind coloana words.

MapReduce

MongoDB oferă un framework de MapReduce. Pentru a utiliza acest framework trebuie să scriem două funcții,i.e., Map și Reduce. Pentru a utilza funcțiile pe o colecție se folosește mapReduce (Documentație):

 
db.collection.mapReduce(
    mapFunction, 
    reduceFunction, 
    {
        finalize: <function>,
        out: <output>,
        query: <document>,
        sort: <document>,
        limit: <number>,
        scope: <document>
    }
)

Unde:

  • mapFunction - funcția de map
  • reduceFunction - funcția de reduce
  • finalize - (opțional) funcție pentru finalizare, se aplică după ce se termină procesul și datele sunt agregate
  • out - (opțional) colecția unde va fi salvat rezultatul
  • query - (opțional) filtru pentru documentele de intrare
  • sort - (opțional) ordonează documentele de intrare după cheie
  • limit - (opțional) limitează numărul de documente de intrare
  • scope - (opțional) variabile din exterior care pot fi folosite în funcțiile map, reduce și finalize

Comanda mapReduce este o mapare peste comanda mapReduce din MongoDB (Documentație)

db.runCommand(
    {
        mapReduce: <collection>,
        map: <function>,
        reduce: <function>,
        finalize: <function>,
        out: <output>,
        query: <document>,
        sort: <document>,
        limit: <number>,
        scope: <document>
    }
)

Ex. 21. Să se utilizeze MapReduce pentru a calcula numărul de apariții ale cuvintelor.

mapFunction = function() {
    var tokens = this.lemmaText.split(" ");
    for (var idx=0; idx<tokens.length; idx++){
        emit(tokens[idx], 1);
    }
}
 
reduceFunction = function(key, values) {
    return Array.sum(values);
};
 
var q = {"gender": "female"}       
db.documents.mapReduce(mapFunction, reduceFunction, {query: q, out: "wordCounts"});
 
db.wordCounts.count()
 
db.wordCounts.find({"_id": "0"})
 
db.wordCounts.drop()

Ex. 22. Să se utilizeze MapReduce pentru a calcula numărul de apariții ale cuvintelor folosind câmpul words.

bd2/laboratoare/11.1575647462.txt.gz · Last modified: 2019/12/06 17:51 by ciprian.truica
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0