de Nick Karnik

Introducere în Mongoose pentru MongoDB

Introducere in Mongoose pentru MongoDB

Mongoose este o bibliotecă de modelare a datelor obiectelor (ODM) pentru MongoDB și Node.js. Gestionează relațiile dintre date, oferă validarea schemelor și este folosit pentru a traduce între obiecte în cod și reprezentarea acelor obiecte în MongoDB.

0*b5piDNW1dqlkJWKe
Cartarea obiectelor între Node și MongoDB gestionată prin Mongoose

MongoDB este o bază de date de documente NoSQL fără schemă. Înseamnă că puteți stoca documente JSON în acesta, iar structura acestor documente poate varia, deoarece nu este aplicată ca bazele de date SQL. Acesta este unul dintre avantajele utilizării NoSQL, deoarece accelerează dezvoltarea aplicațiilor și reduce complexitatea implementărilor.

Mai jos este un exemplu al modului în care datele sunt stocate în baza de date Mongo vs. SQL:

0*rcotALFe2LeebN y
Documente NoSQL vs. Tabelele relaționale în SQL

Terminologii

Colecții

„Colecțiile” din Mongo sunt echivalente cu tabelele din bazele de date relaționale. Pot deține mai multe documente JSON.

ad-banner

Documente

„Documentele” sunt echivalente cu înregistrările sau rândurile de date din SQL. În timp ce un rând SQL poate face referință la date în alte tabele, documentele Mongo de obicei le combină într-un document.

Câmpuri

„Câmpurile” sau atributele sunt similare coloanelor dintr-un tabel SQL.

Schemă

În timp ce Mongo nu are schemă, SQL definește o schemă prin definiția tabelului. O „schemă” Mongoose este o structură de date a documentului (sau forma documentului) care este pusă în aplicare prin intermediul stratului de aplicație.

Modele

„Modelele” sunt constructori de ordin superior care iau o schemă și creează o instanță a unui document echivalent cu înregistrările dintr-o bază de date relațională.

Noțiuni de bază

Instalare Mongo

