MongoDB For Developers

https://www.udemy.com/course/mongodb-the-complete-developers-guide/

Humongous, Because it can store lots and lots of data.

NoSQL - instead of normalizing data which means storing it, distribute it across multiple tables where every table has a clear schema and then using a lot of relations, instead of doing that, MongoDB goes for storing data together in a document.

Mongodb Compass (GUI)

MongoDB Compass Download

Installing MongoDB Ubuntu 20.04 Focal

Install MongoDB Community Edition on Ubuntu

wget -qO - <https://www.mongodb.org/static/pgp/server-5.0.asc> | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] <https://repo.mongodb.org/apt/ubuntu> focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org

Running MongoDB

# terminal 1
sudo mongod --dbpath ~/codespace/mongodb/data/ --logpath ~/codespace/mongodb/logs/mongo.log
# terminal 2
mongo / mongosh
# to run daemon
sudo mongod --fork --dbpath ~/codespace/mongodb/data/ --logpath ~/codespace/mongodb/logs/mongo.log

Running mongoDB using config

sudo mongod -f ~/codespace/mongodb/mongod.cfg

storage:
  dbPath: "data"
systemLog:
  destination: file
  path: "logs/log.log"

shutdown mongo service

mongosh
use admin
db.shutdownServer()

Commands

# show databases
show dbs

# use database
use shop

# delete database 
db.dropDatabase()

# insert
db.products.insertOne({"name": "A book", "price": 12.99})

#show all Products
db.products.find()

#delete one
db.flightData.deleteOne({departureAirport: "TXL"})

#update and add field
db.flightData.updateOne({departureAirport: 'MUC'}, {$set: {marker: 'delete'}})
db.flightData.update({}, {$set: {marker: "toDelete"}})

#less than and greater then 
db.flightData.find({distance: {$lt:10000}})
db.flightData.find({distance: {$gt:10000}})

# get all using find
db.passengers.find().toArray()
db.passengers.find().forEach((passengerData) => {printjson(passengerData)})
# show only name and exclude id
db.passengers.find({}, {name: 1, _id:0})
## embed document
db.movies.find({ "rating.average": { $gt: 7 } })
## grab genres drama
db.movies.find({ "genres": "Drama" })
## grab only that has array Drama
db.movies.find({ "genres": ["Drama"] })
## grab runtime equals to 30 & 42
db.movies.find({ "runtime": { $in: [30, 42]} })
## grab runtime not equals to 30 & 42
db.movies.find({ "runtime": { $nin: [30, 42]} })
## or operator
db.movies.find({ $or: [{"rating.average": {$lt: 5} }, {"rating.average": {$gt: 9.3}}] })
## nor operator
db.movies.find({ $nor: [{"rating.average": {$lt: 5} }, {"rating.average": {$gt: 9.3}}] })
## and operator - use this when theres the same field (different driver like in js can't read same fields
db.movies.find({ $and: [{"rating.average": {$gt: 9} }, {"genres": "Drama"}] })
db.movies.find({ $not: [{"rating.average": {$gt: 9} }, {"genres": "Drama"}] })
db.movies.find({ "genres": "Horror", "genres": "Drama" })
## show only that has field age
db.users.find({age: {$exists: true} })
db.users.find({age: {$exists: true, $ne: null} })
## show only phone that has type string
db.users.find({phone: {$type: "string"}})
db.users.find({phone: {$type: ["number","string"]}})
## regex
db.movies.find({ summary: {$regex: /musical/} })

## not equal - query below are the same
db.movies.find({ runtime: {$not: {$eq: 60}} }).count()
db.movies.find({ runtime: {$ne: 60} }).count()

# embed document
db.flightData.updateMany({}, {$set: { status: {description: "on-time", lastUpdated: "1 hour ago", details: {responsible: "max"} } }})

# update an array
db.passengers.updateOne({name: "Albert Twostone"}, {$set: {hobbies: ["sports", "cooking"]}})
# accessing an array
db.passengers.findOne({name: 'Albert Twostone'}).hobbies
db.passengers.findOne({hobbies: 'sports'})
db.flightData.find({"status.description": "on-time"})
db.flightData.find({"status.details.responsible": "max"})

