6. Databases
Database
- can handle large amount of data efficiently
- provide tools to insert, query and update data
- they offer security features
- they can scale well
- SQL: Structured Query Language, relational databases. Use tables. MySQL, Postgres, SQLite
- NoSQL: store information in xml, yaml, json. No tables or schema to follow. MongoDB, Redis, Neo4j
MongoDB
- mongo is commonly used with node and express
- easy to start and poular
- installation
- multiple databases at the same time
- create a new db
use dbName
(won’t appear until there is data in it) - BSON binary json, more compact in memory
MongoDB CRUD operations
Create
-
insert data in collections
db.dogs.insertOne({name: "Charlie", age:3, breed:"corgie", isFriendly: true})
-
db.dogs.insert([{name: "Walt", breed: "Golden", age: 14}, {name: "Tonya", breed:"Chihua", age:17}])
-
show collections
-
db.dogs.find()
Find
db.dogs.find({breed: "corgi"})
Updating
db.dogs.updateOne({name: "Charlie"}, {$set: {age: 15}})
Deleting
db.dogs.deleteOne({name: "Charlie"})
db.dogs.deleteMany({})
Additional operators
- for nested properties:
db.dogs.find({'personality.childFriendly': true})
- for multiple properties:
db.dogs.find({'personality.childFriendly': true, age: 10})
- comparison, logical operators etc
db.dogs.find({ age: {$gt:20} })
- in operator: the value of a field equals any value in the specified array
db.dogs.find({breed: {$in ['Mutt', 'Corgi']}})
- or
db.dogs.find({$or: [{'personality.catfirendly': true}, {age: {$lte:2}}]})
Mongoose
- Connect node to MongoDB -> driver or Mongoose
- ODM: Object Data Mapper. Maps data from Mongo to JS objects
- ORM: same but for SQL data
Create a new folder and initialize npm
npm init -y
Install mongoose
npm i mongoose
Make an index.js
touch index.js
Require mongoose
const mongoose = require('mongoose');
main().catch(err => console.log(err));
async function main() {
//movieApp is the database
await mongoose.connect('mongodb://localhost:17017/movieApp');
console.log('connection open!')
}
Make a model (class) to interact with the database.
- Define a schema
const productSchema = new mongoose.Schema({
name: String,
price: Number,
quantity: Number
})
- make a model from the schema
//Product should be uppercase and singular.
//mongoose will create a collection called movies
const Product = mongoose.model('Product', productSchema)
- Create an instance of Movie
const amad = new Product({name: "Amad", price: 1986});
- Run node, load index.js from the project folder
node
.load index.js
- save it into mongodb
amad.save();
insert.Many()
Product.insertMany([
{name: 'Amel', price:2001, quantity:8.3, rating:'R'},
{name: 'The Iron', price:2004, quantity:3.3, rating:'R'},
])
.then(data => {
console.log("it works")
console.log(data)
})
No need to save into the db.
Finding
Can be treated as promises but they are not promises
Product.find({}).then(data => console.log(data))
Product.findById('123').then(data => console.log(data))
#### Updating
Product.updateOne({name:'Amad'},{price: 1984}).then( res => console.log(res))
does not return the object
Product.updateManu({name: {$in['Amd', 'sbyme']}}, {price:10}).then(res => console.log(res))
does not return the object
Product.findOneAndUpdate({name: 'tir'},{price: 7.0}).then(m => console.log(m))
we get back the (old) object
Product.findOneAndUpdate({name: 'tir'},{price: 7.0},{new: true}).then(m => console.log(m))
we get back the (new) object
Deleting
Product.remove({name: 'Amel'}).then(msg => console.log(msg))
Product.deleteMany({price: {$gte: 1999}}).then(msg => console.log(msg))
Product.findOneAndDelete(...)
return the deleted object
Schemas
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true,
lowercase: true,
trim: true,
maxlength: 10
},
price: {
type: Number,
required: true,
min: 0
},
onSale: {
type: Boolean,
default: false
},
qty: {
online: {
type: Number,
default: 0
},
inStore: {
type: Number,
default: 0
}
}
})
Validation applies when data is created. When updating, it has to be set:
Product.findOneAndUpdate({name: 'Tyre'}, {price: 10},{new: true, runValidators: true})
Validation Errors
price: {
type: Number,
required: true,
min: [0, "Price must be positive"]
},
size:{
type: String,
enum: ['S','M','L'] //only these strings are allowed
}
Model Instance
Do not use arrow function
productSchema.methods.toggleOnSale = function() {
this.onSale = !this.onSale;
return this.save();
}
productSChema.methods.addCategory = function(newCat){
this.categories.push(newCat);
return this.save();
}
const findProduct = async () => {
const foundProduct = await Product.findOne({name: 'whatever'});
await foundProduct.toggleOnSale();
await foundProduct.addCategory('Outdoors');
}
Static Methods
productSchema.statics.fireSale = function(){
return this.updateMany({},{onSale: true, price: 0})//return allows await
}
Product.fireSale().then(res => console.log(res))
Virtuals
Resource Add properties to schema that do not exist in the database but we can derive. i.e. full name in a database
const personSchema = new mongoose.Schema({
first: String,
last: String
})
personSchema.virtual('fullName').get(function () {
return `${this.first} ${this.last}`
})
set(function(v) {
const firstName = v.substring(0, v.indexOf(' '));
const lastName = v.substring(v.indexOf(' ') + 1);
this.set({ firstName, lastName });
});
Middleware
If youre move a user you can remove all his comments and activity:
.pre
.post
presonSchema.pre('save', async function(){
this.first = 'Yo';
this.last = 'Meme';
})
presonSchema.post('save', async function(){
...
})