NoSQL

Una base de dades NoSQL (sovint interpretat com a Not only SQL i significa 'No només SQL') proporciona un mecanisme per emmagatzemar i recuperar dades que es modela mitjançant relacions tabulars diferents a les utilitzades en les bases de dades relacionals. Aquest canvi de model està motivat per la simplicitat de disseny, l'escalabilitat horitzontal i un major control de la disponibilitat. Les estructures de dades utilitzades per les bases de dades NoSQL (p. ex. clau-valor, graf i document) difereixen de les relacionals, fet que possibilita que algunes operacions siguin més ràpides en les NoSQL que en les relacionals. L'elecció d'una o altra base de dades NoSQL depèn del problema a solucionar.

L'ús de bases de dades NoSQL ha augmentat notablement en sistemes Big Data i en aplicacions web a temps real. Als sistemes NoSQL se'ls anomena també "No només SQL" per emfatitzar que aquest tipus d'eines poden admetre llenguatges d'interrogació estructurats (SQL). Molts gestors NoSQL comprometen la consistència a favor de disponibilitat i la tolerància a la partició. Les reticències principals a adoptar sistemes NoSQL són l'ús de llenguatges d'interrogació de baix nivell, la manca d'interfícies estandarditzades i les grans inversions prèvies fetes en les bases de dades SQL existents. Gran part dels gestors NoSQL no compleixen completament les propietats ACID, i així doncs, no garanteixen transaccions realment fiables.

MongoDB

MongoDB és defineix com una base de dades documental. És programari lliure i està escrita en C++. Segurament és el principal gestor de bases de dades NoSQL del mercat.

Ens ofereix:

  • Alt rendiment: les dades es guarden sovint en el mateix (o molt similar) format amb que les manipulen els programes. A més, MongoDB ofereix un sistema d'indexació molt flexible per accelerar les consultes.
  • Alta disponibilitat: MongoDB ofereix mecanismes de replicació de les dades amb recuperació automàtica de caigudes i redundància de dades.
  • Escalat automàtic: ens ofereix un mecanisme de distribució de dades automatitzat (sharding) que ens permet que les nostres dades creixin sense limitacions.

A MongoDB, els registres es coneixen com a documents, o també com a objectes. Un document és bàsicament un conjunt de parells clau-valor, en format JSON (JavaScript Object Notation), un format lleuger per l'intercanvi de dades.

SQL vs MongoDB

A continuació es pot veure l'equivalència entre els conceptes utilitzats a SQL i MongoDB.

JSON i BSON

Exemple de JSON:

{
    "name" : "John Brown",
    "salary" : 350000,
    "type" : "FT",
    "address" : {
        "area" : "Noida",
        "city" : "Delhi"
    },
    "courses" : [ 
        {
            "name" : "java",
            "type" : "technical"
        }, 
        {
            "name" : "dotnet",
            "type" : "technical"
        }, 
        {
            "name" : "hadoop",
            "type" : "technical"
        }
    ]
}

JSON (acrònim de JavaScript Object Notation) és un estàndard obert basat en text dissenyat per a intercanvi de dades llegible per humans. Deriva del llenguatge script JavaScript, per a representar estructures de dades simples i llistes associatives, anomenades objectes. Malgrat la seva relació amb el JavaScript, té implementacions per a gran part dels llenguatges de programació. Douglas Crockford va especificar i popularitzar el format JSON,[1] i es descriu a l'[RFC:4627 RFC:4627]. El tipus MIME del JSON és application/json. L'extensió de fitxer és .json.

El format JSON s'utilitza habitualment per serialitzar i transmetre dades estructurades en una connexió de xarxa. S'utilitza principalment per intercanviar dades entre un servidor i una aplicació web, sent una alternativa a l'XML.

BSON / biːsən / és un format d'intercanvi d'informació informàtic utilitzat principalment com a format d'emmagatzematge de dades i de transferència de xarxa a la base de dades MongoDB. És una forma binària per representar estructures de dades simples, matrius associatives (anomenats objectes o documents en MongoDB), i diversos tipus de dades d'interès específic per a MongoDB. El nom "BSON" es basa en el terme JSON i significa "JSON binari".

