Tag Archives: development

Mongoose – Models with Circular References

Today at the office I realized of an interesting use case on MongoDB and Mongoose: Many to Many relationship. I had no idea if Mongoose was going to fall in an infinite loop trying to populate each nested document but turns that it populates only the child documents (first level) 8)

On a standard SQL DataBase this kind of relationship is easy to accomplish by using a table to store the ids of the items you want to be related but if you are on a NoSQL DataBase and you want to achieve the same result… well, it is not exactly the same way, but it is not hard either. Let’s see an example.

Let’s say you have two collections: Posts and Attachments. Usually you may be able to Add many attachments to a post, or maybe you want to share a file to many posts, but since we want to do something more interesting we are going to support both: Attach multiple files to a post but also allow your attachments to be shared on multiple posts :D

A common way to handle documents related on a NoSQL DB is to create sub-documents  this allows you to do a one-to-many relation but since we want a many-to-many then what we will do is to use the nice “Populate” method available on Mongoose and save ID references as an array, either attachments IDs on a Post, post IDs on an Attachment or even both!

You can see the code below and an example output, or if you prefer you can also download or fork it on GitHub.

// Dependencies
var mongoose = require('mongoose'),
	async = require('async');

// Connect to Mongoose
mongoose.connect('mongodb://localhost:27017/circular');

var Schema = mongoose.Schema,
	ObjectId = Schema.Types.ObjectId;

// Schema definitions
var PostSchema = new Schema({
	title: {
		type: String,
		'default': ''
	},
	content: {
		type: String,
		'default': ''
	}
});
var AttachmentSchema = new Schema({
	fileName: {
		type: String,
		'default': ''
	}
});

// Circular reference definitions
PostSchema.add({
	attachments: [{
		type: ObjectId,
		ref: 'Attachment'
	}]
});
AttachmentSchema.add({
	posts: [{
		type: ObjectId,
		ref: 'Post'
	}]
});

// A couple of methods to save references inside the documents by using only
// the _id instead of creating a subdocument.
PostSchema.methods.attach = function(attachment, callback) {
	var post = this;
	this.attachments.push(attachment);
	this.save(function(err) {
		attachment.posts.push(post);
		attachment.save(callback);
	});
};
AttachmentSchema.methods.share = function(post, callback) {
	var attachment = this;
	this.posts.push(post);
	this.save(function(err) {
		post.attachments.push(attachment);
		post.save(callback);
	});
};

// Models definition
var Post = mongoose.model('Post', PostSchema, 'posts');
var Attachment = mongoose.model('Attachment', AttachmentSchema, 'attachments');

// Go parallel!
async.parallel({
	post: function(callback) {
		console.log('creating a post');
		var p = new Post({
			title: 'Test post (' + Date.now() + ')',
			content: 'Test post (' + Date.now() + ')'
		});
		p.save(callback);
	},
	attachment: function(callback) {
		console.log('creating an attachment');
		var a = new Attachment({
			fileName: 'test_' + Date.now() + '.txt'
		});
		a.save(callback);
	}
}, function(err, res) {
	// Once we have a new post and a new attachment, create a relation on each one,
	// this is only a demo of having the reference of each other but it is not required.
	res.post[0].attach(res.attachment[0], function(err, res) {
		// Dump the current data, just to see if it is working ;) 
		async.series([function(callback) {
			// Read all our posts and their attachments
			Post.find().populate('attachments').exec(function(err, posts) {
				if (err) {
					return callback(err);
				}

				posts.forEach(function(post) {
					console.log('Post ' + post.title + ' has ' + post.attachments.length + ' attachment(s):');
					post.attachments.forEach(function(attachment) {
						console.log('- Filename: ' + attachment.fileName);
					});
				});
				callback();
			});
		}, function(callback) {
			// Read all our attachments and their posts
			Attachment.find().populate('posts').exec(function(err, attachments) {
				if (err) {
					return callback(err);
				}
				attachments.forEach(function(attachment) {
					console.log('Attachment ' + attachment.fileName + ' is shared in ' + attachment.posts.length + ' post(s):');
					attachment.posts.forEach(function(post) {
						console.log('- Post: ' + post.title);
					});
				});
				callback();
			});
		}], function(err) {
			mongoose.disconnect();
		});
	});
});

After executing this file some times, I got an output like this one:

> node index.js
creating a post
creating an attachment
Post Test post (1354674892970) has 1 attachment(s):
- Filename: test_1354674892973.txt
Post Test post (1354674914843) has 1 attachment(s):
- Filename: test_1354674914845.txt
Attachment test_1354674892973.txt is shared in 1 post(s):
- Post: Test post (1354674892970)
Attachment test_1354674914845.txt is shared in 1 post(s):
- Post: Test post (1354674914843)

Grunt Task: Grunt String Replace

Many of the best projects I have seen and used started as a really simple idea whose objective was to solve one need. Some of the developers behind those decided to contribute their time and experience to the world by sharing their projects as Open Source and now, now it is my turn to contribute with a little one: grunt-string-replace.

I am in the process of improve some tools on my day to day moving away from Ant into Grunt and some of the tasks required to execute different string replacements across multiple files. I started by searching a grunt task but there was not a single one to accomplish what I needed at that time; I found some general replacement tools, or token replacements, but never the flexibility or the power to perform regular expression search and replaces. So, I decided it was my time to create a task to do so, and I did it.

 

grunt-string-replace 

Replaces strings on files by using string or regex patterns. Attempts to be a String.prototype.replace adapter task for your grunt project.

 

More details at the module page https://github.com/erickrdch/grunt-string-replace

#backboneIO conociendo Backbone.js

This time (and contrary to what I said some months ago) I’ll be sharing in Spanish some webcasts in which I’ll be collaborating regarding backbone.

En esta ocasión (y contrariamente a lo que dije hace algunos meses) les estaré compartiendo algunos webcasts en los que estoy participando sobre backbone.
 

Si encuentran útil esta presentación, les agradecería me regalaran unos minutos de su tiempo dando una calificación a la misma en SpeakerRate.

Debug a NodeJS App with Chrome Dev Tools

The more complex is your app, the more difficult is to find those weird issues that all developers (we) always face, and dumping logs to the console or to a logfile is by far the easiest way to debug (it is just as bad as echo-ing on PHP or alert-ing on plain old JavaScript). Thanks to the node community we have this really really, REALLY, nice tool called “node-inspector” that is basically a node app that runs on your server and connects to your application using node’s debug mode exposing the app code using our beloved Chrome Developer Tools. I know, it sounds too good to be real, but it IS real, and actually really easy to get up and running.

 

Continue reading