#datatypes
db.companies.insertOne({ name: "Fresh Apples Inc", isStartup: true, employees: 33, funding: 12323123123123122, details: { ceo: "Mark Super" }, tags: [{title: "super"}, {title: "perfect"}], foundingDate: new Date(), insertedAt: new Timestamp() })
#check current db status
db.stats()

db.books.insertOne({ name: "my fav books", authors: [{name:"max", age: 29}, {name: "madindo", age:35}] })
db.authors.insertMany( [{name: "max", age: 29, address: {street: "Main"}}, {name: "madindo", age:35, address: {street: "tree"}} ])
db.books.updateOne({}, {$set: {authors: [ObjectId("612a51d5046c185f41e62ee3"), ObjectId("612a51d5046c185f41e62ee4")]}})
# $lookup field - making a new field by joining other table like
db.books.aggregate( [ {$lookup: { from: "authors", localField: "authors", foreignField: "_id", as: "creators" } } ] )

# ordered insert
# disable ordered insert - use , { ordered: false }
db.hobbies.insertMany([{_id: "sports", name: "sports"}, {_id: "cooking", name: "Cooking"}, {_id: "cars", name: "Cars"}] , { ordered: false })

# write concern
## means this will be very fast
db.persons.insertOne({name: "chrissy", age: 41}, { writeConcern: { w:0 } })
## means this will be added to its to do list which more better for security
db.persons.insertOne({name: "micheal", age: 51}, { writeConcern: { w:1, j:true } })
## adds timeout on queue it'll be more longer
db.persons.insertOne({name: "micheal", age: 51}, { writeConcern: { w:1, j:true, wtimeout:200 } })

# comparison operators
## not equals
db.movies.find( {runtime: {$ne: 60} } )
## less than & greater than
db.movies.find( {runtime: {$lt: 40} } )
db.movies.find( {runtime: {$gt: 40} } )
## less than equals & greater than equals
db.movies.find( {runtime: {$lte: 40} } )
db.movies.find( {runtime: {$gte: 40} } )

db.sales.insertMany([{ volume: 100, target: 120}, {volume: 89, target: 80}, {volume: 200, target: 177}])

# will compare 2 value
db.sales.find( { $expr: { $gt: ["$volume", "$target"] } })

# else if statement query
db.sales.find( { $expr: { $gt: [{$cond: {if: {$gte: ["$volume", 190]}, then: {$subtract: ["$volume", 10]}, else: "$volume"}}, "$target"]}})
db.sales.find( { $expr: { $gt: [{$cond: {if: {$gte: ["$volume", 190]}, then: {$subtract: ["$volume", 30]}, else: "$volume"}}, "$target"]}})

# frequency has to be the exact value
db.users.find({hobbies: {title: "Sports", frequency: 3} })
# doesn't have to have frequency
db.users.find({"hobbies.title": "Sports"})
# find hobbies exact 3
db.users.find({hobbies: {$size: 3}})
# doesnt care about order, just to make sure the value exist
db.movies.find({genres: {$all: ["Action", "Crime"]}})

db.users.find({hobbies: {$elemMatch: {title: "Cooking", frequency: {$gte: 6}} }})
#cursor
db.movies.find().next()
const dataCursor = db.movies.find()
dataCursor.forEach(doc => {printjson(doc)})
dataCursor.hasNext()

#sort, skip, limit
db.movies.find().sort({"rating.average": 1, runtime: -1})
db.movies.find().sort({"rating.average": 1, runtime: -1}).skip(10).limit(10)

#projection
db.movies.find({}, {name: 1, genres: 1, runtime: 1, rating: 1, _id: 0, "schedule.time": 1})

db.movies.find({genres: {$all: ["Drama", "Horror"]}}, {"genres.$": 1})
db.movies.find({genres: "Drama"}, {"genres.$": 1})
db.movies.find({genres: "Drama"}, {"genres": {$elemMatch: {$eq: "Horror"}} })