Înainte de a începe, să configurăm Mongo. Puteți alege dintre una dintre următoarele opțiuni (folosim opțiunea # 1 pentru acest articol):

  1. Descărcați versiunea MongoDB adecvată pentru sistemul dvs. de operare din Site-ul web MongoDB și urmați instrucțiunile lor de instalare
  2. Creați o bază de date sandbox gratuită abonament pe mLab
  3. Instalați Mongo folosind Docker dacă preferați să utilizați docker

Să navigăm prin unele dintre elementele de bază ale Mangustă prin implementarea unui model care reprezintă date pentru o agendă simplificată.

Folosesc Visual Studio Code, Node 8.9 și NPM 5.6. Lansează IDE-ul tău preferat, creează un proiect gol și hai să începem! Vom folosi sintaxa limitată ES6 în Node, deci nu vom configura Babel.

Instalare NPM

Să mergem în dosarul proiectului și să inițializăm proiectul nostru

npm init -y

Să instalăm Mongoose și o bibliotecă de validare cu următoarea comandă:

npm install mongoose validator

Comanda de instalare de mai sus va instala cea mai recentă versiune a bibliotecilor. Sintaxa Mongoose din acest articol este specifică Mongoose v5 și nu numai.

Conexiune la baza de date

Creați un fișier ./src/database.js sub rădăcina proiectului.

În continuare, vom adăuga o clasă simplă cu o metodă care se conectează la baza de date.

Șirul de conexiune va varia în funcție de instalare.

let mongoose = require('mongoose');

const server="127.0.0.1:27017"; // REPLACE WITH YOUR DB SERVER
const database="fcc-Mail";      // REPLACE WITH YOUR DB NAME

class Database {
  constructor() {
    this._connect()
  }
  
_connect() {
     mongoose.connect(`mongodb://${server}/${database}`)
       .then(() => {
         console.log('Database connection successful')
       })
       .catch(err => {
         console.error('Database connection error')
       })
  }
}

module.exports = new Database()

require(‘mongoose’) apelul de mai sus returnează un obiect Singleton. Înseamnă că prima dată când suni require(‘mongoose’), creează o instanță a clasei Mongoose și o returnează. La apelurile ulterioare, va returna aceeași instanță care a fost creată și returnată pentru prima dată din cauza modului în care funcționează importul / exportul modulului în ES6.

0*RvVsD byUakUzuCj
Importul modulului / necesită flux de lucru

În mod similar, ne-am transformat clasa Bază de date într-un singleton returnând o instanță a clasei în module.exports pentru că avem nevoie doar de o singură conexiune la baza de date.

ES6 ne face foarte ușor să creăm un model singleton (o singură instanță) datorită modului în care funcționează modulul de încărcare prin cache a răspunsului unui fișier importat anterior.

Schema Mongoose vs. Model

Un model Mongoose este un înveliș pe schema Mongoose. O schemă Mongoose definește structura documentului, valorile implicite, validatorii etc., în timp ce un model Mongoose oferă o interfață către baza de date pentru crearea, interogarea, actualizarea, ștergerea înregistrărilor etc.

Crearea unui model Mongoose cuprinde în principal trei părți:

1. Referirea la Mangosta

let mongoose = require('mongoose')

Această referință va fi aceeași cu cea care a fost returnată atunci când ne-am conectat la baza de date, ceea ce înseamnă că schemele și definițiile modelului nu vor trebui să se conecteze în mod explicit la baza de date.

2. Definirea schemei

O schemă definește proprietățile documentului printr-un obiect în care numele cheii corespunde cu numele proprietății din colecție.

let emailSchema = new mongoose.Schema({
  email: String
})

Aici definim o proprietate numită e-mail cu un tip de schemă Şir care se mapează la un validator intern care va fi declanșat atunci când modelul este salvat în baza de date. Va eșua dacă tipul de date al valorii nu este un tip șir.

Sunt permise următoarele tipuri de scheme:

  • Matrice
  • Boolean
  • Tampon
  • Data
  • Mixt (Un tip de date generic / flexibil)
  • Număr
  • ObjectId
  • Şir

Mixed și ObjectId sunt definite în require(‘mongoose’).Schema.Types.

3. Exportarea unui model

Trebuie să apelăm constructorul modelului pe instanța Mongoose și să-i transmitem numele colecției și o referință la definiția schemei.

module.exports = mongoose.model('Email', emailSchema)

Să combinăm codul de mai sus în ./src/models/email.js pentru a defini conținutul unui model de bază de e-mail:

let mongoose = require('mongoose')

let emailSchema = new mongoose.Schema({
  email: String
})

module.exports = mongoose.model('Email', emailSchema)

O definiție a schemei ar trebui să fie simplă, dar complexitatea ei se bazează de obicei pe cerințele aplicației. Schemele pot fi reutilizate și pot conține și mai multe scheme copil. În exemplul de mai sus, valoarea proprietății e-mail este un tip de valoare simplu. Cu toate acestea, poate fi și un tip de obiect cu proprietăți suplimentare.

Putem crea o instanță a modelului pe care l-am definit mai sus și o putem completa folosind următoarea sintaxă:

let EmailModel = require('./email')

let msg = new EmailModel({
  email: 'ada.lovelace@gmail.com'
})

Să îmbunătățim schema de e-mail pentru a face ca proprietatea de e-mail să devină un câmp unic, obligatoriu și să convertim valoarea în minusculă înainte de a o salva. De asemenea, putem adăuga o funcție de validare care să ne asigure că valoarea este o adresă de e-mail validă. Vom face referință și vom folosi biblioteca de validare instalată mai devreme.

let mongoose = require('mongoose')
let validator = require('validator')

let emailSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    validate: (value) => {
      return validator.isEmail(value)
    }
  }
})

module.exports = mongoose.model('Email', emailSchema)