Instal.lació

El manual es troba aquí, i la secció d'instal.lació aquí.

Pots descarregar el MongoDB Community Edition des de la pàgina de descàrrega.

Convé afegir la carpeta bin de la instal.lació al PATH.

A Windows hi ha dues opcions:

  • Amb servei.
  • Sense servei: cal iniciar bin/mongod i que existeixi una carpeta /data/db i /data/log (a la mateixa carpeta que la instal.lació).

Veure la base de dades actual:

> db

Sel.leccionar una base de dades:

> use <database>

Per mostrar totes les bases de dades existents i les col.leccions existents de la base de dades sel.leccionada:

> show dbs
> show collections

Crea una base de dades anomenada "testdb", una col.lecció "persones" i insereix un document:

> use testdb
> db.persones.insertOne( { nom: 'Pere', edat: 23 } );
{
  "acknowledged" : true,
 "insertedId" : ObjectId("5bf52138e44e1fdc9edd15e5")
}

MongoDB torna un id que es genera automàticament per cada document.

Per trobar els registres creats:

> db.persones.find({})
{ "_id" : ObjectId("5bf52138e44e1fdc9edd15e5"), "nom" : "Pere", "edat" : 23 }
{ "_id" : ObjectId("5bf5219de44e1fdc9edd15e6"), "nom" : "Joan", "edat" : 21 }
{ "_id" : ObjectId("5bf521b0e44e1fdc9edd15e7"), "nom" : "Lluís", "edat" : 28 }
{ "_id" : ObjectId("5bf52255e44e1fdc9edd15e8"), "nom" : "Jordi", "edat" : 35 }

Afegint una condició:

> db.persones.find({ edat: 21})
{ "_id" : ObjectId("5bf5219de44e1fdc9edd15e6"), "nom" : "Joan", "edat" : 21 }

Trobar per _id:

> db.persones.find( {_id: ObjectId("5bf521b0e44e1fdc9edd15e7")} )
{ "_id" : ObjectId("5bf521b0e44e1fdc9edd15e7"), "nom" : "Lluís", "edat" : 28 }

Actualitzar un document (el primer que troba):

> db.persones.updateOne({ nom: "Jordi" }, { $set: {edat: 36} }) 
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

Incrementar un camp a molts registres:

> db.persones.updateMany({}, { $inc: { edat: NumberInt(1) } })
{ "acknowledged" : true, "matchedCount" : 4, "modifiedCount" : 4 }

La opció per esborrar un camp és $unset.

Reemplaçar un document (excepte el _id):

> db.persones.replaceOne({ nom: "Lluís" }, { nom: "Maria", edat: 36 })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

Esborrar un document (el primer que troba: no troba cap):

> db.persones.deleteOne({ edat: 13 })
{ "acknowledged" : true, "deletedCount" : 0 }

Esborrar molts documents (no troba cap):

> db.persones.deleteMany({ edat: 15 })
{ "acknowledged" : true, "deletedCount" : 0 }

Sortir:

exit

Arrays

Els arrays poden formar part d'un document, com per exemple "mitjans":

{ "_id" : ObjectId("5bf684eff884d83e4c4a8cfa"), "nom" : "Joan", "edat" : 22, "mitjans" : [ "tv", "mobil" ] }

Podem filtrar la cerca pel contingut de l'array. La primera comanda busca exactament documents amb els dos mitjans; la segona, que al menys continguin els dos; la tercera, que contingui un.

> db.mitjans.find({ mitjans: ["mobil", "portatil"] })
> db.mitjans.find({ mitjans: { $all: ["mobil", "portatil"] } })
> db.mitjans.find({ mitjans: "tv" })

Podem afegir contingut a un array (push) o treure'l (pull). També podem afegir només si no existeix (addToSet).

> db.persones.updateOne({}, {$push: {mitjans: "tv"}})
> db.persones.updateOne({}, {$pull: {mitjans: "tv"}})

Condicions combinades

Es poden combinar condicions per utilitzar-les a les cerques (find) o bé a les condicions per fer un update o un delete.

Les condicions es troben explicades aquí.