db.movies.find({"rating.average": {$gt: 9}}, {genres: {$slice: 2}, name: 1} )

Update

#updates
db.users.updateOne({_id: ObjectId("613f3a0c5a75da440a5a5d0c")}, {$set: {hobbies: [{title: "Sports", frequency: 5}, {title: "Cooking", frequency:3}, {title: "Hiking", frequency:1  }]}})
db.users.updateMany({"hobbies.title": "Sports"}, {$set: {isSporty: true}})

db.users.updateOne({name: "manuel"}, {$inc: {age: 2}})
db.users.updateOne({name: "manuel"}, {$inc: {age: 1}, $set: {isSporty: false}})

db.users.updateOne({name: "Chris"}, {$min: {age: 35}})
db.users.updateOne({name: "Chris"}, {$max: {age: 32}})
db.users.updateOne({name: "Chris"}, {$mul: {age: 1.1}})

// removing field
db.users.updateMany({isSporty: true} , {$unset: {phone: ""}})
//rename field
db.users.updateMany({}, {$rename: {age: "totalAge"}})
// update or insert
db.users.updateOne({name: "Maria"}, {$set: {age:29, hobbies: [{title: "Good food", frequency: 3}], isSporty: true}}, {upsert: true})
//adding array with filter inside filter
db.users.updateMany({hobbies: {$elemMatch: {title: "Sports", frequency: {$gte: 3}}}}, {$set: {"hobbies.$.highFrequency": true }})
////updateing array element
db.users.update({totalAge: {$gt:30}}, {$inc: {"hobbies.$[].frequency": -1} })
// complex array element
db.users.updateMany({"hobbies.frequency" : {$gt: 2}}, {$set: {"hobbies.$[el].goodFrequency": true }}, {arrayFilters: [{"el.frequency": {$gt: 2}}]})
// update push arryaay
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {title: "Sports", frequency: 2}} } )
// same as above but unique
db.users.updateOne({name: "Maria"}, {$addToSet: {hobbies: {title: "Hiking", frequency: 2}} } )
// update push array then sort them
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {$each: [{title: "Good Wine", frequency: 1}, {title: "Hiking", frequency: 2}], $sort: {frequency: -1} }  } } )
// remove an array using update
db.users.updateOne({name: "Maria"}, {$pull: {hobbies: {title: "Hiking"}}})
// remove first or last array
db.users.updateOne({name: "Chris"}, {$pop: {hobbies: -1}})

Delete

# delete
db.users.deleteOne({name: "Chris"})
//delete if totalAge is not exist and is sporty
db.users.deleteMany({totalAge: {$exists: false}, isSporty: true})
db.users.deleteMany({})
// delete collection
db.users.drop()
/// delete database
db.dropDatabase()

Indexes

db.contacts.explain().find()
db.contacts.explain("executionStats").find({"dob.age": {$gt: 60}})
db.contacts.createIndex({"dob.age": 1})
db.contacts.dropIndex({"dob.age": 1})
db.contacts.getIndexes()
db.contacts.createIndex({email: 1}, {unique: true})

db.contacts.createIndex({"dob.age": 1}, {partialFilterExpression: {gender:"male"}})

// after inserting it will be gone after 10 seconds (TTL)
db.sessions.createIndex({createdAt: 1}, {expireAfterSeconds: 10})

// this will index the whole text
db.products.createIndex({description: 1})
// this will index each text
db.products.createIndex({description: "text"})
db.products.find({$text: {$search: "book"}})
db.products.find({$text: {$search: "awesome"}}, {score: {$meta: "textScore"}}).sort({score: {$meta: "textScore"}})
// exclude a word
db.products.find({$text: {$search: "awesome -t-shirt"}})
// default language will remove unused words such as "is" "are" from index
// weights title is less important than description
db.products.createIndex({title: "text", description: "text"}, {default_language: "english", weights: {title: 1, description: 10}})
// case sensitive
db.products.find({$text: {$search: "Awesome", $caseSensitive: true}})

Building indexes

Foreground - faster - Collection is locked during index creation

Background - slower - Collection is accessible during index creation

