10/29/15

M101J: MongoDB for Java Developers : Homework : Week 3

Hi Readers,

Here are answers for Mongo Course session October 2015, Week 3.

Homework 3.1

Download the students.json file from the Download Handout link and import it into your local Mongo instance with this command:
  1. mongoimport -d school -c students < students.json
This dataset holds the same type of data as last week's grade collection, but it's modeled differently. You might want to start by inspecting it in the Mongo shell.
Write a program in the language of your choice that will remove the lowest homework score for each student. Since there is a single document for each student containing an array of scores, you will need to update the scores array and remove the homework.
Remember, just remove a homework score. Don't remove a quiz or an exam!
Hint/spoiler: With the new schema, this problem is a lot harder and that is sort of the point. One way is to find the lowest homework in code and then update the scores array with the low homework pruned.
To confirm you are on the right track, here are some queries to run after you process the data with the correct answer shown:
Let us count the number of students we have:
  1. use school
  2. db.students.count()
The answer will be 200.
Let's see what Tamika Schildgen's record looks like once you have removed the lowest score:
  1. db.students.find( { _id : 137 } ).pretty( )
This should be the output:
{
 "_id" : 137,
 "name" : "Tamika Schildgen",
 "scores" : [
  {
   "type" : "exam",
   "score" : 4.433956226109692
  },
  {
   "type" : "quiz",
   "score" : 65.50313785402548
  },
  {
   "type" : "homework",
   "score" : 89.5950384993947
  }
 ]
}
To verify that you have completed this task correctly, provide the identity (in the form of their _id) of the student with the highest average in the class with following query that uses the aggregation framework. The answer will appear in the _id field of the resulting document.
  1. db.students.aggregate( { '$unwind' : '$scores' } , { '$group' : { '_id' : '$_id' , 'average' : { $avg : '$scores.score' } } } , { '$sort' : { 'average' : -1 } } , { '$limit' : 1 } )

Answer is :- 13






Homework 3.1 and 3.2


Question 3.1 was

Making your blog accept posts
In this homework you will be enhancing the blog project to insert entries into the posts collection. After this, the blog will work. It will allow you to add blog posts with a title, body and tags and have it be added to the posts collection properly.
We have provided the code that creates users and allows you to login (the assignment from last week). To get started, please download blog.zip from the Download Handout link and unpack. You will be using these file for this homework and for homework 3.3.
The areas where you need to add code are marked with "XXX". You need only touch the BlogPostDAO class. There are three locations for you to add code for this problem. Scan that file for XXX to see where to work.
Here is an example of valid blog post:
> db.posts.find().pretty()
{
    "_id" : ObjectId("513d396da0ee6e58987bae74"),
    "title" : "Martians to use MongoDB",
    "author" : "andrew",
    "body" : "Representatives from the planet Mars announced today that the planet would adopt MongoDB as a planetary standard. Head Martian Flipblip said that MongoDB was the perfect tool to store the diversity of life that exists on Mars.",
    "permalink" : "martians_to_use_mongodb",
    "tags" : [
        "martians",
        "seti",
        "nosql",
        "worlddomination"
    ],
    "comments" : [ ],
    "date" : ISODate("2013-03-11T01:54:53.692Z")
}
Note: You must add at least one post like the one above to the posts collection before running the blog.
As a reminder, to run your blog you type:
mvn compile exec:java -Dexec.mainClass=course.BlogController

Question 3.2 was

Making your blog accept posts
In this homework you will add code to your blog so that it accepts comments. You will be using the same code as you downloaded for HW 3.2. 

Once again, the area where you need to work is marked with an XXX in the BlogPostsDAO class. There is only a single location you need to work to insert comments. You don't need to figure out how to retrieve comments for this homework because the code you did in 3.2 already pulls the entire blog post (unless you specifically projected to eliminate the comments) and we gave you the code in the template that pulls them out of the JSON document. 

