NOSQL MongoDB Origini MongoDB deriva il suo nome dalla parola "huMONGOus”, che significa enorme. E' attualmente il il database NoSQL più diffuso*. Sviluppato inizialmente dalla società di software 10gen (ora MongoDB Inc.) nel 2007 come un componente di un altro prodotto. Nel 2009 viene rilasciato in open-source come database a se stante. *Fonte del dato della diffusione dei DB: http://db-engines.com/en/ranking MongoDB Datamodel MongoDB è un document store: ogni record viene memorizzato come un documento; esso può avere un numero arbitrario di campi aventi una qualsiasi lunghezza. Rispetto al modello relazionale: si accorpano quanto più possibile gli oggetti, creando delle macro entità dal massimo contenuto informativo. MongoDB non possiede uno schema e ogni documento non è strutturato, ha solo una chiave obbligatoria: _id , la quale è utilizzata per identificare univocamente il documento. MongoDB BSON e JSON Il formato utilizzato per il salvataggio e la trasmissione dei document in MongoDB è il BSON (JSON Binario). Il formato JSON (JavaScript Object Notation) è largamente diffuso nel mondo web, esso è un formato di testo completamente indipendente dal linguaggio di programmazione utilizzato ed è basato su due strutture: - un insieme di coppie nome/valore; - un elenco ordinato di valori. MongoDB Esempio di JSON { "_id" : 1, "name" : { "first" : "John", "last" : "Backus" }, "contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ], "awards" : [ { "award" : "W.W. McDowell Award", "year" : 1967 }, { "award" : "Draper Prize", "year" : 1993 } ] } MongoDB BSON e JSON II formato BSON estende il modello JSON allo scopo di aggiungere ulteriori tipi di dati (ad esempio il formato data e il byte array) e garantire una maggiore efficienza nell'interazione tra diversi linguaggi di programmazione. La dimensione massima di un singolo documento BSON è di 16MB. Ad ogni document è associato un _id che agisce come object identifier ed è composto da 12 byte come segue: MongoDB BSON e GridFS La dimensione del BSON potrebbe essere limitante. Si può utilizzare la modalità in GridFS che, invece che effettuare un'unica memorizzazione, divide i file in più parti (chunk) di dimensione prestabilita e li memorizza come document separati. Il limite di default di un chunk è 255kB. Vengono utilizzate due collection per memorizzare i file: - una per i chunk; - una per i metadati. Sarà compito del driver o del client riassemblare i chunk necessari. MongoDB Datamodel In MongoDB il document è l'unità di base del database ed equivale ad una riga dei database relazionali. La tabella seguente riassume le “equivalenze” di termini fra MongoDB e gli RDBMS: MongoDB RDBMS Database Database Collection Tabella Document Record Field Colonna MongoDB Datamodel Database Collection Collection Collection Document Document _id Field1 Field2 Document Document _id Field1 Field3 Field4 Architettura MongoDB Architettura Base MongoDB è organizzato secondo una architettura base Client/Server. Il client può essere: - shell mongo (implementata in JavaScript) - un applicativo scritto in diversi linguaggi di programmazione mongod client Oltre a questa configurazione base è possibile incrementare la ridondanza e la disponibilità del dato attraverso la configurazione di un “replica set” MongoDB Architettura con Replica Set Un replica set è un gruppo di istanze mongod che gestiscono lo stesso insieme di dati. Tutte Primary le modifiche che vengono effettuate unicamente sul primary, vengono scritte in un log che viene poi distribuito in maniera asincrona ai Secondary Secondary Replica set client secondary i quali poi replicano le modifiche sui loro dati. I client leggono i dati dal primary, ma possono essere configurati anche per leggere i dati dai secondary. Il dato restituito da un secondary potrebbe non essere aggiornato. MongoDB Architettura con Replica Set Nel caso in cui il server primario non Lettura fosse più disponibile un meccanismo Scrittura eleggere un nuovo server primario fra Driver di automatic election provvede ad Mongod: Primary i secondari. È possibile prevedere Lettura Mongod: Secondary Lettura Mongod: Secondary un'ulteriore istanza di mongod, avente il ruolo di votazione durante l'elezione del nuovo Mongod: Primary Driver arbiter, che viene utilizzato solo per la primary. Repliche asincrone Lettura Mongod: Primary Scrittura Lettura Mongod: Secondary Replica asincrona MongoDB Introduzione Architettura Sharding L'architettura appena vista presenta potenziali problemi: - all'aumentare del numero di query è possibile che si possa esaurire la capacità di CPU del server; - all'aumentare delle dimensioni dei dati c'è il rischio di superare la capacità del disco di una singola macchina; - dati di lavoro di dimensioni superiori alla RAM del sistema potrebbero “stressare” la velocità di I / O dei dischi . Possibili soluzioni: - scalare verticalmente (aumento CPU, HD e ram); - scalare orizzontalmente (sharding). MongoDB Architettura Sharding MongoDB può scalare orizzontalmente in maniera lineare. L'architettura Sharding è costituita da tre componenti: - Shards: immagazzinano i dati, per garantire la consistenza e la disponibilità dei dati sono spesso costituiti da dei Replica Sets; - Query Routers o istanze mongos: interfacce che ridirezionano le richieste verso i shards corretti e restituiscono il risultato ai vari client. - Config servers: contengono i metadati che descrivono il mapping tra dati e shards. MongoDB Architettura Sharding Mongod: Primary Mongod: Primary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Shard 1 Shard N Config Config mongos mongos Config client MongoDB Architettura Sharding Il partizionamento ed il bilanciamento è automatico in base alla Shard Key (un field del document) che si decide di utilizzare. È poi possibile decidere, per la suddivisione dei dati in chunks, se utilizzare: Range Based Sharding Hash Based Sharding mongod Key-range (0...99) mongod Key-range (0...33) mongod Chunk 1 Hash Function Key-range (67...99) mongod Chunk N mongod Chunk 1 mongod Chunk N Query Language MongoDB Datatype Datatype Descrizione Boolean Tipo booleano true/false Arrays Utilizzato per memorizzare array o liste di valori Integer Valori numerici (32-bit o 64-bit a seconda del server. Double Valori numerici in virgola mobile String Stringa in formato UTF-8 Symbol Come una stringa, ma utilizzato nei linguaggi che usano il tipo symbol (es. Ruby) Regular expression Utilizzato per memorizzare espressioni regolari Date Memorizza il tempo e la data in formato Unix. Timestamp Memorizza un Timestamp Binary data Memorizza dati in formato binario Code Utilizzato per memorizzare codice Javascript nel document Null Memorizza un valore null Object ID Memorizza l'ID del document Object Utilizzato per gli embedded document Min/Max key Questi vengono usati solo internamente e servono per comparare al valore minimo e massimo di un dato BSON MongoDB Query Language – Uso dei database Per selezionare un database: > use DATABASE_NAME Questo comando seleziona il database e, qualora non esista, lo crea. Per vedere quali database sono presenti all'interno di un server possiamo usare il comando > show dbs local 0.78125GB test 0.23012GB vengono mostrati tutti i database sui quali è stato fatto almeno un insert e la dimensione che occupano. MongoDB Query Language – Uso dei database Per sapere quale database è stato selezionato si usa il comando: > db viene restituito il nome del database in uso. Per eliminare un database occorre selezionarlo attraverso use e poi dare il comando: > db.dropDatabase() se per caso non viene selezionato nessun database e viene dato il comando db.dropDatabase viene eliminato il database di default. MongoDB Query Language – Collection Una volta che abbiamo creato il database possiamo creare le collection al suo interno. Possono essere usati due metodi: - il primo prevede una creazione esplicita > db.createCollection(name, options) - il secondo prevede una creazione implicita > db.users.insert(document) Per visualizzare tutte le collection presenti in un database si usa il comando: > show collections MongoDB Query Language - Insert Con il comando insert possiamo inserire un document all'interno di una collection. > db.docs.insert({ _id: ObjectId(7df78ad8902c), title: 'Dispense MongoDB', description: 'Dispense corso DASSIA su MongoDB', by: 'CRS4', url: 'http://dassia.crs4.it/', tags: ['mongodb', 'database', 'NoSQL'] }) Se l'_id non viene specificato questo viene assegnato in maniera automatica. MongoDB Query Language - Find Il metodo “base” per effettuare le query è find: restituisce un cursore ai dati corrispondenti e supporta sia criteri per la query che le proiezioni dei dati. Possono poi essere applicati dei modificatori al cursore. db.users.find( MongoDB { age: {$gt: 18 } }, ← criteri di query { name : 1, address: 1} ← proiezione ) . limit ( 5 ) SQL ← collection ← modificatore di cursore SELECT _id, name, address ← proiezione FROM users ← tabella WHERE age > 18 ← criteri di selezione LIMIT 5 ← modificatore di cursore MongoDB Query Language - Find L'uso del metodo find è il seguente > db.COLLECTION_NAME.find({QueryCriteria},{Projection}) dove i QueryCriteria e la Projection sono opzionali. Vanno racchiusi fra parentesi grafe e possono essere più di uno: eventuali criteri di selezione vanno indicati singolarmente. Se si vuole effettuare una proiezione senza criteri di selezione: > db.COLLECTION_NAME.find({},{Projection}) Se non si ha nessuna proiezione da fare, ma solo delle selezioni la sintassi è > db.COLLECTION_NAME.find({QueryCriteria}) MongoDB Find – Query Criteria Criterio Sintassi Esempio Uguaglianza {<key>:<value>} db.users.find({“name” : “Nicola” }) Minore di {<key>:{$lt:<value>}} db.users.find({ age : {$lt:25}}) Minore o uguale di {<key>:{$lte:<value>}} db.users.find({ age : {$lte:25}}) Maggiore di {<key>:{$gt:<value>}} db.users.find({ age : {$gt:18}}) Maggiore o uguale di {<key>:{$gte:<value>}} db.users.find({ age : {$gte:19}}) Non uguale {<key>:{$ne:<value>}} db.users.find({ age : {$ne:45}}) MongoDB Find – Query Criteria Il metodo find supporta criteri multipli in AND, OR o combinazione di questi: AND (tutti gli utenti di 18 anni che si chiamano Nicola) > db.users.find({ age: 18, name: 'Nicola'}) OR (tutti gli utenti di 18 anni e tutti gli utenti che si chiamano Nicola) > db.users.find({ $or: [{age: 18}, {name: 'Nicola'}] }) AND e OR (tutti gli utenti di 18 anni e che contemporaneamente si chiamano Nicola o vivono a Cagliari) > db.users.find({ age: 18, $or:[ {name: 'Nicola'}, {town: 'Cagliari'} ] }) MongoDB Find – Projection I criteri di proiezione servono ad indicare quali attributi del documento restituire. db.users.find( { age: 18 } , { name: 1} ) { _id: 1, age: 18, name: 'Almo' } { _id: 2, age: 28, name: 'Asia' } projection query criteria { _id: 3, age: 21, name: 'Giada' } { _id: 1, age: 18, name: 'Almo' } { _id: 1, name: 'Almo' } { _id: 4, age: 38, name: 'Ugo' } { _id: 5, age: 18, name: 'Sofia' } { _id: 5, name: 'Sofia' } { _id: 5, age: 18, name: 'Sofia' } { _id: 6, age: 58, name: 'Guido' } Bisogna notare che _id viene sempre restituito, se non lo si vuole visualizzare va messo a zero. db.users.find( { age: 18 } , { name: 1, _id : 0} ) MongoDB Cursor Modifier: Limit e Skip Il cursore ai dati restituito da un find può essere modificato mediante l'utilizzo di un cursor modifier. Abbiamo quindi: limit che viene utilizzato per restituire i primi N documenti trovati. > db.COLLECTION_NAME.find().limit(N) skip che permette di “saltare” i primi M documenti trovati. > db.COLLECTION_NAME.find().skip(M) Ad esempio > db.users.find({},{name:1,_id:0}).limit(1).skip(2) restituisce il nome del terzo documento. MongoDB Sort e Distinct sort che esegue l'ordinamento per la chiave specificata ascendente (1) o discendente (-1) > db.COLLECTION_NAME.find().sort({key:1}) distinct se volessi trovare tutti i valori diversi presenti per uno o più field in una collection > db.COLLECTION_NAME.distinct('key') Ad esempio, la seguente trova tutti i nomi presenti nella collection > db.users.distinct('name') mentre la seguente restituisce tutti i nomi distinti dei maggiorenni presenti nella collection > db.users.distinct('name',{age:{$gte:18}}) MongoDB Count Per contare il numero di documenti può essere usato il metodo count() sia in questo modo: > db.COLLECTION_NAME.count() che > db.COLLECTION_NAME.find().count() Se vogliamo specificare dei criteri di query, ricorrendo alla programmazione funzionale, abbiamo: db.users.count( { age: { $gt: 30 } }) oppure db.users.find( { age: { $gt: 30 } }).count() MongoDB Update e Save Per aggiornare un document si hanno due metodi a disposizione: - update: aggiorna i field che vengono indicati nel document; - save: rimpiazza il document esistente con uno nuovo. Ad esempio abbiamo: > db.users.update({name: 'Pino'}, {$set:{name: 'Ugo'}}) > db.users.save({_id:ObjectId(),NEW_DATA}) MongoDB Remove Document Per rimuovere un document si utilizza remove. > db.COLLECTION_NAME.remove(DELETION_CRITERIA) Ad esempio > db.users.remove({name: 'Maurizio'}) Mentre la sintassi per rimuovere il primo document dove compare il criterio è: > db.COLLECTION_NAME.remove(DELETION_CRITERIA, 1) Per rimuovere tutti i document da una collection devo usare > db.COLLECTION_NAME.remove() MongoDB Indici Secondari Tutti i documenti sono indicizzati per la loro chiave primaria _id. Ciò significa che utilizzando il metodo find con alcuni criteri di ricerca controlleremo tutti i documenti nella loro interezza. Per snellire queste operazioni si possono creare degli indici secondari tramite il comando ensureIndex. > db.COLLECTION_NAME.ensureIndex(keys,options) Posso ad esempio specificare uno o più field sui quali generare gli indici sia in maniera ascendente (1) che discendente (-1). > db.users.ensureIndex({'name' : 1, 'age' : -1}) MongoDB Aggregazione In MongoDB è possibile processare diversi dati per restituire dati aggregati o computazioni sugli stessi. > db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATIONS) È presente il concetto di pipeline nell'aggregazione: possiamo definire una pipeline di stage consecutivi per ridurre e filtrare i dati all'interno dell'aggregazione. Ad esempio abbiamo gli stage per: - $match: filtra dei dati in base a criteri assegnati - $group: raggruppa i dati in base a criteri assegnati - $unwind: separa un array del documento creando singoli documenti - $projection: proietta i dati in base a criteri assegnati - $sort: ordina i dati in base a criteri assegnati - $limit: prende in esame solo i primi N document - $skip: salta i primi N document MongoDB Aggregazione – Group All'interno di $group possiamo utilizzare diverse espressioni per ottenere dati aggregati o particolari computazioni sugli stessi: $sum $avg $min $max $first $last $push $addtoset Ad esempio: db.users.aggregate( [ { $group:{_id:'$name', count_omonimi:{$sum:1} } } ]) db.users.aggregate( [ { $group:{_id:null, eta_min:{$min:'$age'} } } ]) db.users.aggregate( [ { $group:{_id:'$name', eta:{$push:'$age'} } } ]) db.users.aggregate( [ { $group:{_id:'$name', eta:{$addToSet:'$age'} } } ]) MongoDB Aggregazione – Esempio db.orders.aggregate( [ { $match: {status: 'A' } }, { $group: {_id: '$cust_id', total: { $sum: '$amount' } } } ] ) { cust_id: 'A123', amount: 500, status: 'A' { cust_id: 'A123', amount: 250, status: 'A' } } orders amount: 500, status: 'A' } } { cust_id: 'A123', } { cust_id: 'B212', amount: 200, status: 'A' { cust_id: 'A123', amount: 300, status: 'D' $match amount: 250, status: 'A' { cust_id: 'B212', amount: 200, status: 'A' { cust_id: 'A123', } $group { _id: 'A123', total: 750 } { _id: 'B212', total: 200 } } results MongoDB Funzionalità Avanzate – MapReduce In MongoDB è presente una implementazione interna del Map-Reduce per la realizzazione di operazioni di aggregazioni complesse: si possono definire le operazioni di map e di reduce creando delle funzioni in linguaggio JavaScript. db.runCommand( { opzionali ) } mapReduce: <collection>, map: <function>, reduce: <function>, finalize: <function>, out: <output>, query: <document>, sort: <document>, limit: <number>, scope: <document>, jsMode: <boolean>, verbose: <boolean> JavaScript function MongoDB Funzionalità Avanzate – MapReduce db.orders.mapReduce( function( ) { emit ( this.cust_id, this.amount ); }, function( key, values ) { return Array.sum( values ) }, { query: { status: 'A' }, out: 'order_totals' }) { cust_id: 'A123', amount: 500, status: 'A' { cust_id: 'A123', amount: 250, status: 'A' } } orders amount: 500, status: 'A' } } { cust_id: “A123”, } { cust_id: “B212”, amount: 200, status: 'A' { cust_id: 'A123', amount: 300, status: 'D' query amount: 250, status: 'A' { cust_id: 'B212', amount: 200, status: 'A' { cust_id: 'A123', } } reduce map { 'A123': [500, 250] } { 'B212': 200 } { _id: 'A123', total: 750 } { _id: 'B212', total: 200 } order_totals MongoDB Funzionalità Avanzate – MapReduce 1 definisce una funzione di map, di reduce, client una query e una collection di output 2 request client mongos MongoDB Funzionalità Avanzate – MapReduce 3 Config mongos MR request Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Primary Mongod: Primary Shard N Mongod: Primary Shard N Shard N 4 query Mongod: Primary map MongoDB Funzionalità Avanzate – MapReduce 5 map <K,V> <k,v> <k,v> buffer 5a Mongod: Secondary <k,v> <k,v> <k,v> <k,v> <k,v> buffer partial reduce Mongod: Secondary Mongod: Primary Shard N MongoDB Funzionalità Avanzate – MapReduce 6 reduce reduce Mongod reduce Mongod Mongod 7 mongos Out Collection Request Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Secondary Mongod: Primary Mongod: Primary Shard N Mongod: Primary Shard N Shard N MongoDB Funzionalità Avanzate – MapReduce 8 Mongod: Secondary Mongod: Secondary reduce Mongod: Primary Shard X 8a Mongod: Primary finalize MongoDB Funzionalità Avanzate – MapReduce Il Map Reduce implementato all'interno di Mongo presenta una serie di limiti: - Javascript non è il linguaggio più indicato per il processamento di funzioni quali MapReduce - Javascript limita l'utilizzo di librerie di terze parti per l'elaborazione dei dati - Viene aggiunto un carico extra in termini di informazioni memorizzate - Solo l'architettura con i shards consente di parallelizzare i dati e solo a seconda della query che viene utilizzata Quando i dati crescono occorre passare a un tool pensato per la gestione dei JOB MapReduce MongoDB Connettore Hadoop Lo scopo di questo connettore è offrire la possibilità di usare MongoDB come sorgente di input o destinazione di output per i task di MapReduce sotto Hadoop. Permette di utilizzare nei tool dell'universo Hadoop, come Pig ed Hive, i documenti BSON/MongoDB. Generalmente il connettore MongoDB viene utilizzato come strato intermedio fra l'applicazione e Hadoop in modo da sfruttare le potenzialità del query language di MongoDB per filtrare i dati. MongoDB Connettore Hadoop – Aggregazione Batch MongoDB Connettore Hadoop – Data Warehouse MongoDB Connettore Hadoop – ETL MongoDB Connettore Hadoop – Casi d'uso Gestione dati e metadati Gestione dati, check Analisi utenti in, Analisi utenti, segmentazione, review personalizzazione Gestione dati hotel e Segmentazione hotel per prezzi migliorare le ricerche MongoDB Connettore Hadoop – Casi d'uso Gestione dati studenti Analisi studenti per l'apprendimento mirato Gestione dati Call center / Portali web Modellazione rischio, sicurezza e prevenzione truffe Analisi uso da parte degli utenti e ottimizzazione delle tariffe