db.ratings.createIndex({age: 1},{background: true})

Crud Operations

//Create
insertOne(data, options)
insertMany(data, options)

//Read
find(filter, options)
findOne(filter, options)

//Update
updateOne(filter, data, options)
updateMany(filter, data, options)
replaceOne(filter, data,options)

//Delete
deleteOne(filter, options)
deleteMany(filter, options)

When would you use embedded document for relation - 1 to 1 relation else if you keep want to use relation

db.persons.insertOne( {name: "Max", age: 29, salary: 3000} )
db.cars.insertOne( { model: "BMW", price: 40000, owner: ObjectId("61264e0a046c185f41e62eda") } )

create collection document validation

db.createCollection("posts", {validator: { $jsonSchema: { bsonType: "object", required: ['title', 'text', 'creator', 'comments'], properties: { title: { bsonType: "string", description: "must be a string and is required"},text: { bsonType: "string", description: "must be a string and is required" }, creator: { bsonType: "objectId",description: "must be an objectId and is required" }, comments: { bsonType: "array", required: ['text', 'author'], items: { bsonType: "object", properties: { text: { bsonType: "text", description: "must be a string and is required" }, author: { bsonType: "objectId", description: "must be an objectId and is required" } } } } } } } })

update collection document validation

db.runCommand({collMod:"posts", validator: { $jsonSchema: { bsonType: "object", required: ['title', 'text', 'creator', 'comments'], properties: { title: { bsonType: "string", description: "must be a string and is required"},          text: { bsonType: "string", description: "must be a string and is required" }, creator: { bsonType: "objectId",description: "must be an objectId and is required" }, comments: { bsonType: "array", required: ['text', 'author'], items: { bsonType: "object", properties: { text: { bsonType: "string", description: "must be a string and is required" }, author: { bsonType: "objectId", description: "must be an objectId and is required" } } } } } } }, validationAction: 'warn' })

Atomaticity is same as transactional query where when something fails it will rollback the query.

Import Data

mongoimport tv-shows.json -d movieData -c movies --jsonArray --drop

Query Diagnosis & Query Planning

queryPlanner - Show summary for executed query + winning plan

executionStats - Show detailed summary for executed query + winning plan + possibly rejected plans

allPlansExecution - Show detailed Summary for executed query + winning plan + winning plan decision process

db.customers.explain("executionStats").find({name: "Max"})
db.customers.explain("executionStats").find({name: "Max"}, {_id: 0, name: 1})

db.customers.explain("allPlansExecution").find({name: "Max", age:30})
db.contacts.explain("executionStats").find({"addresses.street": "Main Street"})

Executing a js file

# credit-rating.js
conn = new Mongo();
db = conn.getDB("credit");

for (let i = 0; i < 1000000; i++) {
    db.ratings.insertOne({
        "person_id": i + 1,
        "score": Math.random() * 100,
        "age": Math.floor(Math.random() * 70) + 18 
    })
}
$ mongo credit-rating.js

GeoSpatial

db.places.insertOne({name: "California Academy of Sciences", location: {type: "Point", coordinates: [-122.4724356, 37.7672544]}})
db.places.insertOne({name: "Conservatory of Flowers", location: {type: "Point", coordinates: [-122.4615748, 37.7701756]}})
db.places.insertOne({name: "Golden Gate Park Tennis Courts", location: {type: "Point", coordinates: [-122.4593702, 37.7705046]}})
db.places.insertOne({name: "Nopa", location: {type: "Point", coordinates: [-122.4389058, 37.7747415]}})

db.places.createIndex({location: "2dsphere"})
//find the nearest to this coordinates
db.places.find({location: {$near: {$geometry: {type: "Point", coordinates: [-122.471114, 37.771104] } } } })
// use max distance
db.places.find({location: {$near: {$geometry: {type: "Point", coordinates: [-122.471114, 37.771104] }, $maxDistance: 500, $minDistance: 10 } } })
// find using polygon
db.places.find({location: {$geoWithin: {$geometry: {type: "Polygon", coordinates: [[p1,p2,p3,p4,p1]] }} } })