This assignment has fairly little code, but it's a little more subtle than the previous assignment because you are going to be manipulating an array within the Mongo document. For the sake of clarity, here is a document out of the posts collection from a working project that also has comments. 
{
 "_id" : ObjectId("513d396da0ee6e58987bae74"),
 "author" : "andrew",
 "body" : "Representatives from the planet Mars announced today that the planet would adopt MongoDB as a planetary standard. Head Martian Flipblip said that MongoDB was the perfect tool to store the diversity of life that exists on Mars.",
 "comments" : [
  {
   "author" : "Larry Ellison",
   "body" : "While I am deeply disappointed that Mars won't be standardizing on a relational database, I understand their desire to adopt a more modern technology for the red planet.",
   "email" : "larry@oracle.com"
  },
  {
   "author" : "Salvatore Sanfilippo",
   "body" : "This make no sense to me. Redis would have worked fine."
  }
 ],
 "date" : ISODate("2013-03-11T01:54:53.692Z"),
 "permalink" : "martians_to_use_mongodb",
 "tags" : [
  "martians",
  "seti",
  "nosql",
  "worlddomination"
 ],
 "title" : "Martians to use MongoDB"
}

Note that you add comments in this blog from the blog post detail page, which appears at
http://localhost:8082/post/post_slug
where post_slug is the permalink. For the sake of eliminating doubt, the permalink for the example blog post above is http://localhost:8082/post/martians_to_use_mongodb 
As a reminder, to run your blog you type: 
mvn compile exec:java -Dexec.mainClass=course.BlogController


Answer of 3.1 and 3.2 is

You need to make changes to your BlogPostDAO.java file. Final version of file is given below


package course;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.bson.Document;
import org.bson.conversions.Bson;

import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import com.mongodb.client.model.UpdateOptions;

public class BlogPostDAO {
    MongoCollection<Document> postsCollection;

    public BlogPostDAO(final MongoDatabase blogDatabase) {
        postsCollection = blogDatabase.getCollection("posts");
    }

    // Return a single post corresponding to a permalink
    public Document findByPermalink(String permalink) {
        // XXX HW 3.2,  Work Here
        Document post = null;
        BasicDBObject dbObject  = new BasicDBObject("permalink", permalink);
  post = postsCollection.find(dbObject).first();
        return post;
    }

    // Return a list of posts in descending order. Limit determines
    // how many posts are returned.
    public List<Document> findByDateDescending(int limit) {

        // XXX HW 3.2,  Work Here
        // Return a list of DBObjects, each one a post from the posts collection
     List<Document> posts = postsCollection.find()//
                .sort(Sorts.descending("date"))//
                .limit(limit)//
                .into(new ArrayList<Document>());
        return posts;
    }


    public String addPost(String title, String body, List tags, String username) {

        System.out.println("inserting blog entry " + title + " " + body);

        String permalink = title.replaceAll("\\s", "_"); // whitespace becomes _
        permalink = permalink.replaceAll("\\W", ""); // get rid of non alphanumeric
        permalink = permalink.toLowerCase();

        // XXX HW 3.2, Work Here
        // Remember that a valid post has the following keys:
        // author, body, permalink, tags, comments, date
        //
        // A few hints:
        // - Don't forget to create an empty list of comments
        // - for the value of the date key, today's datetime is fine.
        // - tags are already in list form that implements suitable interface.
        // - we created the permalink for you above.

        // Build the post object and insert it
        
        Document post = new Document();
        post.append("title", title)//
                .append("author", username)//
                .append("body", body)//
                .append("permalink", permalink)//
                .append("tags", tags)//
                .append("comments", Collections.<Document> emptyList())//
                .append("date", new Date());

        postsCollection.insertOne(post);

        return permalink;
    }

    // White space to protect the innocent

    // Append a comment to a blog post
    public void addPostComment(final String name, final String email, final String body,
                               final String permalink) {

        // XXX HW 3.3, Work Here
        // Hints:
        // - email is optional and may come in NULL. Check for that.
        // - best solution uses an update command to the database and a suitable
        //   operator to append the comment on to any existing list of comments
  
     Document comment = new Document().append("author", name).append("body", body);
        if (email != null) {
            comment.append("email", email);
        }
        
        Bson update = new Document("$push", new Document("comments", comment));
        postsCollection.updateOne(Filters.eq("permalink", permalink), update, new UpdateOptions().upsert(true));
  
    }
}