Node.js and MongoDB: Part 2 – Refactor Hell

How I usually feel about deprecation warnings. Source.

I mentioned briefly in my last post about how the book I have been using, Node.JS, MongoDB and Angular JS Web Development, has a lot of deprecated code in it once I hit chapter 13. I do not want to teach myself code that is going to end up being trash here as the technology world evolves so whenever I got the deprecation warning, I actually went ahead and refactored all the source code from the book into the latest version using the documentation from MongoDB’s API. This way, I actually am learning the latest and official way to do things, and creating great reference code for me in the future to use (and for anyone who actually reads these blog posts or browses my GitHub as well). Chapter 15 was definitely the worst of it. First of all, the book starts using a new collection called “word_stats” that has over 4000 words in it. However, the book neglects to tell you where to get the collection data from. It pissed me off originally, and I actually was about to just skip to chapter 16 until, lo and behold, I noticed a massive generate_data.js file in the chapter 16 source code folder. I opened it up in VS Code and discovered thats the file that generates the schema and all the data for the word_stats collection. I decided to go ahead and give it a run and now I could start messing around with the source code in chapter 15. I was happy I did not have to skip that chapter. But the good news only lasted a short while as I went to run the source code and was met with the infamous DeprecationWarning message yet again:

You can see all the examples use [collection].group which Mongo kindly informs us will be deprecated in newer versions.

Now for the past DeprecationWarning messages, the fix was usually simple. Something like change update to updateMany, so no sweat there. But in this particular message it tells me, “We recommend rewriting using the aggregation framework.” I was set to refactor the group method into the aggregation framework. Now since I have never messed with MongoDB before this book, it was kind of like trying to read a foreign language for the first time. Some wods you realized were close to English and could easily understand but the rest made no sense whatsoever. I have spent the past few days reading StackOverflow, the MongoDB docs and the MongoDB Node.js docs trying to understand what the hell is going on. Here’s a little sample to compare the refactoring for one of the examples.

doc_group.js [Book Source Code with Group]
----------------------------
collection.group(['first','last'], 
              {first:'o',last:{$in:['a','e','i','o','u']}},
              {"count":0}, 
              function (obj, prev) { prev.count++; }, true,
              function(err, results){
        console.log("\n'O' words grouped by first and last" +
                    " letter that end with a vowel: ");
        console.log(results);
  });

doc_group_refactored.js [My Refactored Code with Aggregation Framework]
---------------------------
collection.aggregate([
        {$match: {first: 'o', last:{$in:['a','e','i','o','u']}}}, 
        {$sortByCount: "$last"}    
]).toArray(function(err, docs){
        console.log("\n'O' words grouped by first and last" +
                        " letter that end with a vowel: ");
        console.log(docs);
});

So, to me, the syntax is way different. [collection].group() is has the following parameters: keys, query, initial, reduce, finalize, command, options, callback. The [collection].aggregate() function just takes an array of the steps in the pipeline as the only parameter and returns an array instead of an object so I have to use toArray to get the information I need. So we can break things down, but the first thing you do in aggregation is find a match. In our case, we need to match where the first letter is ‘o’ and the last letter is a vowel (in the array of vowels). Fortunately, the document has a field path for $first and $last letter of each word so luckily we can just copy that directly over from the group’s query parameter. It looks like they set a placeholder 0 for the count and then use the reduce parameter function to increment for each word it finds that matches the first set of items in the query parameter. Then after the count is done, it returns the whole object. I think I actually like the aggregation framework better now that I have practiced using it, plus I found the $sortByCount operator in the aggregation framework and once I understood how to use it, it automatically did the counting for me and was easy. There is no need for a reduce or finalize function, just one line of code and I can do the counting with the new aggregation framework. The output, however, is not as detailed but I think works just fine:

doc_group.js [Book Source Code with Group]
----------------------------
'O' words grouped by first and last letter that end with a vowel: 
[ { first: 'o', last: 'e', count: 21 },
  { first: 'o', last: 'o', count: 1 },
  { first: 'o', last: 'a', count: 1 } ]

doc_group_refactored.js [My Refactored Code with Aggregation Framework]
---------------------------
'O' words grouped by first and last letter that end with a vowel: 
[ { _id: 'e', count: 21 },
  { _id: 'a', count: 1 },
  { _id: 'o', count: 1 } ]

You can see above that the original code prints out the first and last letters to the output. But using $sortByCount, the item we are grouping by becomes _id so in our case that is the last letter. I would love to figure out how to add the first letter in there too (even though it really doesn’t matter) but every time I tried, I got null as a response so I will just leave it alone.In the book, they actually go over the aggregate method over the next few pages (after the group method), but the callback function is no longer a parameter like the book states so I still needed to refactor that code as well.

You can view the full source code for chapter 15 and all of my refactoring in my GitHub repo. Hopefully my refactoring can help someone else who needs to migrate from the group to the aggregation framework in MongoDB with Node.js. I think this is long enough of a post for a single chapter of a book, I will be learning Mongoose for Node.js next which is another plugin to connect to MongoDB. I probably will skip writing a post about that chapter unless something interesting happens, hopefully soon I will be getting into AngularJS!

Leave a Reply

Your email address will not be published. Required fields are marked *