// inserting area
db.areas.insertOne({name: "Golden Gate Park", area: {type: "Polygon", coordinates: [[p1,p2,p3,p4,p1]] } })
db.areas.createIndex({area: "2dsphere"})
// to find point in one area
db.areas.find({area: {$geoIntersects: {$geometry: {type: "Point", coordinates: [-122.49089, 37.76992] }}}})

Aggregate Framework

// can modify from each pipeline
db.persons.aggregate([ { $match: { gender: "female" } }])
db.persons.aggregate([ { $match: { gender: 'female' } }, { $group: { _id: { state: "$location.state" }, totalPersons: { $sum: 1 } } }])
db.persons.aggregate([ { $match: { gender: 'female' } }, { $group: { _id: { state: "$location.state" }, totalPersons: { $sum: 1 } } }, { $sort: {totalPersons: -1 } }])

db.persons.aggregate([ {$project: { _id: 0, gender: 1, fullName: { $concat: ["$name.first", " ", "$name.last" ] } } } ])
db.persons.aggregate([ {$project: { _id: 0, gender: 1, fullName: { $concat: [{$toUpper: "$name.first"}, " ", {$toUpper: "$name.last"} ] } } } ])

db.persons.aggregate([ {$project: { _id: 0, gender: 1, fullName: { $concat: [{$toUpper: { $substrCP:["$name.first", 0, 1]} }, {$substrCP: ["$name.first", 1, {$subtract : [{$strLenCP: "$name.first"}, 1] } ]},  " ", {$toUpper: { $substrCP: ["$name.last", 0, 1]} }, {$substrCP: ["$name.last", 1, {$subtract : [{$strLenCP: "$name.last"}, 1] } ]} ] } } } ])

db.persons.aggregate([ {$project: {_id: 0, name: 1, email: 1, location: {type: 'Point',coordinates: ['$location.coordinates.longitude','$location.coordinates.latitude']}}},{$project: { _id: 0, location:1, email:1, fullName: { $concat: [{$toUpper: { $substrCP:["$name.first", 0, 1]} }, {$substrCP: ["$name.first", 1, {$subtract : [{$strLenCP: "$name.first"}, 1] } ]},  " ", {$toUpper: { $substrCP: ["$name.last", 0, 1]} }, {$substrCP: ["$name.last", 1, {$subtract : [{$strLenCP: "$name.last"}, 1] } ]} ] } } } ])

//convert to double
db.persons.aggregate([ {$project: {_id: 0, name: 1, email: 1, location: { type: 'Point', coordinates: [ { $convert :{ input: '$location.coordinates.longitude', to: "double", onError: 0, onNull: 0.0}},  { $convert : { input: '$location.coordinates.latitude', to: "double", onError: 0, onNull: 0.0}} ] }}},{$project: { _id: 0, location:1, email:1, fullName: { $concat: [{$toUpper: { $substrCP:["$name.first", 0, 1]} }, {$substrCP: ["$name.first", 1, {$subtract : [{$strLenCP: "$name.first"}, 1] } ]},  " ", {$toUpper: { $substrCP: ["$name.last", 0, 1]} }, {$substrCP: ["$name.last", 1, {$subtract : [{$strLenCP: "$name.last"}, 1] } ]} ] } } } ])

//convert to date
db.persons.aggregate([ {$project: {_id: 0, name: 1, email: 1, birthdate: { $convert: { input: "$dob.date", to: "date"}}, age: "$dob.age", location: { type: 'Point', coordinates: [ { $convert :{ input: '$location.coordinates.longitude', to: "double", onError: 0, onNull: 0.0}},  { $convert : { input: '$location.coordinates.latitude', to: "double", onError: 0, onNull: 0.0}} ] }}},{$project: { _id: 0, location:1, email:1, birthdate: 1, age: 1, fullName: { $concat: [{$toUpper: { $substrCP:["$name.first", 0, 1]} }, {$substrCP: ["$name.first", 1, {$subtract : [{$strLenCP: "$name.first"}, 1] } ]},  " ", {$toUpper: { $substrCP: ["$name.last", 0, 1]} }, {$substrCP: ["$name.last", 1, {$subtract : [{$strLenCP: "$name.last"}, 1] } ]} ] } } } ])