Per exemple, per buscar amb l'operador AND aquells que tinguin el mitjà "tv" i siguin menors de 30 anys:

> db.mitjans.find( { $and: [ {mitjans: "tv"}, {edat: { $lt: 30}} ]} )

O trobar els que no tinguin "mobil":

> db.persones.find({ mitjans: { $not: { $eq: "mobil"} }  })

Importar dades

Es poden importar dades des d'un arxiu de text que tingui un format de text com aquest:

{"mitjans":["sobretaula"],"nom":"Amya","edat":37}
{"mitjans":["tauleta","mobil","portatil"],"nom":"Abdul","edat":20}
{"mitjans":["tauleta","mobil","portatil"],"nom":"Jordyn","edat":16}
{"mitjans":["tauleta","mobil","portatil"],"nom":"Annie","edat":25}

L'eina és mongoimport. Aquest és un exemple d'ús, per importar un arxiu "mitjans.json" a la base de dades "testdb" i la col.lecció "mitjans":

$ mongoimport --db testdb --collection mitjans mitjans.json


La documentació del driver es troba aquí.

Des d'aquesta pàgina (MongoDB Driver Sync), Es poden descarregar manualment els tres jars que es necessiten:

Per defecte, MongoDB no té control d'accés.

Així es podria accedir a la base de dades "testdb" del teu ordinador:

MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");
MongoDatabase database = mongoClient.getDatabase("testdb");

Per defecte, MongoDB utilitza el port 27017.

Si es vol accedir a una col.lecció, cal fer-ho així:

MongoCollection<Document> collection = database.getCollection("persones");

Tot l'accés als documents que hi ha a les col.leccions es fa mitjançant la classe Document.

Aquesta classe és realment un mapa, però té el mètode append per a facil.litar l'encadenació de crides al put:

public Document append(final String key, final Object value) {
    documentAsMap.put(key, value);
    return this;
}

Per tant, es poden fer coses com:

Document doc = new Document().append("nom", "John").append("edat", 26);

A partir d'aquest objecte, es pot afegir a la col.lecció així:

collection.insertOne(doc);

Per fer una cerca i iterar sobre els resultats, es pot fer:

MongoCursor<Document> cursor = collection.find().iterator();
while (cursor.hasNext()) {
    Document doc = cursor.next();
    System.out.println(doc);
}
cursor.close();

Cal sempre tancar el cursor. Com és una classe que implementa AutoCloseable, es pot fer un try-with-resources.

Per crear una condició, cal passar un objecte Bson al mètode collection.find(). Per exemple, per trobar les persones amb més de 30 anys, i fer-ho amb un try-with-resources:

Bson condicio = Filters.gt("edat", 30);
try (MongoCursor<Document> cursor = collection.find(condicio).iterator()) {
    while (cursor.hasNext()) {
        Document doc = cursor.next();
        System.out.println(doc);
    }
}

La condició per cercar per ID seria aquesta (si busquem per l'id 5bf5716d7a8847dc909284e9):

Bspm condicio = Filters.eq("_id", new ObjectId("5bf5716d7a8847dc909284e9"));

Per fer updates:

UpdateResult uResult = collection.updateOne(Filters.eq("nom", "Maria"), Updates.set("edat", 34));
System.out.println(uResult); // AcknowledgedUpdateResult{matchedCount=1, modifiedCount=1, upsertedId=null}

Els sets es poden combinar fer Updates.combine(...). També es poden utilitzar les següents operacions per fer updates:

  • Updates.push(camp, contingut) per afegir un element a un array.
  • Updates.pull(camp, contingut) per esborrar un element d'un array.
  • Updates.addToSet(camp, contingut) per afegir un element a un array, si no existeix.

Per fer deletes:

DeleteResult dResult = collection.deleteOne(Filters.eq("nom", "Mario"));
System.out.println(dResult); // AcknowledgedDeleteResult{deletedCount=0}

Per accedir a arrays dins d'un document només cal obtenir una List del tipus que contingui. Per exemple, si l'array "mitjans" conté Strings:

List<String> mitjans = (List<String>) document.get("mitjans");


TO DO: sorts, projections, read/write concerns, aggregation.