How I Conquered a Functioning (Relational) Database with MongoDB and Mongoose.js

Daniel Schroeder
3 min readJan 26, 2018

When you learn about databases and web development in high school and college, you are taught SQL and php, how to set foreign keys to create a relational databases, and how to modularize your tables to keep data as seclusive as possible. Then, when you dive into M.E.A.N. stack web application and switch from a relational database to the newer OO non-relational databases (NoSQL) like MongoDB, your entire world flips upside down. Then, add on the plethora of MongoDB support libraries (like mongoose.js) and you are completely lost in an abyss of javascript promises, API documentation, and frustration. No need to fret! I have deciphered the documentation, implemented the functionality, and created a fully relational database to work with a M.E.A.N. stack web application utilizing mongoose.js and AngularJS (yes AngularJS as in your package.json dependencies reads “angular”: ^1.6.8").

Schemas

When utilizing mongoose.js, it is important to carefully plan out your model schemas and how you want to store data for web application based on how it is accessed and what objects belong to other objects. In my case I had a user object (model) that could create block objects which consisted of basic descriptor attributes and building objects. Immediately I had related objects that were referenced by other objects and… even worse… objects referenced by objects referenced by objects.

First, I needed a building schema, because I referenced buildings individually as well as in the User object. This was just a basic mongoose schema with a name, type, and address:

Next, I needed a way to “embed” this schema in an array within the User object in order to create blocks of buildings. As follows, the User schema:

This schema defines a User as having a String name and an array of block objects (defined by the “[ ]” in the attribute definition). One thing to notice is the mongoose syntax for the building attribute of the block array, which is an array of type mongoose.Schema.ObjectId with a reference to “Building.” This is the mongoose equivalent of setting a foreign key. When we push a document to the User.block array, the Building._id will be stored in this building array, effectively creating what mongoose likes to call a subdocument. More explicitly:

Subdocuments are documents embedded in other documents. In Mongoose, this means you can nest schemas in other schemas. -Mongoose.js Docs

Ok, we have the set up, now how to make use of it in our application?

Nested ng-repeat with AngularJS

There are two distinctions to make note of in this nested ng-repeat directive:

  1. The first definition “block in userBlocks” contains a scope variable that needs to be populated by the controller.
  2. The second definition “building in block.building” is referencing the block variables being iterated over in the first call.

This creates a similar structure to a double-for loop. With this implementation, we can efficiently look through the objects in the building array of the block array of the user object and successfully accomplish a relational style query for retrieving this data.

Populating $scope.userBlocks in the Controller

The final step to making this code work is to finally retrieve and populate the data from the database through a AngularJS service and API route handler. Step 1 is to create a route handler that pulls the user block array and uses the mongoose method “populate()” to populate the building subdocuments referenced in the block array. This is the most important step. As our schema is defined, the block array holds an array of building document references by ObjectId. Our route handler needs to tell mongoose to populate this array with the building documents that each ObjectId references.

The ‘/api/getUserBlocks’ route handler finds the desired document then executes a populate function to retrieve the subdocuments stored in the building array of User.block and sends the data back to the controller. This retrieves all the necessary data to populate our userBlocks $scope variable in the html view, and allows the nested ng-repeat directives to iterate through the User model arrays and correctly display all the necessary block and building information.

This method of data population and retrieval allows M.E.A.N. stack applications to execute SQL type queries to display and manipulate database documents. If you enjoyed this article, check out my tips for building a M.E.A.N. stack administrative dashboard with Node.js and Bootstrap 4.

--

--