// shortcut date
db.persons.aggregate([ {$project: {_id: 0, name: 1, email: 1, birthdate: { $toDate: "$dob.date" }, age: "$dob.age", location: { type: 'Point', coordinates: [ { $convert :{ input: '$location.coordinates.longitude', to: "double", onError: 0, onNull: 0.0}},  { $convert : { input: '$location.coordinates.latitude', to: "double", onError: 0, onNull: 0.0}} ] }}},{$project: { _id: 0, location:1, email:1, birthdate: 1, age: 1, fullName: { $concat: [{$toUpper: { $substrCP:["$name.first", 0, 1]} }, {$substrCP: ["$name.first", 1, {$subtract : [{$strLenCP: "$name.first"}, 1] } ]},  " ", {$toUpper: { $substrCP: ["$name.last", 0, 1]} }, {$substrCP: ["$name.last", 1, {$subtract : [{$strLenCP: "$name.last"}, 1] } ]} ] } } } ])

// group by and sort
db.persons.aggregate([ {$project: {_id: 0, name: 1, email: 1, birthdate: { $toDate: "$dob.date" }, age: "$dob.age", location: { type: 'Point', coordinates: [ { $convert :{ input: '$location.coordinates.longitude', to: "double", onError: 0, onNull: 0.0}},  { $convert : { input: '$location.coordinates.latitude', to: "double", onError: 0, onNull: 0.0}} ] }}},{$project: { _id: 0, location:1, email:1, birthdate: 1, age: 1, fullName: { $concat: [{$toUpper: { $substrCP:["$name.first", 0, 1]} }, {$substrCP: ["$name.first", 1, {$subtract : [{$strLenCP: "$name.first"}, 1] } ]},  " ", {$toUpper: { $substrCP: ["$name.last", 0, 1]} }, {$substrCP: ["$name.last", 1, {$subtract : [{$strLenCP: "$name.last"}, 1] } ]} ] } } }, { $group: { _id: {birthYear: { $isoWeekYear: "$birthdate" } }, numPersons: {$sum:1} } }, {$sort : {numPersons: -1} } ])

// adding in a new array
db.friends.aggregate({
  $group : {_id: {age: "$age"}, allHobbies: {$push: "$hobbies"} }
})
// unwind - has duplicates
db.friends.aggregate([
  { $unwind : "$hobbies"},
  { $group : {_id: {age: "$age"}, allHobbies: {$push: "$hobbies"} }}
])
// unwind - remove duplicates
db.friends.aggregate([ { $unwind: "$hobbies" }, { $group: { _id: { age: "$age" }, allHobbies: { $addToSet: "$hobbies" } } }])

// first
db.friends.aggregate([
  { $project: {_id: 0, examScore: { $slice: ["$examScores", 1] } } }
])
// the last 2
db.friends.aggregate([
  { $project: {_id: 0, examScore: { $slice: ["$examScores", -2] } } }
])
// start at 2
db.friends.aggregate([
  { $project: {_id: 0, examScore: { $slice: ["$examScores", 2,1] } } }
])
// getting the length of an array
db.friends.aggregate([
  { $project: {_id: 0, numScores: {$size: "$examScores"} } }
])

db.friends.aggregate([ 
    { $project: { _id: 0, scores: { $filter: { input: "$examScores", as: "sc", cond: { $gt: ["$$sc.score", 60] } } } } }
])

//buckets
db.persons.aggregate([ 
  { 
    $bucket: { 
      groupBy: "$dob.age", 
      boundaries: [18, 30, 40,50,60, 120],
      output: {
        numPerson: { $sum: 1 },
        averageAge: {$avg: "$dob.age"}
      }
    } 
  }
])
db.persons.aggregate([ 
  { 
    $bucketAuto: { 
      groupBy: "$dob.age", 
      buckets: 5,
      output: {
        numPerson: { $sum: 1 },
        averageAge: {$avg: "$dob.age"}
      }
    } 
  }
])