Operațiuni de bază

Mongoose are un API flexibil și oferă multe modalități de a îndeplini o sarcină. Nu ne vom concentra asupra variațiilor, deoarece acest lucru nu intră în domeniul de aplicare al acestui articol, dar amintiți-vă că majoritatea operațiunilor pot fi realizate în mai multe moduri, fie din punct de vedere sintactic, fie prin arhitectura aplicației.

Creați înregistrare

Să creăm o instanță a modelului de e-mail și să o salvăm în baza de date:

let EmailModel = require('./email')

let msg = new EmailModel({
  email: 'ADA.LOVELACE@GMAIL.COM'
})

msg.save()
   .then(doc => {
     console.log(doc)
   })
   .catch(err => {
     console.error(err)
   })

Rezultatul este un document care este returnat la o salvare reușită:

{ 
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: 'ada.lovelace@gmail.com',
  __v: 0 
}

Următoarele câmpuri sunt returnate (câmpurile interne sunt prefixate cu un subliniat):

  1. _id câmpul este generat automat de Mongo și este o cheie primară a colecției. Valoarea sa este un identificator unic pentru document.
  2. Valoarea email câmpul este returnat. Observați că este cu litere mici, deoarece am specificat lowercase:true atribut în schemă.
  3. __v este proprietatea versionKey setată pe fiecare document când a fost creată prima dată de Mongoose. Valoarea sa conține revizuirea internă a documentului.

Dacă încercați să repetați operația de salvare de mai sus, veți primi o eroare deoarece am specificat că câmpul de e-mail ar trebui să fie unic.

Fetch Record

Să încercăm să recuperăm înregistrarea pe care am salvat-o mai devreme în baza de date. Clasa model expune mai multe metode statice și de instanță pentru a efectua operațiuni pe baza de date. Acum vom încerca să găsim înregistrarea pe care am creat-o anterior folosind metoda find și să transmitem e-mailul ca termen de căutare.

EmailModel
  .find({
    email: 'ada.lovelace@gmail.com'   // search query
  })
  .then(doc => {
    console.log(doc)
  })
  .catch(err => {
    console.error(err)
  })

Documentul returnat va fi similar cu ceea ce a fost afișat când am creat înregistrarea:

{ 
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: 'ada.lovelace@gmail.com',
  __v: 0 
}

Actualizați înregistrarea

Să modificăm înregistrarea de mai sus schimbând adresa de e-mail și adăugând un alt câmp la ea, totul într-o singură operație. Din motive de performanță, Mongoose nu va returna documentul actualizat, așa că trebuie să trecem un parametru suplimentar pentru a-l solicita:

EmailModel
  .findOneAndUpdate(
    {
      email: 'ada.lovelace@gmail.com'  // search query
    }, 
    {
      email: 'theoutlander@live.com'   // field:values to update
    },
    {
      new: true,                       // return updated doc
      runValidators: true              // validate before update
    })
  .then(doc => {
    console.log(doc)
  })
  .catch(err => {
    console.error(err)
  })

Documentul returnat va conține e-mailul actualizat:

{ 
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: 'theoutlander@live.com',
  __v: 0 
}

Ștergeți înregistrarea

Vom folosi findOneAndRemove apel pentru a șterge o înregistrare. Returnează documentul original care a fost eliminat:

EmailModel
  .findOneAndRemove({
    email: 'theoutlander@live.com'
  })
  .then(response => {
    console.log(response)
  })
  .catch(err => {
    console.error(err)
  })

Ajutoare

Am analizat unele dintre funcționalitățile de bază de mai sus cunoscute sub numele de operații CRUD (Creare, citire, actualizare, ștergere), dar Mongoose oferă, de asemenea, posibilitatea de a configura mai multe tipuri de metode și proprietăți de ajutor. Acestea pot fi utilizate pentru a simplifica și mai mult lucrul cu datele.

Să creăm o schemă de utilizator în ./src/models/user.js cu câmpurilefirstName și lastName:

let mongoose = require('mongoose')

let userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String
})

module.exports = mongoose.model('User', userSchema)

Proprietate virtuală

O proprietate virtuală nu este persistată în baza de date. Îl putem adăuga la schema noastră ca ajutor pentru a obține și seta valori.

Să creăm o proprietate virtuală numită fullName care poate fi folosit pentru a seta valori pe firstName și lastName și preluați-le ca valoare combinată atunci când citiți:

userSchema.virtual('fullName').get(function() {
  return this.firstName + ' ' + this.lastName
})

userSchema.virtual('fullName').set(function(name) {
  let str = name.split(' ')
  
  this.firstName = str[0]
  this.lastName = str[1]
})

Rambursările pentru get și set trebuie să utilizeze cuvântul cheie funcție, deoarece trebuie să accesăm modelul prin this cuvânt cheie. Utilizarea funcțiilor săgeată grasă va schimba ce this se refera la.

Acum, putem stabili firstName și lastName prin atribuirea unei valori către fullName:

let model = new UserModel()

model.fullName="Thomas Anderson"

console.log(model.toJSON())  // Output model fields as JSON
console.log()
console.log(model.fullName)  // Output the full name

Codul de mai sus va afișa următoarele:

{ _id: 5a7a4248550ebb9fafd898cf,
  firstName: 'Thomas',
  lastName: 'Anderson' }
  
Thomas Anderson

Metode de instanță

Putem crea metode de asistență personalizate pe schemă și le putem accesa prin instanța model. Aceste metode vor avea acces la obiectul model și pot fi utilizate destul de creativ. De exemplu, am putea crea o metodă pentru a găsi toți oamenii care au același prenume ca instanța curentă.

În acest exemplu, să creăm o funcție pentru a returna inițialele pentru utilizatorul curent. Să adăugăm o metodă de asistență personalizată numită getInitials la schemă:

userSchema.methods.getInitials = function() {
  return this.firstName[0] + this.lastName[0]
}

Această metodă va fi accesibilă printr-un exemplu de model:

let model = new UserModel({
  firstName: 'Thomas',
  lastName: 'Anderson'
})

let initials = model.getInitials()

console.log(initials) // This will output: TA

Metode statice

Similar metodelor de instanță, putem crea metode statice pe schemă. Să creăm o metodă pentru a recupera toți utilizatorii din baza de date:

userSchema.statics.getUsers = function() {
  return new Promise((resolve, reject) => {
    this.find((err, docs) => {
      if(err) {
        console.error(err)
        return reject(err)
      }
      
      resolve(docs)
    })
  })
}

Apelare getUsers în clasa Model va returna toți utilizatorii din baza de date:

UserModel.getUsers()
  .then(docs => {
    console.log(docs)
  })
  .catch(err => {
    console.error(err)
  })

Adăugarea de instanțe și metode statice este o abordare plăcută pentru a implementa o interfață pentru interacțiunile bazei de date cu privire la colecții și înregistrări.

Middleware

Middleware sunt funcții care rulează în etape specifice ale unei conducte. Mongoose acceptă middleware pentru următoarele operații:

  • Agregat
  • Document
  • Model
  • Interogare

De exemplu, modelele au pre și post funcții care iau doi parametri:

  1. Tipul evenimentului („init”, „validare”, „salvare”, „eliminare”)
  2. Un apel invers care este executat cu acest referindu-se la instanța model
Exemplu de middleware (aka pre și post hooks)

Să încercăm un exemplu adăugând două câmpuri numite createdAt și updatedAt la schema noastră:

let mongoose = require('mongoose')

let userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String,
  createdAt: Date,
  updatedAt: Date
})

module.exports = mongoose.model('User', userSchema)

Cand model.save() se numește, există un pre(‘save’, …) și post(‘save’, …) eveniment care este declanșat. Pentru al doilea parametru, puteți trece o funcție care este apelată atunci când evenimentul este declanșat. Aceste funcții duc un parametru la următoarea funcție din lanțul middleware.

