2.10.2012

Nomination part 4 - models


This post is part of the series about creating an app with node.js, express for Facebook


For this part we will use the mongoose module that we already have installed, so no updates to the package.json.

Our model looks more or less to something like this:


Nomination is our main doc that have all the attributes to create a nomination, this one have a list of users, users its a doc that is embedded in nomination, i put it like this so you can see the separation but its not another doc in mongodb but we need to create the schema definition anyway in mongoose.

To add our modules lets create a folder called "models" or whatever in the root path of our app, inside that lets create a file called "nomination.js".

Lets see our code in nomination.js:

function defineModels(mongoose, fn) {
 var Schema = mongoose.Schema,
 Nomination, User;

/**
 *
 * Model: User
 *
 */
 User = new Schema({
 '_id' : String, //fb id
 'name' : String, //user name
 'votes' : Number //number of votes for this user
 });

 /**
 *
 * Model: Nomination
 *
 */
 Nomination = new Schema({
 'name' : String,
 'owner' : String, //who is the owner of the nomination
 'endDate' : Date, //when this nomination is going to end
 'users' : [User], //users added to this nomination
 'voters' : [String], //all the dudes that vote this nomination
 'category' : String,
 'sub_cat' : String,
 'active' : Boolean, //nomination finished
 'erased' : [String] //people erased by facebook id, we wont be able to re-add them
 });

 Nomination.virtual('id')
 .get(function() {
 return this._id.toHexString();
 });

 Nomination.pre('save', function(next) {
 //we could use this later
 next();
 });

 mongoose.model('Nomination', Nomination);

fn();
}

exports.defineModels = defineModels;

Line 1: Creating our main function to export later

2-3: our internal variables and a reference to schema

5-14: user model by "new Schema", with id gathered from fb, name and number of votes this user will have, all of type of String excepto votes

16-31: Our nomination model, this will have name, owner, date to deactivate it, list of nominated users, people that already voted here, category and a subcategory, activation flag and the list of deleted users, in this case we will let mongo to create an id, even if we dont put it, doesnt mean it isn't present in the document, we are just letting mongoose do the work

33-36: A virtual function to get the id, just in case

38-41: a small save middleware, before saving a doc of this type, mongoose will first call this function, for this example im not putting nothing yet, "next()" its to call the next function that in this case will be the actual save.

43: declaring the model

45: we call the callback passed, usually its to make an instance of the model to start using it

48: Exporting the function

Nice, we have our models defined, lets create something to use it, i called it "controller", from there we will handle the mongoose functionality so we are going to expose only whats is necesary.

We need to create a "controllers" folder or whatever name and a file called "nominator.js" that is going to be our controller, this file will look like this:

var mongoose = require('mongoose'),
    models = require('../models/nomination.js'),
    Nomination, db;

models.defineModels(mongoose, function() {
  Nomination = mongoose.model('Nomination');
  db = mongoose.connect('mongodb://nominator:nominat0r@ds029257.mongolab.com:29257/nomi-nation');
});

var NOMINATOR = {};

function findIndexByKeyValue(obj, key, value)
{
    var l = obj.length;
    for (var i = 0; i < l; i++) {
  if (obj[i][key] == value) {
   return i;
  }
 }
 return null;
}

/**
 * Add vote
 * @nomination object
 * @voterId string
 * @userId string
 * @callback function
*/
NOMINATOR.vote = function(nomination, voterId, userId, callback) {
    //TODO: check we dont want more than 10 users per nomination
    var isPresent = -1;
    try {
        isPresent = nomination.voters.indexOf(voterId);
    }
    catch (e){
        isPresent = -1;
    }
    if (isPresent < 0){
        nomination.voters.push(voterId);
    }
    //TODO: change to an actual mongo function
    var index = findIndexByKeyValue(nomination.users, "_id", userId);
    nomination.users[index].votes += 1;
    nomination.save(callback);
    //Nomination.update({_id :nomination.id}, {users: nomination.users}, callback);
};

