# Mongo Module

Employee Collection :

We will be using the below collection to understand mongo operations:

collection Employee {
    property empId : Id
    property firstName:String
    property dob:CalendarDate
    property salary:Int
    property designation:CompanyData::Designation
}

# CRUD Operations

# 1. CREATE : Creating a employee record :

def addEmployee(_id:String, firstName:String, dob:CalendarDate, salary:Int, designation:CompanyData::Designation): Unit = {
  val employeeRow = EmployeeRow(     // creating a employee row 
    _id = id,
    firstName = firstName,
    dob = dob,
    salary = salary,
    designation= designation
    ) 
  EmployeeWriter().save(employeeRow)  // writing to db 
}

# 2. READ : Reading a record from database

Eg : Find the employee whose ID is 001

EmployeeQuery()._id.is(“001”).find()

# 3. UPDATE: Update a record in database collection

val q = EmployeeQuery()._id.is(“001”) // Finding a record which needs to be updated.
val u = EmployeeUpdate().salary.set(1000) // Updating salary to 1000
EmployeeWriter().updateOne(q,u)

# 4. REMOVE: Removing a record from database collection

    val query = EmployeeQuery().name.is(“john”) // Finding a record which needs to be removed.
    EmployeeWriter.remove(query)

# CREATE : Creating a row and writing it to collection in DB

  • User EmployeeRow for creating a row in the employee collection, which is like a holder to create the employees data.
  • Use EmployeeWriter to write the data to the DB.
    val employeeRow = EmployeeRow(
        _id = empId,
        firstName = firstName,
        dob = dob,
        salary = salary,
        designation= designation
        )
      EmployeeWriter().save(employeeRow)

# save() vs insert()

insert(): Always ADDS a row to the collection.
save(): Either ADDS a row or UPDATES if a row already exists with the provided _id.

// ADDS a row to the collection -> If NO row exists with the provided `_id`
// .save() and .insert() acts the same way as in this case id is always generated.
val employeeRow = EmployeeRow(
    _id = new Id(), // newly generated id
    firstName = firstName,
    dob = dob,
    salary = salary,
    designation= designation
    )
EmployeeWriter().save(employeeRow) // ADD a new row
                OR                
EmployeeWriter().insert(employeeRow) // ADD a new row
val employeeRow = EmployeeRow(
    _id = id, 
    firstName = firstName,
    dob = dob,
    salary = salary,
    designation= designation
    )
// ADDS a row to the collection -> If NO row exists with the provided `_id`
// UPDATES an existing row -> If a row already exists with the provided `_id`
EmployeeWriter().save(employeeRow) 
                OR
// ADDS a row to the collection -> If NO row exists with the provided `_id`
// Throws DuplicateEntryKey Error -> If a row already exists with the provided `_id`
EmployeeWriter().insert(employeeRow)

Note: Mongo generates its own _id if it's not provided.

# insertOne() vs insertMany()

  • InsertOne is used when you want to add only one record into the collection
val employeeRow = EmployeeRow(
    _id = id,
    firstName = firstName,
    dob = dob,
    salary = salary,
    designation= designation
    )
EmployeeWriter().insertOne(employeeRow)
  • InsertMany is used when you want to add multiple records at once into the collection
val _id1 = new Id();
val _id2 = new Id();
val _id3 = new Id();
val employeeRow1 = EmployeeRow(
    _id = id1,
    firstName = "john",
    dob = 12-12-2012,
    salary = 1000,
    designation= SE
    )
val employeeRow2 = EmployeeRow(
    _id = id2,
    firstName = "ron",
    dob = 13-02-1996,
    salary = 20000,
    designation= SSE
    )
val employeeRow3 = EmployeeRow(
    _id = id3,
    firstName = "steve",
    dob = 3-06-1995,
    salary = 40000,
    designation= CEO
    )
EmployeeWriter().insertMany(employeeRow1,employeeRow2,employeeRow3)

# Query : Reading a record from data base

# find()

  • Find on a query returns a list of all matching records from the collection.
  • If no matching records are found it returns an empty list.
//  The above example returns the record of all employees whose salary is 1000.
val q = EmployeeQuery().salary.is(1000).find()
output : List[EmployeeRow]

# findOne()

  • FindOne on a query returns only one record, i.e the first matching occurrence of the matching record.
  • If no value is found it returns a None , therefore findOne() is an Option[<Row>].
\\ The above example returns the first matching record whose salary is 1000 else it returns None.
val q = EmployeeQuery().empId.is("A001").findOne()
output : Option[EmployeeRow]
  • Since we have got a optional field
    • We can return a option i.e Option[EmployeeRow].
    • We can do a .get and return EmployeeRow if the employees existance is already checked in pre condition.
  • we can use asc and desc methods to arrange the records in ascending / descending order and then findOne().
    val q = EmployeeQuery().designation.is("SE").dob.desc.findOne()
    output : Option[EmployeeRow]