Să adăugăm un cârlig de pre-salvare și să setăm valori pentru createdAt și updatedAt:

userSchema.pre('save', function (next) {
  let now = Date.now()
   
  this.updatedAt = now
  // Set a value for createdAt only if it is null
  if (!this.createdAt) {
    this.createdAt = now
  }
  
  // Call the next function in the pre-save chain
  next()    
})

Să creăm și să salvăm modelul nostru:

let UserModel = require('./user')

let model = new UserModel({
  fullName: 'Thomas Anderson'
}

msg.save()
   .then(doc => {
     console.log(doc)
   })
   .catch(err => {
     console.error(err)
   })

Ar trebui să vedeți valori pentru createdAt și updatedAt când este tipărită înregistrarea creată:

{ _id: 5a7bbbeebc3b49cb919da675,
  firstName: 'Thomas',
  lastName: 'Anderson',
  updatedAt: 2018-02-08T02:54:38.888Z,
  createdAt: 2018-02-08T02:54:38.888Z,
  __v: 0 }

Pluginuri

Să presupunem că dorim să urmărim când a fost creată o înregistrare și ultima actualizare pe fiecare colecție din baza noastră de date. În loc să repetăm ​​procesul de mai sus, putem crea un plugin și îl putem aplica fiecărei scheme.

Să creăm un fișier ./src/model/plugins/timestamp.js și reproduceți funcționalitatea de mai sus ca un modul reutilizabil:

module.exports = function timestamp(schema) {

  // Add the two fields to the schema
  schema.add({ 
    createdAt: Date,
    updatedAt: Date
  })

  // Create a pre-save hook
  schema.pre('save', function (next) {
    let now = Date.now()
   
    this.updatedAt = now
    // Set a value for createdAt only if it is null
    if (!this.createdAt) {
      this.createdAt = now
    }
   // Call the next function in the pre-save chain
   next()    
  })
}

Pentru a utiliza acest plugin, îl transferăm pur și simplu schemelor cărora ar trebui să li se ofere această funcționalitate:

let timestampPlugin = require('./plugins/timestamp')

emailSchema.plugin(timestampPlugin)
userSchema.plugin(timestampPlugin)

Construirea interogărilor

Mongoose are un API foarte bogat care gestionează multe operațiuni complexe acceptate de MongoDB. Luați în considerare o interogare în care putem construi în mod incremental componente de interogare.

În acest exemplu, vom:

  1. Găsiți toți utilizatorii
  2. Treceți peste primele 100 de înregistrări
  3. Limitați rezultatele la 10 înregistrări
  4. Sortați rezultatele după câmpul firstName
  5. Selectați prenumele
  6. Executați această interogare
UserModel.find()                   // find all users
         .skip(100)                // skip the first 100 items
         .limit(10)                // limit to 10 items
         .sort({firstName: 1}      // sort ascending by firstName
         .select({firstName: true} // select firstName only
         .exec()                   // execute the query
         .then(docs => {
            console.log(docs)
          })
         .catch(err => {
            console.error(err)
          })

Închidere

Abia am zgâriat suprafața explorând unele dintre capacitățile Mongoose. Este o bibliotecă bogată, plină de caracteristici utile și puternice, care fac o bucurie să lucrați cu modele de date în stratul de aplicație.

Deși puteți interacționa direct cu Mongo folosind Mongo Driver, Mongoose va simplifica acea interacțiune, permițându-vă să modelați relațiile dintre date și să le validați cu ușurință.

Fapt amuzant: Mangustă este creat de Valeri Karpov cine este un inginer incredibil de talentat! El a inventat termenul Stiva MEAN.

Dacă acest articol a fost de ajutor, ??? și Follasă-mă pe Twitter.

1611751271 548 Introducere in Mongoose pentru MongoDB
S-ar putea să vă placă și atelierul meu de pe YouTube: Cum se construiește un API REST cu Node | Express | Mongo