3. Treballant amb MongoDB: Operacions bàsiques de shell
3.1. Col·leccions i Documents
Com ja sabem, la unitat bàsica d'informació amb la qual treballa MongoDB és el document, que seria l'equivalent a un registre en un model relacional. Aquests són documents JSON, formats per parells clau-valor, i que representen la informació de manera força intuïtiva. Els servidors de MongoDB, per la seva banda, emmagatzemaran aquestes dades en format BSON (Binary JSON), un format de serialització binària.
Pel que fa als documents JSON per a MongoDB, cal tenir en compte alguns aspectes:
- Pel que fa a les claus:
- No poden ser nul·les.
- Poden consistir en qualsevol caràcter UTF-8, excepte els caràcters
.o$. - Són sensibles a majúscules i minúscules.
- Han de ser úniques dins del mateix document.
- Pel que fa als valors:
- Poden ser de qualsevol tipus permès.
- Pel que fa al document:
- Ha de tenir un camp
_id, amb un valor únic, que actuarà com a identificador del document. - Si no especifiquem aquesta clau, MongoDB la generarà automàticament, amb un objecte de tipus
ObjectId.
- Ha de tenir un camp
Si els documents són l'equivalent als registres, les col·leccions són l'equivalent a les taules, amb la diferència que les col·leccions tenen un esquema dinàmic, amb el qual els documents de la mateixa col·lecció poden presentar claus o tipus de dades diferents entre ells.
Els noms de les col·leccions estaran subjectes a les següents restriccions:
- No poden ser la cadena buida (
""), ni el caràcternull, ni contenir el símbol$. - Podem utilitzar el punt (
.) en els noms de les col·leccions per afegir prefixos, però les col·leccions no es poden crear amb el prefixsystem., ja que aquest s'utilitza per a col·leccions internes del sistema. Per exemple,db.system.testno seria vàlid, peròdb.systema.testsí que ho seria.
3.2. Operacions bàsiques amb MongoDB
A continuació, veurem algunes de les operacions bàsiques que podem realitzar a MongoDB:
insertOne(document)→ Afegeix un document a la col·lecció:db.collection.insertOne({ a:1 })
insertMany(documents)→ Afegeix un conjunt de documents a la col·lecció:db.collection.insertMany([{ a:1 },{ a:2 },{ a:3,b:5 }])
find(criteria)→ Obté tots els documents d'una col·lecció que coincideixen amb el patró especificat.db.collection.find({a:1}). Tingueu en compte que el patró serà també un objecte JSON.
findOne(Criterion)→ Obté un element de la col·lecció que coincideix amb el patró.db.collection.findOne()
updateOne(Criterion, Operation, [options])iupdateMany(Criterion, Operation, [options])→ Actualitza un (o diversos en el cas d'updateMany) documents de la col·lecció. Requereix dos paràmetres:- els criteris de cerca del document a actualitzar i
- l'operació d'actualització.
- Admet un tercer paràmetre opcional per a opcions.
db.collection.updateOne({a:1}, {$set: {a:2}})
deleteOne(Criterion)ideleteMany(Criterion)→ Elimina els documents d'una col·lecció que coincideixen amb els criteris.db.collection.deleteOne({a:1})
A les següents seccions aprofundirem en les diferents operacions.
3.3. Tipus de dades
Els tipus de dades amb els quals treballa MongoDB són similars als que podem trobar en JavaScript i Java. MongoDB admet els tipus bàsics descrits a la taula següent:
null→ Representa tant el valor nul com un camp que no existeix.boolean→ Permet els valors true i false.number→ Representa valors numèrics de punt flotant. Si volem utilitzar tipus enters o enters llargs, hem d'utilitzar les nostres pròpies classes: NumberInt (32 bits) o NumberLong (64 bits).String→ Representa qualsevol cadena de text UTF-8 vàlida.Date→ Representa dates, expressades en mil·lisegons.array→ Llistes de valors que es representen com a vectors.Documents incrustats→ Els documents poden tenir altres documents incrustats en ells.ObjectId→ Aquest és el tipus per defecte per als camps_id, i està dissenyat per generar fàcilment valors únics a nivell global.
3.3.1. Data
Mongo utilitza el tipus Date de JavaScript. Quan generem un nou objecte de tipus Date, hem d'utilitzar l'operador New, ja que en cas contrari obtindríem una representació de la data en forma de cadena.
Per exemple, si definim les variables a i b de la següent manera:
els resultats serien prou diferents:
| JavaScript | |
|---|---|
3.3.2. Arrays
Els arrays es poden utilitzar per representar col·leccions ordenades, com llistes o cues, o col·leccions desordenades, com conjunts. Com en JavaScript, i a diferència d'altres llenguatges, com Java, cada element del vector pot tenir un tipus de dada diferent, incloent-hi altres objectes de tipus vector.
Vegem alguns exemples sobre vectors en JavaScript i, per tant, en MongoDB:
3.3.3. Documents incrustats
Un parell clau-valor en un document pot tenir un altre document com a valor. Això es coneix com a documents incrustats (embedded), i seria quan s'utilitza un objecte JSON dins d'un altre. Per exemple:
| JavaScript | |
|---|---|
Com podem veure, el document en si mateix conté informació sobre la pel·lícula i el seu director. En un model relacional, normalment tindríem dues taules relacionades entre si. En aquest cas, és possible que si volem mantenir informació específica sobre els directors, acabem amb informació redundant.
3.3.4. Que són els OBjectIds?
La classe ObjectId utilitza 12 bytes, organitzats de la següent manera:
- Timestamp (bytes 0-3) → La marca de temps en segons des de l'1 de gener de 1970.
- Machine ID (bytes 4-6) → Identificador únic de la màquina, normalment un hash del seu hostname.
- PID (bytes 7-8) → Identificador del procés que genera l'ObjectID, per garantir la unicitat dins de la mateixa màquina.
- Increment (bytes 9-11) → Valor auto-incremental, per garantir la unicitat en el mateix segon, màquina i procés.
Com podem veure, és un mecanisme més robust que un camp auto-incremental com en MySQL. Això correspon a la naturalesa distribuïda de MongoDB, de manera que els objectes es poden generar en un entorn multi-host.
3.4. Afegint informació a les col·leccions
La manera natural d'afegir elements a la base de dades és a través dels diferents mètodes d'inserció, disponibles en totes les col·leccions.
3.4.1. insertOne()
Permet inserir un document a la col·lecció. Per exemple, per inserir l'objecte peli creat a la secció anterior, podem fer:
| JavaScript | |
|---|---|
Com podem veure, la resposta és un document JSON que conté un valor booleà que indica si l'operació ha estat exitosa, i un ObjectID, amb l'ID assignat automàticament.
Important
Tingueu en compte:
- Si la col·lecció a la qual afegim un document no existeix, es crearà automàticament.
- Pel que fa al camp
_id, com podem veure, es va generar automàticament. No obstant això, podem indicar aquest identificador, sense que sigui de tipus ObjectId, l'única restricció és que sigui únic, per evitar duplicats. - No hem utilitzat cap esquema per a la col·lecció, ja que cada document que inserim pot tenir un esquema diferent.
3.4.2. insertMany()
Permet afegir diversos documents a una col·lecció. Aleshores, hem de proporcionar un array de documents:
Important
Si es produeix un error durant la inserció, ni el document que provoca l'error ni els documents següents s'inseriran a la col·lecció.
3.5. Eliminant informació
Per eliminar documents d'una col·lecció utilitzarem les ordres deleteOne(), deleteMany() o findOneAndDelete(), proporcionant-los com a paràmetre un JSON amb una condició que volem que compleixin el document o documents a eliminar.
- L'ordre deleteOne només eliminarà el primer element que coincideixi amb els criteris, així que si volem eliminar un document específic, hem d'utilitzar criteris que corresponguin a identificadors únics, com el
_id. - L'ordre deleteMany eliminarà tots els documents que coincideixin amb els criteris.
Tant deleteOne com deleteMany retornen un document amb un booleà, indicant si l'operació s'ha realitzat, així com el nombre d'elements eliminats (deletedCount).
Per la seva banda, findOneAndDelete també elimina un document, basant-se en criteris de selecció i ordenació, però també retornant el document que ha estat eliminat.
Per exemple, creem una col·lecció amb diversos elements:
Si volem eliminar tots els documents d'una col·lecció, podríem utilitzar l'ordre drop en lloc d'això, però cal anar amb molta cura, ja que també eliminarà alguna metainformació.
| JavaScript | |
|---|---|
3.6. Actualització de documents
Per actualitzar documents, podem optar per actualitzacions de reemplaçament, utilitzant el mètode replaceOne(), o fer modificacions als documents existents, utilitzant els mètodes updateOne(), updateMany() i findOneAndUpdate(). Aquests mètodes rebran dos arguments: el primer serà els criteris o condicions que han de complir els documents a actualitzar, i el segon serà un document amb el nou document o les actualitzacions a aplicar.
3.6.1. Actualització de reemplaçament (replace)
L'operació de reemplaçament, com el seu nom indica, reemplaça un document sencer que compleix els criteris d'actualització amb un altre document nou. Per exemple, creem una nova col·lecció de calendaris, per emmagatzemar contactes, amb informació sobre telèfons:
| JavaScript | |
|---|---|
_id de l'objecte, a través del qual podrem identificar aquest document de manera inequívoca. Així, podríem reemplaçar aquest document per un altre mitjançant:
| JavaScript | |
|---|---|
Com podem veure, es tracta de reemplaçar tot el document, amb la qual cosa podem fins i tot modificar la seva estructura.
Com hem anticipat, les modificacions es realitzen utilitzant els mètodes updateOne(), updateMany() i findOneAndUpdate(). Similar a les operacions d'eliminació, el mètode updateOne() modificarà només el primer document que coincideixi amb els criteris donats i el mètode updateMany(), tots aquells que coincideixin amb els criteris. Per la seva banda, el mètode findOneAndUpdate() modifica el document i retorna el document original per defecte, tot i que això és configurable a través d'opcions.
3.6.1.1. Modificadors
Els modificadors són claus especials que ens permeten especificar operacions d'actualització més complexes. Normalment, no necessitarem reemplaçar tot el document, com en el cas anterior, sinó afegir o modificar camps específics:
$set→ Assigna un valor a un camp del document. Si no existeix, el crearà.db.collection.updateOne({criteri}, {$set: {camp:valor} });
$unset→ Elimina un camp d'un o més documents. Com que necessitem introduir un parell clau-valor, afegirem un booleà com a valor.db.collection.updateMany({criteri}, {$unset: {camp:true} });
$inc→ Incrementa o decrementa el valor numèric d'una clau (no es refereix a l'identificador), creant-ne una de nova si no existeix.db.collection.updateOne({criteri}, {$inc: {camp:increment} });
$push→ Afegeix elements a un array. Si l'array no existeix, el crea amb els elements que indiquem en el push, mentre que si ja existeix, els afegeix al final d'aquest.db.collection.update({criteri}, {$push: {nom_array:{llista_de_valors} } });
$pull→ Elimina elements d'un array basant-se en algun criteri.db.collection.update({criteri},{$pull:{vector:element}}).
$pop→ Elimina elements d'un array tractat com una pila o cua, és a dir, eliminant el primer (-1) o últim (1) element.db.collection.update({criteri},{$pop:{vector: [ -1 | 1 ] }})
Information
Aquests modificadors funcionen tant amb updateOne com amb updateMany.
3.6.1.2. Upserts
Quan no es troba cap document que coincideixi amb els criteris per a una actualització, com és d'esperar, no es produeix cap canvi a la col·lecció.
D'altra banda, de vegades, podem desitjar que si un document amb certs criteris no existeix quan volem modificar-lo, es creï. Això s'aconsegueix a través d'actualitzacions especials, anomenades upserts. Amb aquesta operació, ens estalviem de buscar primer a la col·lecció per saber si hem de realitzar una operació d'inserció (si no existeix) o de modificació (si existeix).
Per realitzar un upsert, utilitzarem el tercer argument de les actualitzacions, que consisteix en un document amb diferents opcions en format clau-valor, afegint la clau upsert a true.
| JavaScript | |
|---|---|