/**
 * Add user
 * @nomination object
 * @user object
 * @callback function
*/
NOMINATOR.addUser = function(nomination, user, callback) {
    //TODO: check we dont want more than 10 users per nomination
    var isPresent = nomination.erased.indexOf(user._id);
    if (isPresent >= 0){ callback(new Error('User can\'t be added'), null); return; }
    if (user instanceof Array){
        nomination.users = user;
    }else{
        nomination.users.push(user);
    }
    nomination.save(callback);
    //Nomination.update({_id :nomination.id}, {users: nomination.users}, callback);
};

/**
 * Erase user
 * @nomination object
 * @user object
 * @callback function
*/
NOMINATOR.eraseUser = function(nomination, user, callback) {
    nomination.users.remove(user); //erased from the users list
    nomination.erased.push(user._id); //add to the erased so we dont add them again
    nomination.save(callback);
    //Nomination.update({_id :nomination.id}, {users: nomination.users, erased: nomination.erased}, callback);
};

/**
 * Create nomination
 * @nomination object
 * @callback function
*/
NOMINATOR.createNomination = function(nomination, callback) {
    var myNomination = new Nomination(nomination);
    myNomination.save(callback);
};

/**
 * Erase nomination
 * @nominationid string
 * @callback function
*/
NOMINATOR.eraseNomination = function(nominationid, callback) {
    Nomination.erase({'_id' : nominationid }, callback);
};

/**
 * find nominations the user own
 * @userid fb userid
 * @callback function
 *
*/
NOMINATOR.findMyNominations = function(userId, callback) {
    Nomination.where('owner', userId)
        .where('active', true)
        .run(callback);
};

/**
 * find nominations the user voted
 * @userId fb userid
 * @callback function
 *
*/
NOMINATOR.findVoted = function(userId, callback) {
    Nomination.where('voters').in([userId])
        .where('active', true)
        .run(callback);
};

/**
 * find nominates where the user was nominated
 * @userId fb userid
 * @callback function
 *
*/
NOMINATOR.findNominated = function(userId, callback) {
    Nomination.where('users._id').in([userId])
        .where('active', true)
        .run(callback);
};

// Exporting functions
module.exports = NOMINATOR;

Line 1: loading the mongoose modules

2: loading the models, we only export one function

3: our local variables

5-8: we start using the function we exported in "models.js" and we make an instance of the model "nomination" and we connect to the db

10: Lets create our object that will hold all the functionality to export it at the end

11: A small helper function to look for indexes inside the arrays

23-47: Adding votes, lets first check if the user voted its already in the list, if not to added if yes, to add one vote to this victim nominated, we save the model that function will make this change to go directly to the mongodb, lets call the callback to show the results for whom call us, this callback will have as parameters "error" and "doc"

49-66: Adding users, first lets check the user its not in our list of people deleted so we don't put it again, if so lets throw an error, then lets check if we are getting an array or a single user and add them to the list of users and then we save and call the callback


68-79: Erasing users, first lets erase him for the list of users and then add them to the list of erased, save and call the callback


81-89: Creating nominations, we get as a parameter an object with the attributes needed to create a nomination, we make an instance of it and save it, calling the callback


91-125: Searching, search is easy using query, we use the base model and we keep adding whatever we are looking for, e.g,:  "where('owner', userId)", here we are looking where the nomination owner is the userId that we get as a parameter, then we check that this nomination is active and at the end we run the query with "run(callback)" and send the results to the callback, this will be the same for all the searching just with different parameters


128: Lets export our object with all the functionality 


We are done!!!, we have our models and our controller to handle them in our app, next time we will see how make some testing for our models and routes


The model picture was created using cacoo :) online


Fork the code in github:

Greetings 

2 comments:

  1. Mongoose creates virtual id getters for you by default so no need to define them yourself :)

    ReplyDelete