# limit()

  • Limit method limits the number of records to be returned or passed to the next query builder.
    val q = EmployeeQuery().designation.is("SE").limit(5).find()
    output : List[EmployeeRow]
  • Returns <= 5 records from the collection which matches the query.

# Query Building

    val q1 = EmployeeQuery().designation.is(“SE”)
    val q2 = q1.salary.lessOrEqual(10000)
    val q3 = q1.salary.greaterOrEqual(5000)
    q3.find() // list of employees with designation SE and salary <=10000 and >= 5000
    q2.find() // list of employees with designation SE and salary <=10000 and >= 5000
    q1.find() // list of employees with designation SE and salary <=10000 and >= 5000
  • q2 is the query for designation SE and salary <= 10000
  • find() gives list of employees with designation SE and salary <=10000 and >= 5000 but it looks like it only gives designation SE and salary >= 5000 which is an most important point to note, if not taken care gives wrong data.
  • MongoQuery is a builder i.e no new query is created just by creating a new variable, it just appends the query.
  • q1 = q1 = q3
  • If you want to do q2 and q3 independently without appending to q1 :
    • Use copy method
    val q1 = EmployeeQuery().designation.is(“SE”)
    val q2 = q1.copy.salary.lessOrEqual(10000).find()
    val q3 = q1.copy.salary.greaterOrEqual(5000).find()

# Points To note

    val q1 = EmployeeQuery().designation.is(“SE”).salary.lessOrEqual(10000).find()
    val q2 = EmployeeQuery().designation.is(“SE”).salary.greaterOrEqual(5000).find()
    val q3 = EmployeeQuery().designation.is(“SE”).name.is("john").find()
  • In the above example for the 3 queries “EmployeeQuery().designation.is(“SE”)” is common , so its a best practice to move the query to a separate variable and use the variable with copy method.
    val qDesig = EmployeeQuery().designation.is(“SE”)
    // **** DON'T FORGET TO USE COPY ****
    val q1 = qDesig.copy.salary.lessOrEqual(10000).find()
    val q2 = qDesig.copy.salary.greaterOrEqual(5000).find()
    val q3 = qDesig.copy.name.is("john").find()
    val q1 = EmployeeQuery().designation.is(“SE”).salary.lessOrEqual(10000).findOne
    if(q1 != None) {
      Println(“got the data”)
    }else
    {
      Println(“data not found”)
    }
  • Always try to avoid if conditions in the code instead of if pattern matching can be used :
    val q1 = EmployeeQuery().designation.is(“SE”).salary.lessOrEqual(10000).findOne
    q1 match {
    case Some(employeeRow) => Println(“got the data”)
    case None = > Println(“data not found”)
    }

# Indexing

  • Indexes support the efficient execution of queries in MongoDB.
  • Without indexes, MongoDB must perform a collection scan, i.e. scan every document in a collection, to select those documents that match the query statement.
  • If an appropriate index exists for a query, MongoDB can use the index to limit the number of documents it must inspect.
  • Indexes are special data structures that store a small portion of the collection’s data set in an easy to traverse form.
  • Without Indexing Example :
  • With Indexing Example:

Indexing designation :

collection Employe {
    property id : Id
    property firstName:String
    property dob:CalendarDate
    property salary:Int
    property designation:CompanyData::Designation
    
    index desingationIdIndex {
        up designation
    }
}

# lazyFind()

  • todo

# Update

# updateOne

  • Used to update the existing row in a collection.
  • It takes two parameters the query criteria and the update modifications to apply.
    val q = EmployeeQuery()._id.is("A001") // query employee with A001 as employee Id
    val u = EmployeeUpdate().salary.set(20000) // update salary to 20000
    EmployeeWriter().updateOne(q,u)

# updateMulti

  • Used to update the existing rows in a collection.
  • It takes two parameters the query criteria and the update modifications to apply.
    val q = EmployeeQuery().designation.is("SE") // query employee designation  = SE
    val u = EmployeeUpdate().designation.set("SSE") // change designation to SSE
    EmployeeWriter().updateMulti(q,u)
  • updates all employees whose designation is “SE” to “SSE”

# Delete

# remove()

  • Removes documents from a collection.
  • Eg : Remove with no Arguments
    EmployeeWriter().remove()
  • Removes all the rows from the employee collection.
  • Remove also takes a query for deleting rows which match it.
  • Eg : Remove with argument (query)
    val q = EmployeeQuery().designation.is("SE").salary.is(10000)
    EmployeeWriter().remove(q)
  • Removes all the marching query.
  • removeById takes a smile Id as a parameter to delete the particular row.

# drop()

    EmployeeWriter().drop()
  • Drops the employee database collection.