// get the olderst man
db.persons.aggregate([
  { $match: {gender: "male"} },
  { $project: {_id: 0, name: { $concat: ["$name.first", " ", "$name.last"] }, birthdate: { $toDate: "$dob.date"} } },
  { $sort: { birthdate: 1 } },
  { $skip: 10 },
  { $limit: 10 }
])

// get the result to a new collection / table
db.persons.aggregate([
  { $match: {gender: "male"} },
  { $project: {_id: 0, name: { $concat: ["$name.first", " ", "$name.last"] }, birthdate: { $toDate: "$dob.date"} } },
  { $sort: { birthdate: 1 } },
  { $skip: 10 },
  { $limit: 10 },
  { $out: "newPersons" }
])

// aggregate using geoNear
db.newPersons.aggregate([ { $geoNear: { near: { type: "Point", coordinates: [-18.4, -42.8] }, maxDistance: 1000000, $limit: 10, query: { age: { $gt: 30 } }, distanceField: "distance" } }])

$group vs $project

$group - Sum, Count, Average, Build array

$project - Include/exclude Fields, Transform Fields (within a single document)

Working with numbers

db.persons.insertOne({age: NumberInt("20")})
db.persons.insertOne({age: 5000000000000})
//need to use the quote
db.persons.insertOne({age: NumberLong("5000000000000")})

// not precise
db.science.insertOne({a: 0.3, b:0.1})
db.science.aggregate([{$project: {result: {$subtract: ["$a", "$b"]} } }])
//precise
db.science.insertOne({a: NumberDecimal("0.3"), b: NumberDecimal("0.1")})
db.science.aggregate([{$project: {result: {$subtract: ["$a", "$b"]} } }])

Security

  • Authentication & Authorization

Authentication - Identifies valid users of the database

Authorization - Identifies what these users may actually do in database

Role based access control

Transport Encryption - data that sent to your app to the server should be encrypted

Encryption at Rest - data in the database also should be encrypted

Auditing

Server & network config and setup

Backup and software updates

User

use admin
db.createUser({user: "madindo", pwd: "madindo123", roles: ["userAdminAnyDatabase"]})
db.auth('madindo','madindo123')

Built-in Roles

  • Database User

read, readWrite

  • Database Admin

dbAdmin, userAdmin, dbOwner

  • All Database Roles

readAnyDatabase, readWriteAnyDatabase, userAdminAnyDatabase, dbAdminAnyDatabase

  • Cluster Admin

clusterManager, clusterMonitor, hostManager, clusterAdmin

  • Backup/restore

backup, restore

  • Superuser

dbOwner (admin), userAdmin (admin), userAdminAnyDatabase ( root )

mongo -u madindo -p password --authenticationDatabase admin
use shop
db.createUser({user: 'appdev', pwd: 'dev', roles: ["readWrite"]})
db.auth('appdev', 'dev')

mongo -u appdev -p dev --authenticationDatabase shop
db.getUser("appdev")

SSL

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365
// capped collection
db.createCollection("capped", {capped: true, size: 10000, max: 3})

Replica

Sharding (Horizontal Scaling)

Transaction

const session = db.getMongo().startSession()
session.startTransaction()

session.commitTransaction()
session.abortTransaction()

Mongodb

const mongodb = require('mongodb').MongoClient;

mongodb.connect('mongodb+srv://madindo:<password>@cluster0.1sjg7.mongodb.net/shop?retryWrites=true&w=majority')
.then(client => {
  console.log('Connected');
  client.close();
})
.catch(err => {
  console.log(err);
})

Stitch

Serverless Platform for building application

  • uses cloud database (Atlas)
  • Authentication
  • Access to (atlas) database
  • stich queryanywhere
  • mongoDB Mobile
  • React to Events
  • stitch triggers
  • Execute code / functionality in the cloud
  • stitch function
  • stitch services

Serverless?

client → backend → MongoDB /  Atlas

now client → stitch → MongoDB /  Atlas

Subscribe to You Live What You Learn

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe