Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Battle of the ORM
V
JavaScript ORM
• ORM: Object/relational impedance mismatch
• DAO: Centralized data access
• In JavaScript: A really easy w...
Comparison Points
• Getting setup
• Applying constraints
• White/blacklisting properties, data visibility
• Document assoc...
Mongoose Setup
var mongoose=require('mongoose'),

Foo=mongoose.model('Foo',{name:String});

function setupFoo(){

var foo=...
Waterline Setup
var Waterline=require('waterline'),orm=new Waterline(),

mongoAdapter=require('sails-mongo'),

config={

a...
Setup Results
/usr/local/bin/node mongoose.js
Got foo from database, {
"_id": "558646750d141363a10539ee",
"name": "Test Fo...
Round 1: Setup
Mongoose
• Fast to setup
• Uses callbacks natively
• Thin wrapper for mongodb
driver
• Provides __v propert...
Mongoose Constraints
Foo=mongoose.model('Foo',{name:{type:String,required:true}});

…
function testConstraints(){

var foo...
/usr/local/bin/node mongoose.js
{ [ValidationError: Foo validation failed]
message: 'Foo validation failed',
name: 'Valida...
Waterline Constraints
orm.loadCollection(Waterline.Collection.extend({

identity: 'foo',

connection: 'localhostMongo',

a...
/usr/local/bin/node waterline.js
Error (E_VALIDATION) :: 1 attribute is invalid
at WLValidationError.WLError (techdt.la-mv...
Round 2: Constraints
Mongoose
• Flexible constraints to handle
most situations
• Easy to setup
• Somewhat hard to read
val...
Mongoose Visibility [1]
Foo=mongoose.model('Foo',{

name:{type:String,required:true},

secret:{type:String,select:false}

...
/usr/local/bin/node mongoose.js
Saved foo to database, {
"__v": 0,
"name": "Test Foo",
"secret": "123456",
"_id": "55864fd...
Mongoose Visibility [2]
FooSchema=new mongoose.Schema({

name:{type:String,required:true},

secret:{type:String,select:fal...
Waterline Visibility
orm.loadCollection(Waterline.Collection.extend({

identity: 'foo',

connection: 'localhostMongo',

at...
/usr/local/bin/node waterline.js
Saved foo to database, {
"name": "Test Foo",
"createdAt": "2015-06-21T06:30:44.374Z",
"up...
Round 3: Visibility
Mongoose
• Two methods to hide
document properties
• One doesn’t work in all cases
• The other is a bi...
Mongoose Associations
BarSchema=new mongoose.Schema({

name:String,foo:{type:mongoose.Schema.Types.ObjectId,ref:'Foo'}

})...
/usr/local/bin/node mongoose.js
Got bar from database {
"_id": "55865d0e96b702dba1cecc85",
"name": "Test Bar",
"foo": "558...
Waterline Associations
orm.loadCollection(Waterline.Collection.extend({

identity: 'bar',

connection: 'localhostMongo',

...
/usr/local/bin/node waterline.js
Saved bar to database {
"name": "Test Bar",
"foo": "558660a765f025eba1fbb7af",
"createdAt...
Round 4: Associations
Mongoose
• Easy to setup document
associations
• Can call populate on find, or
on a document
• Associ...
Wrap Up
Mongoose
• Mongo ORM for JavaScript
• Has some rough edges
• Only option for highly
concurrent document updates
• ...
Upcoming SlideShare
Loading in …5
×

Big Data Day LA 2015 - Mongoose v/s Waterline: Battle of the ORM by Tim Fulmer of HopSkipDrive

2,595 views

Published on

Mongo is swiftly becoming the default, general purpose database du jour. In green field Node.JS systems, Mongo is almost the default persistence mechanism. At the same time, going completely schema-less raises it's own issues.

Enter the NoSQL ORM solution. By encoding schema information in easily changeable JavaScript definitions, Node.JS systems can get the benefits of both worlds. High velocity change throughput, with just enough guardrails to keep things on the tracks.

Mongoose has traditionally been the go-to ORM package, providing lightweight schema definitions on top of Mongo. Waterline has recently come out of the Sails.JS project with some interesting innovations. In this presentation we compare and contrast the two packages through a few typical ORM use cases.

Published in: Technology
  • Be the first to comment

Big Data Day LA 2015 - Mongoose v/s Waterline: Battle of the ORM by Tim Fulmer of HopSkipDrive

  1. 1. Battle of the ORM V
  2. 2. JavaScript ORM • ORM: Object/relational impedance mismatch • DAO: Centralized data access • In JavaScript: A really easy way to configure collections
  3. 3. Comparison Points • Getting setup • Applying constraints • White/blacklisting properties, data visibility • Document associations • Lifecycle hooks
  4. 4. Mongoose Setup var mongoose=require('mongoose'),
 Foo=mongoose.model('Foo',{name:String});
 function setupFoo(){
 var foo=new Foo({name:'Test Foo'});
 foo.save(function(err,foo){
 if(err) return console.log(err);
 Foo.findOne({_id:foo._id})
 .exec(function(err,foo){
 if(err) return console.log(err);
 if(!foo) console.log(
 'Mongoose returns null when findOne returns nothing.');
 console.log('Got foo from database, %s.',JSON.stringify(foo,null,2));
 process.exit(0);
 });
 });
 }
 mongoose.connect('mongodb://localhost/test');
 var db = mongoose.connection;
 db.on('error', console.error.bind(console, 'connection error:'));
 db.once('open', function () {
 setupFoo();
 });
  5. 5. Waterline Setup var Waterline=require('waterline'),orm=new Waterline(),
 mongoAdapter=require('sails-mongo'),
 config={
 adapters:{default:mongoAdapter,mongo:mongoAdapter},
 connections:{
 localhostMongo:{
 adapter: 'mongo',host: 'localhost',port: 27017,database: 'test'
 }}};
 orm.loadCollection(Waterline.Collection.extend({
 identity: 'foo',
 connection: 'localhostMongo',
 attributes: {name: 'string'}
 }));
 function setupFoo(Foo){
 Foo.create({name:'Test Foo'}).then(function(foo){
 Foo.findOne().where({id:foo._id}).then(function(foo){
 if(!foo) console.log(
 'Waterline returns undefined when findOne returns nothing.');
 console.log('Got foo from database, %s.',JSON.stringify(foo,null,2));
 process.exit(0);
 });
 });
 }
 orm.initialize(config,function(err,models){
 if(err) return console.log(err);
 setupFoo(models.collections.foo);
 });
  6. 6. Setup Results /usr/local/bin/node mongoose.js Got foo from database, { "_id": "558646750d141363a10539ee", "name": "Test Foo", "__v": 0 }. Process finished with exit code 0 /usr/local/bin/node waterline.js Got foo from database, { "name": "Test Foo", "createdAt": "2015-06-21T05:07:35.012Z", "updatedAt": "2015-06-21T05:07:35.012Z", "id": "5586469768455964a130adf4" }. Process finished with exit code 0
  7. 7. Round 1: Setup Mongoose • Fast to setup • Uses callbacks natively • Thin wrapper for mongodb driver • Provides __v property for document versioning Waterline • Slightly more configuration • Uses promises natively • Abstracts data store from document definition • Provides created & modified timestamps
  8. 8. Mongoose Constraints Foo=mongoose.model('Foo',{name:{type:String,required:true}});
 … function testConstraints(){
 var foo=new Foo({});
 foo.save(function(err){
 console.log(err);
 process.exit(1);
 });
 }
 … db.once('open', function () {
 testConstraints();
 });
  9. 9. /usr/local/bin/node mongoose.js { [ValidationError: Foo validation failed] message: 'Foo validation failed', name: 'ValidationError', errors: { name: { [ValidatorError: Path `name` is required.] properties: [Object], message: 'Path `name` is required.', name: 'ValidatorError', kind: 'required', path: 'name', value: undefined } } } Process finished with exit code 1
  10. 10. Waterline Constraints orm.loadCollection(Waterline.Collection.extend({
 identity: 'foo',
 connection: 'localhostMongo',
 attributes: {name:{type:'string',required:true}}
 }));
 … function testConstraints(Foo){
 Foo.create({}).catch(function(err){
 console.log(err);
 process.exit(1);
 });
 }
 orm.initialize(config,function(err,models){
 if(err) return console.log(err);
 testConstraints(models.collections.foo);
 });
  11. 11. /usr/local/bin/node waterline.js Error (E_VALIDATION) :: 1 attribute is invalid at WLValidationError.WLError (techdt.la-mvw/node_modules/waterline/ lib/waterline/error/WLError.js:26:15) … Invalid attributes sent to foo: • name • `undefined` should be a string (instead of "null", which is a object) • "required" validation rule failed for input: null Process finished with exit code 1
  12. 12. Round 2: Constraints Mongoose • Flexible constraints to handle most situations • Easy to setup • Somewhat hard to read validation error messages • Does not provide stack information Waterline • Flexible constraints to handle most situations • Easy to setup • Easy to read validation error messages • Provides stack information to help debugging
  13. 13. Mongoose Visibility [1] Foo=mongoose.model('Foo',{
 name:{type:String,required:true},
 secret:{type:String,select:false}
 });
 … function testSecret(){
 var foo=new Foo({name:'Test Foo',secret:'123456'});
 foo.save(function(err,foo){
 if(err) return console.log(err);
 console.log( 'Saved foo to database, %s.',JSON.stringify(foo,null,2));
 Foo.findOne({_id:foo._id})
 .exec(function(err,foo){
 if(err) return console.log(err);
 if(!foo) console.log(
 'Mongoose returns undefined when findOne returns nothing.');
 console.log( 'Got foo from database, %s.',JSON.stringify(foo,null,2));
 process.exit(0);
 });
 })
 }
  14. 14. /usr/local/bin/node mongoose.js Saved foo to database, { "__v": 0, "name": "Test Foo", "secret": "123456", "_id": "55864fdf4e5d5e94a1af5a14" }. Got foo from database, { "_id": "55864fdf4e5d5e94a1af5a14", "name": "Test Foo", "__v": 0 }. Process finished with exit code 0
  15. 15. Mongoose Visibility [2] FooSchema=new mongoose.Schema({
 name:{type:String,required:true},
 secret:{type:String,select:false}
 },{toJSON:{transform: function (doc,ret) {delete ret.secret;}}}
 ),
 Foo=mongoose.model('Foo',FooSchema); /usr/local/bin/node mongoose.js Saved foo to database, { "__v": 0, "name": "Test Foo", "_id": "558654d2945f15a1a1a2d840" }. Got foo from database, { "_id": "558654d2945f15a1a1a2d840", "name": "Test Foo", "__v": 0 }. Process finished with exit code 0
  16. 16. Waterline Visibility orm.loadCollection(Waterline.Collection.extend({
 identity: 'foo',
 connection: 'localhostMongo',
 attributes: {
 name:{type:'string',required:true},
 secret:{type:'string'},
 toJSON: function() {
 var foo= this.toObject();
 delete foo.secret;
 return foo;
 }
 }
 })); … function testSecret(Foo){
 Foo.create({name:'Test Foo',secret:123456}).then(function(foo){
 console.log( 'Saved foo to database, %s.',JSON.stringify(foo,null,2));
 })
 }
  17. 17. /usr/local/bin/node waterline.js Saved foo to database, { "name": "Test Foo", "createdAt": "2015-06-21T06:30:44.374Z", "updatedAt": "2015-06-21T06:30:44.374Z", "id": "55865a146954cbb4a16fe33e" }.
  18. 18. Round 3: Visibility Mongoose • Two methods to hide document properties • One doesn’t work in all cases • The other is a bit tough to configure Waterline • One method to hide document properties • Works in all cases • Simple to configure
  19. 19. Mongoose Associations BarSchema=new mongoose.Schema({
 name:String,foo:{type:mongoose.Schema.Types.ObjectId,ref:'Foo'}
 }),
 Bar=mongoose.model('Bar',BarSchema);
 … function testAssociations(){
 var foo=new Foo({name:'Test Foo'});
 foo.save(function(err,foo){
 if(err) return console.log(err);
 var bar=new Bar({name:'Test Bar',foo:foo});
 bar.save(function(err,bar){
 if(err) return console.log(err);
 Bar.findOne({_id:bar._id},function(err,bar){
 console.log(
 'Got bar from database %s.',JSON.stringify(bar,null,2));
 bar.populate('foo',function(err,bar){
 if(err) return console.log(err);
 console.log(
 'Populated foo on bar %s.',JSON.stringify(bar,null,2));
 process.exit(0);
 });
 });
 });
 });
 }
  20. 20. /usr/local/bin/node mongoose.js Got bar from database { "_id": "55865d0e96b702dba1cecc85", "name": "Test Bar", "foo": "55865d0d96b702dba1cecc84", "__v": 0 }. Populated foo from database { "_id": "55865d0e96b702dba1cecc85", "name": "Test Bar", "foo": { "_id": "55865d0d96b702dba1cecc84", "name": "Test Foo", "__v": 0 }, "__v": 0 }. Process finished with exit code 0
  21. 21. Waterline Associations orm.loadCollection(Waterline.Collection.extend({
 identity: 'bar',
 connection: 'localhostMongo',
 attributes: {
 name:{type:'string',required:true},
 foo:{model:'foo'}
 }
 })); … function testAssociations(Foo,Bar){
 Foo.create({name:'Test Foo'}).then(function(foo){
 Bar.create({name:'Test Bar',foo:foo}).then(function(bar){
 console.log('Saved bar to database %s.',JSON.stringify(bar,0,2));
 Bar.findOne({id:bar.id}).populate('foo').then(function(bar){
 console.log('Populated foo on bar %s.',JSON.stringify(bar,0,2));
 process.exit(0);
 });
 });
 });
 }
  22. 22. /usr/local/bin/node waterline.js Saved bar to database { "name": "Test Bar", "foo": "558660a765f025eba1fbb7af", "createdAt": "2015-06-21T06:58:47.079Z", "updatedAt": "2015-06-21T06:58:47.079Z", "id": "558660a765f025eba1fbb7b0" }. Populated foo on bar { "foo": { "name": "Test Foo", "createdAt": "2015-06-21T06:58:47.071Z", "updatedAt": "2015-06-21T06:58:47.071Z", "id": "558660a765f025eba1fbb7af" }, "name": "Test Bar", "createdAt": "2015-06-21T06:58:47.079Z", "updatedAt": "2015-06-21T06:58:47.079Z", "id": "558660a765f025eba1fbb7b0" }. Process finished with exit code 0
  23. 23. Round 4: Associations Mongoose • Easy to setup document associations • Can call populate on find, or on a document • Associated documents may or may not be populated Waterline • Easy to setup document associations • Associated documents may live in other databases • Can only call populate on find • Associated documents only populated when requested
  24. 24. Wrap Up Mongoose • Mongo ORM for JavaScript • Has some rough edges • Only option for highly concurrent document updates • Consistently more code to do the same things Waterline • JavaScript ORM for many popular databases • More modern, today JavaScript framework • Less rough edges • Does not support document isolation

×