This shows you the differences between two versions of the page.
bd2:laboratoare:12 [2019/12/07 13:35] ciprian.truica |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laboratorul 12 - MongoDB II ====== | ||
- | ===== Obiective ===== | ||
- | |||
- | * Indexare | ||
- | * Funcții | ||
- | * Agregarea datelor | ||
- | |||
- | ===== 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 să se importe documentele din fișierul {{:bd2:laboratoare:bd2_mongo.txt|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**. | ||
- | |||
- | <color red>Ex. 1.</color> Analizați schema documentelor și înțelegeți structura | ||
- | |||
- | <code javascript> | ||
- | 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"}) | ||
- | </code> | ||
- | |||
- | <color red>Ex. 2.</color> Descrieți tipul indecșilor construiți anterior. | ||
- | |||
- | <note important> | ||
- | 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()'' | ||
- | </note> | ||
- | |||
- | ==== 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'' | ||
- | |||
- | <code javascript> | ||
- | 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 } ) | ||
- | </code> | ||
- | |||
- | ==== Utilizare indecși textuali ==== | ||
- | |||
- | Pentur a utiliza un index textual se folosește următoare sintaxă: | ||
- | <code javascript> | ||
- | { | ||
- | $text: | ||
- | { | ||
- | $search: <string>, | ||
- | $language: <string>, | ||
- | $caseSensitive: <boolean>, | ||
- | $diacriticSensitive: <boolean> | ||
- | } | ||
- | } | ||
- | </code> | ||
- | |||
- | 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 | ||
- | |||
- | |||
- | <code javascript> | ||
- | # 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}) | ||
- | </code> | ||
- | |||
- | <color red>Ex. 3.</color> 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. | ||
- | |||
- | <color red>Ex. 4.</color> Să se scrie o funcție care primește un **query** pentru filtrare și împarte **lemmaText** în cuvinte. Funcția va întoarce un vector. | ||
- | |||
- | <code javascript> | ||
- | 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) | ||
- | |||
- | </code> | ||
- | |||
- | <color red>Ex. 5.</color> 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()''. | ||
- | |||
- | <code javascript> | ||
- | 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() | ||
- | </code> | ||
- | |||
- | <color red>Ex. 6.</color> 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)'' | ||
- | |||
- | <code javascript> | ||
- | // Cate documente au lungimea textului lematizat 10 | ||
- | db.documents.count({lemmaTextLength: 10}) | ||
- | </code> | ||
- | | ||
- | |||
- | 2. Distinct: db.collection.distinct(field, query) | ||
- | |||
- | <code javascript> | ||
- | // afiseaz varstele distincte | ||
- | db.documents.distinct("age") | ||
- | |||
- | // afiseaz varstele distincte pentru female | ||
- | db.documents.distinct("age", {gender: "female"}).sort() | ||
- | </code> | ||
- | | ||
- | <color red>Ex. 7.</color> Afișați toate cuvintele distincte. Folosiți câmpul **words**. | ||
- | |||
- | ==== Aggregation Pipeline ==== | ||
- | |||
- | Pentru agregare, MongoDB oferă și **Aggregation Pipeline** ([[https://docs.mongodb.com/manual/core/aggregation-pipeline/|Documentație]]). | ||
- | |||
- | <code javascript> | ||
- | db.collection.aggregate( | ||
- | { $match: <query> }, | ||
- | { $unwind: <array> }, | ||
- | { $project: <projection>}, | ||
- | { $group: <aggregation_group, aggregation_functions> }, | ||
- | { $sort: <sorting_fields> }, | ||
- | ) | ||
- | </code> | ||
- | |||
- | 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. 8.</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> | ||
- | |||
- | <color red>Ex. 9.</color> 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'' ([[https://docs.mongodb.com/manual/core/map-reduce/|Documentație]]): | ||
- | |||
- | |||
- | <code javascript> | ||
- | |||
- | db.collection.mapReduce( | ||
- | mapFunction, | ||
- | reduceFunction, | ||
- | { | ||
- | finalize: <function>, | ||
- | out: <output>, | ||
- | query: <document>, | ||
- | sort: <document>, | ||
- | limit: <number>, | ||
- | scope: <document> | ||
- | } | ||
- | ) | ||
- | </code> | ||
- | |||
- | 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 ([[https://docs.mongodb.com/manual/reference/command/mapReduce/|Documentație]]) | ||
- | |||
- | <code javascript> | ||
- | db.runCommand( | ||
- | { | ||
- | mapReduce: <collection>, | ||
- | map: <function>, | ||
- | reduce: <function>, | ||
- | finalize: <function>, | ||
- | out: <output>, | ||
- | query: <document>, | ||
- | sort: <document>, | ||
- | limit: <number>, | ||
- | scope: <document> | ||
- | } | ||
- | ) | ||
- | </code> | ||
- | |||
- | |||
- | <color red>Ex. 10.</color> Să se utilizeze MapReduce pentru a calcula numărul de apariții ale cuvintelor. | ||
- | |||
- | <code javascript> | ||
- | 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() | ||
- | </code> | ||
- | |||
- | <color red>Ex. 11.</color> Să se utilizeze MapReduce pentru a calcula numărul de apariții ale cuvintelor folosind câmpul **words**. |