diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js
index ea9c9d3a..29f25166 100644
--- a/lib/connectors/memory.js
+++ b/lib/connectors/memory.js
@@ -70,6 +70,31 @@ function deserialize(dbObj) {
   }
 }
 
+Memory.prototype.getCollection = function(model) {
+  var modelClass = this._models[model];
+  if (modelClass.settings.memory) {
+    model = modelClass.settings.memory.collection || model;
+  }
+  return model;
+}
+
+Memory.prototype.initCollection = function(model) {
+  this.collection(model, {});
+  this.collectionSeq(model, 1);
+}
+
+Memory.prototype.collection = function(model, val) {
+  model = this.getCollection(model);
+  if (arguments.length > 1) this.cache[model] = val;
+  return this.cache[model];
+};
+
+Memory.prototype.collectionSeq = function(model, val) {
+  model = this.getCollection(model);
+  if (arguments.length > 1) this.ids[model] = val;
+  return this.ids[model];
+};
+
 Memory.prototype.loadFromFile = function(callback) {
   var self = this;
   var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
@@ -161,36 +186,31 @@ Memory.prototype.saveToFile = function (result, callback) {
 Memory.prototype.define = function defineModel(definition) {
   this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
   var m = definition.model.modelName;
-  if(!this.cache[m]) {
-    this.cache[m] = {};
-    this.ids[m] = 1;
-  }
+  if(!this.collection(m)) this.initCollection(m);
 };
 
 Memory.prototype.create = function create(model, data, callback) {
   // FIXME: [rfeng] We need to generate unique ids based on the id type
   // FIXME: [rfeng] We don't support composite ids yet
-  var currentId = this.ids[model];
-  if (currentId === undefined) {
-    // First time
-    this.ids[model] = 1;
-    currentId = 1;
+  var currentId = this.collectionSeq(model);
+  if (currentId === undefined) { // First time
+    currentId = this.collectionSeq(model, 1);
   }
   var id = this.getIdValue(model, data) || currentId;
   if (id > currentId) {
     // If the id is passed in and the value is greater than the current id
     currentId = id;
   }
-  this.ids[model] = Number(currentId) + 1;
+  this.collectionSeq(model, Number(currentId) + 1);
 
   var props = this._models[model].properties;
   var idName = this.idName(model);
   id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
   this.setIdValue(model, data, id);
-  if(!this.cache[model]) {
-    this.cache[model] = {};
+  if(!this.collection(model)) {
+    this.collection(model, {});
   }
-  this.cache[model][id] = serialize(data);
+  this.collection(model)[id] = serialize(data);
   this.saveToFile(id, callback);
 };
 
@@ -210,30 +230,30 @@ Memory.prototype.updateOrCreate = function (model, data, callback) {
 
 Memory.prototype.save = function save(model, data, callback) {
   var id = this.getIdValue(model, data);
-  var cachedModels = this.cache[model];
-  var modelData = cachedModels && this.cache[model][id];
+  var cachedModels = this.collection(model);
+  var modelData = cachedModels && this.collection(model)[id];
   modelData = modelData && deserialize(modelData);
   if (modelData) {
     data = merge(modelData, data);
   }
-  this.cache[model][id] = serialize(data);
+  this.collection(model)[id] = serialize(data);
   this.saveToFile(data, callback);
 };
 
 Memory.prototype.exists = function exists(model, id, callback) {
   process.nextTick(function () {
-    callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
+    callback(null, this.collection(model) && this.collection(model).hasOwnProperty(id));
   }.bind(this));
 };
 
 Memory.prototype.find = function find(model, id, callback) {
   process.nextTick(function () {
-    callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
+    callback(null, id in this.collection(model) && this.fromDb(model, this.collection(model)[id]));
   }.bind(this));
 };
 
 Memory.prototype.destroy = function destroy(model, id, callback) {
-  delete this.cache[model][id];
+  delete this.collection(model)[id];
   this.saveToFile(null, callback);
 };
 
@@ -266,8 +286,8 @@ Memory.prototype.fromDb = function (model, data) {
 
 Memory.prototype.all = function all(model, filter, callback) {
   var self = this;
-  var nodes = Object.keys(this.cache[model]).map(function (key) {
-    return this.fromDb(model, this.cache[model][key]);
+  var nodes = Object.keys(this.collection(model)).map(function (key) {
+    return this.fromDb(model, this.collection(model)[key]);
   }.bind(this));
 
   if (filter) {
@@ -505,24 +525,23 @@ Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
     callback = where;
     where = undefined;
   }
-  var cache = this.cache[model];
+  var cache = this.collection(model);
   var filter = null;
   if (where) {
     filter = applyFilter({where: where});
-  }
-  Object.keys(cache).forEach(function (id) {
-    if (!filter || filter(this.fromDb(model, cache[id]))) {
-      delete cache[id];
-    }
-  }.bind(this));
-  if (!where) {
-    this.cache[model] = {};
+    Object.keys(cache).forEach(function (id) {
+      if (!filter || filter(this.fromDb(model, cache[id]))) {
+        delete cache[id];
+      }
+    }.bind(this));
+  } else {
+    this.collection(model, {});
   }
   this.saveToFile(null, callback);
 };
 
 Memory.prototype.count = function count(model, callback, where) {
-  var cache = this.cache[model];
+  var cache = this.collection(model);
   var data = Object.keys(cache);
   if (where) {
     var filter = {where: where};
@@ -539,7 +558,7 @@ Memory.prototype.count = function count(model, callback, where) {
 Memory.prototype.update =
   Memory.prototype.updateAll = function updateAll(model, where, data, cb) {
     var self = this;
-    var cache = this.cache[model];
+    var cache = this.collection(model);
     var filter = null;
     where = where || {};
     filter = applyFilter({where: where});
@@ -571,8 +590,8 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, c
 
   this.setIdValue(model, data, id);
 
-  var cachedModels = this.cache[model];
-  var modelData = cachedModels && this.cache[model][id];
+  var cachedModels = this.collection(model);
+  var modelData = cachedModels && this.collection(model)[id];
 
   if (modelData) {
     this.save(model, data, cb);
@@ -594,6 +613,16 @@ Memory.prototype.buildNearFilter = function (filter) {
   // noop
 }
 
+Memory.prototype.automigrate = function (models, cb) {
+  if (typeof models === 'function') cb = models, models = [];
+  if (models.length === 0) models = Object.keys(this._models);
+  var self = this;
+  models.forEach(function(m) {
+    self.initCollection(m);
+  });
+  if (cb) cb();
+}
+
 function merge(base, update) {
   if (!base) {
     return update;
diff --git a/lib/dao.js b/lib/dao.js
index c4236a91..cff2e52c 100644
--- a/lib/dao.js
+++ b/lib/dao.js
@@ -13,11 +13,12 @@ var Relation = require('./relations.js');
 var Inclusion = require('./include.js');
 var List = require('./list.js');
 var geo = require('./geo');
-var mergeQuery = require('./scope.js').mergeQuery;
 var Memory = require('./connectors/memory').Memory;
 var utils = require('./utils');
 var fieldsToArray = utils.fieldsToArray;
 var removeUndefined = utils.removeUndefined;
+var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
+var mergeQuery = utils.mergeQuery;
 var util = require('util');
 var assert = require('assert');
 
@@ -53,6 +54,14 @@ function setIdValue(m, data, value) {
   }
 }
 
+function byIdQuery(m, id) {
+  var pk = idName(m);
+  var query = { where: {} };
+  query.where[pk] = id;
+  m.applyScope(query);
+  return query;
+}
+
 DataAccessObject._forDB = function (data) {
   if (!(this.getDataSource().isRelational && this.getDataSource().isRelational())) {
     return data;
@@ -69,6 +78,40 @@ DataAccessObject._forDB = function (data) {
   return res;
 };
 
+DataAccessObject.defaultScope = function(target, inst) {
+  var scope = this.definition.settings.scope;
+  if (typeof scope === 'function') {
+    scope = this.definition.settings.scope.call(this, target, inst);
+  }
+  return scope;
+};
+
+DataAccessObject.applyScope = function(query, inst) {
+  var scope = this.defaultScope(query, inst) || {};
+  if (typeof scope === 'object') {
+    mergeQuery(query, scope || {}, this.definition.settings.scoping);
+  }
+};
+
+DataAccessObject.applyProperties = function(data, inst) {
+  var properties = this.definition.settings.properties;
+  properties = properties || this.definition.settings.attributes;
+  if (typeof properties === 'object') {
+    util._extend(data, properties);
+  } else if (typeof properties === 'function') {
+    util._extend(data, properties.call(this, data, inst) || {});
+  } else if (properties !== false) {
+    var scope = this.defaultScope(data, inst) || {};
+    if (typeof scope.where === 'object') {
+      setScopeValuesFromWhere(data, scope.where, this);
+    }
+  }
+};
+
+DataAccessObject.lookupModel = function(data) {
+  return this;
+};
+
 /**
  * Create an instance of Model with given data and save to the attached data source. Callback is optional.
  * Example:
@@ -89,8 +132,8 @@ DataAccessObject.create = function (data, callback) {
   if (stillConnecting(this.getDataSource(), this, arguments)) return;
 
   var Model = this;
-  var modelName = Model.modelName;
-
+  var self = this;
+  
   if (typeof data === 'function') {
     callback = data;
     data = {};
@@ -116,6 +159,7 @@ DataAccessObject.create = function (data, callback) {
 
     for (var i = 0; i < data.length; i += 1) {
       (function (d, i) {
+        Model = self.lookupModel(d); // data-specific
         instances.push(Model.create(d, function (err, inst) {
           if (err) {
             errors[i] = err;
@@ -131,11 +175,16 @@ DataAccessObject.create = function (data, callback) {
     function modelCreated() {
       if (--wait === 0) {
         callback(gotError ? errors : null, instances);
-        if(!gotError) instances.forEach(Model.emit.bind('changed'));
+        if(!gotError) {
+          instances.forEach(function(inst) {
+            inst.constructor.emit('changed');
+          });
+        }
       }
     }
   }
-
+  
+  var enforced = {};
   var obj;
   var idValue = getIdValue(this, data);
   
@@ -145,6 +194,13 @@ DataAccessObject.create = function (data, callback) {
   } else {
     obj = new Model(data);
   }
+  
+  this.applyProperties(enforced, obj);
+  obj.setAttributes(enforced);
+  
+  Model = this.lookupModel(data); // data-specific
+  if (Model !== obj.constructor) obj = new Model(data);
+  
   data = obj.toObject(true);
   
   // validation required
@@ -155,12 +211,13 @@ DataAccessObject.create = function (data, callback) {
       callback(new ValidationError(obj), obj);
     }
   }, data);
-
+  
   function create() {
     obj.trigger('create', function (createDone) {
       obj.trigger('save', function (saveDone) {
 
         var _idName = idName(Model);
+        var modelName = Model.modelName;
         this._adapter().create(modelName, this.constructor._forDB(obj.toObject(true)), function (err, id, rev) {
           if (id) {
             obj.__data[_idName] = id;
@@ -208,7 +265,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
   if (stillConnecting(this.getDataSource(), this, arguments)) {
     return;
   }
-
+  var self = this;
   var Model = this;
   if (!getIdValue(this, data)) {
     return this.create(data, callback);
@@ -220,7 +277,9 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
       inst = new Model(data);
     }
     update = inst.toObject(false);
+    this.applyProperties(update, inst);
     update = removeUndefined(update);
+    Model = this.lookupModel(update);
     this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) {
       var obj;
       if (data && !(data instanceof Model)) {
@@ -242,6 +301,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
       if (inst) {
         inst.updateAttributes(data, callback);
       } else {
+        Model = self.lookupModel(data);
         var obj = new Model(data);
         obj.save(data, callback);
       }
@@ -290,7 +350,9 @@ DataAccessObject.exists = function exists(id, cb) {
   if (stillConnecting(this.getDataSource(), this, arguments)) return;
 
   if (id !== undefined && id !== null && id !== '') {
-    this.dataSource.connector.exists(this.modelName, id, cb);
+    this.count(byIdQuery(this, id).where, function(err, count) {
+      cb(err, err ? false : count === 1);
+    });
   } else {
     cb(new Error('Model::exists requires the id argument'));
   }
@@ -311,17 +373,7 @@ DataAccessObject.exists = function exists(id, cb) {
  */
 DataAccessObject.findById = function find(id, cb) {
   if (stillConnecting(this.getDataSource(), this, arguments)) return;
-
-  this.getDataSource().connector.find(this.modelName, id, function (err, data) {
-    var obj = null;
-    if (data) {
-      if (!getIdValue(this, data)) {
-        setIdValue(this, data, id);
-      }
-      obj = new this(data, {applySetters: false, persisted: true});
-    }
-    cb(err, obj);
-  }.bind(this));
+  this.findOne(byIdQuery(this, id), cb);
 };
 
 DataAccessObject.findByIds = function(ids, cond, cb) {
@@ -683,7 +735,7 @@ DataAccessObject.find = function find(query, cb) {
   var self = this;
 
   query = query || {};
-
+  
   try {
     this._normalize(query);
   } catch (err) {
@@ -692,6 +744,8 @@ DataAccessObject.find = function find(query, cb) {
     });
   }
 
+  this.applyScope(query);
+  
   var near = query && geo.nearFilter(query.where);
   var supportsGeo = !!this.getDataSource().connector.buildNearFilter;
 
@@ -702,6 +756,7 @@ DataAccessObject.find = function find(query, cb) {
     } else if (query.where) {
       // do in memory query
       // using all documents
+      // TODO [fabien] use default scope here?
       this.getDataSource().connector.all(this.modelName, {}, function (err, data) {
         var memory = new Memory();
         var modelName = self.modelName;
@@ -735,8 +790,9 @@ DataAccessObject.find = function find(query, cb) {
   this.getDataSource().connector.all(this.modelName, query, function (err, data) {
     if (data && data.forEach) {
       data.forEach(function (d, i) {
-        var obj = new self(d, {fields: query.fields, applySetters: false, persisted: true});
-
+        var Model = self.lookupModel(d);
+        var obj = new Model(d, {fields: query.fields, applySetters: false, persisted: true});
+        
         if (query && query.include) {
           if (query.collect) {
             // The collect property indicates that the query is to return the
@@ -817,34 +873,39 @@ DataAccessObject.findOne = function findOne(query, cb) {
  * @param {Function} [cb] Callback called with (err)
  */
 DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyAll = function destroyAll(where, cb) {
-      if (stillConnecting(this.getDataSource(), this, arguments)) return;
-      var Model = this;
+  if (stillConnecting(this.getDataSource(), this, arguments)) return;
+  var Model = this;
 
-      if (!cb && 'function' === typeof where) {
-        cb = where;
-        where = undefined;
-      }
-      if (!where) {
-        this.getDataSource().connector.destroyAll(this.modelName, function (err, data) {
-          cb && cb(err, data);
-          if(!err) Model.emit('deletedAll');
-        }.bind(this));
-      } else {
-        try {
-          // Support an optional where object
-          where = removeUndefined(where);
-          where = this._coerce(where);
-        } catch (err) {
-          return process.nextTick(function() {
-            cb && cb(err);
-          });
-        }
-        this.getDataSource().connector.destroyAll(this.modelName, where, function (err, data) {
-          cb && cb(err, data);
-          if(!err) Model.emit('deletedAll', where);
-        }.bind(this));
-      }
-    };
+  if (!cb && 'function' === typeof where) {
+    cb = where;
+    where = undefined;
+  }
+  
+  var query = { where: where };
+  this.applyScope(query);
+  where = query.where;
+  
+  if (!where || (typeof where === 'object' && Object.keys(where).length === 0)) {
+    this.getDataSource().connector.destroyAll(this.modelName, function (err, data) {
+      cb && cb(err, data);
+      if(!err) Model.emit('deletedAll');
+    }.bind(this));
+  } else {
+    try {
+      // Support an optional where object
+      where = removeUndefined(where);
+      where = this._coerce(where);
+    } catch (err) {
+      return process.nextTick(function() {
+        cb && cb(err);
+      });
+    }
+    this.getDataSource().connector.destroyAll(this.modelName, where, function (err, data) {
+      cb && cb(err, data);
+      if(!err) Model.emit('deletedAll', where);
+    }.bind(this));
+  }
+};
 
 /**
  * Delete the record with the specified ID.
@@ -857,16 +918,16 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
 // 'deleteById' will be used as the name for strong-remoting to keep it backward
 // compatible for angular SDK
 DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.deleteById = function deleteById(id, cb) {
-      if (stillConnecting(this.getDataSource(), this, arguments)) return;
-      var Model = this;
-
-      this.getDataSource().connector.destroy(this.modelName, id, function (err) {
-        if ('function' === typeof cb) {
-          cb(err);
-        }
-        if(!err) Model.emit('deleted', id);
-      }.bind(this));
-    };
+  if (stillConnecting(this.getDataSource(), this, arguments)) return;
+  var Model = this;
+  
+  this.remove(byIdQuery(this, id).where, function(err) {
+    if ('function' === typeof cb) {
+      cb(err);
+    }
+    if(!err) Model.emit('deleted', id);
+  });
+};
 
 /**
  * Return count of matched records. Optional query parameter allows you to count filtered set of model instances.
@@ -888,6 +949,11 @@ DataAccessObject.count = function (where, cb) {
     cb = where;
     where = null;
   }
+  
+  var query = { where: where };
+  this.applyScope(query);
+  where = query.where;
+  
   try {
     where = removeUndefined(where);
     where = this._coerce(where);
@@ -896,6 +962,7 @@ DataAccessObject.count = function (where, cb) {
       cb && cb(err);
     });
   }
+  
   this.getDataSource().connector.count(this.modelName, cb, where);
 };
 
@@ -926,13 +993,17 @@ DataAccessObject.prototype.save = function (options, callback) {
   if (!('throws' in options)) {
     options.throws = false;
   }
-
+  
   var inst = this;
   var data = inst.toObject(true);
   var modelName = Model.modelName;
-
+  
+  Model.applyProperties(data, this);
+  
   if (this.isNewRecord()) {
     return Model.create(this, callback);
+  } else {
+    inst.setAttributes(data);
   }
 
   // validate first
@@ -1016,7 +1087,13 @@ DataAccessObject.updateAll = function (where, data, cb) {
   assert(typeof where === 'object', 'The where argument should be an object');
   assert(typeof data === 'object', 'The data argument should be an object');
   assert(cb === null || typeof cb === 'function', 'The cb argument should be a function');
-
+  
+  var query = { where: where };
+  this.applyScope(query);
+  this.applyProperties(data);
+  
+  where = query.where;
+  
   try {
     where = removeUndefined(where);
     where = this._coerce(where);
@@ -1025,6 +1102,7 @@ DataAccessObject.updateAll = function (where, data, cb) {
       cb && cb(err);
     });
   }
+  
   var connector = this.getDataSource().connector;
   connector.update(this.modelName, where, data, cb);
 };
@@ -1075,7 +1153,7 @@ DataAccessObject.prototype.remove =
  * @param {Mixed} value Value of property
  */
 DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
-  this[name] = value;
+  this[name] = value; // TODO [fabien] - currently not protected by applyProperties
 };    
 
 /**
@@ -1101,6 +1179,8 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
 DataAccessObject.prototype.setAttributes = function setAttributes(data) {
   if (typeof data !== 'object') return;
   
+  this.constructor.applyProperties(data, this);
+  
   var Model = this.constructor;
   var inst = this;
   
diff --git a/lib/model.js b/lib/model.js
index e12b9b4b..7e7a66f2 100644
--- a/lib/model.js
+++ b/lib/model.js
@@ -60,6 +60,10 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
   }
   var properties = _extend({}, ctor.definition.properties);
   data = data || {};
+  
+  if (typeof ctor.applyProperties === 'function') {
+    ctor.applyProperties(data);
+  }
 
   options = options || {};
   var applySetters = options.applySetters;
@@ -130,7 +134,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
     }
     if (properties[p]) {
       // Managed property
-      if (applySetters) {
+      if (applySetters || properties[p].id) {
         self[p] = propVal;
       } else {
         self.__data[p] = propVal;
diff --git a/lib/relation-definition.js b/lib/relation-definition.js
index 2590a3a5..1fa299e4 100644
--- a/lib/relation-definition.js
+++ b/lib/relation-definition.js
@@ -3,9 +3,10 @@
  */
 var assert = require('assert');
 var util = require('util');
+var utils = require('./utils');
 var i8n = require('inflection');
 var defineScope = require('./scope.js').defineScope;
-var mergeQuery = require('./scope.js').mergeQuery;
+var mergeQuery = utils.mergeQuery;
 var ModelBaseClass = require('./model.js');
 var applyFilter = require('./connectors/memory').applyFilter;
 var ValidationError = require('./validations.js').ValidationError;
diff --git a/lib/scope.js b/lib/scope.js
index 6b4dafb1..dfdb364d 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -1,13 +1,14 @@
 var i8n = require('inflection');
 var utils = require('./utils');
 var defineCachedRelations = utils.defineCachedRelations;
+var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
+var mergeQuery = utils.mergeQuery;
 var DefaultModelBaseClass = require('./model.js');
 
 /**
  * Module exports
  */
 exports.defineScope = defineScope;
-exports.mergeQuery = mergeQuery;
 
 function ScopeDefinition(definition) {
   this.isStatic = definition.isStatic;
@@ -229,35 +230,6 @@ function defineScope(cls, targetClass, name, params, methods, options) {
 
   cls['__count__' + name] = fn_count;
 
-  /*
-   * Extracting fixed property values for the scope from the where clause into
-   * the data object
-   *
-   * @param {Object} The data object
-   * @param {Object} The where clause
-   */
-  function setScopeValuesFromWhere(data, where, targetModel) {
-    for (var i in where) {
-      if (i === 'and') {
-        // Find fixed property values from each subclauses
-        for (var w = 0, n = where[i].length; w < n; w++) {
-          setScopeValuesFromWhere(data, where[i][w], targetModel);
-        }
-        continue;
-      }
-      var prop = targetModel.definition.properties[i];
-      if (prop) {
-        var val = where[i];
-        if (typeof val !== 'object' || val instanceof prop.type
-          || prop.type.name === 'ObjectID') // MongoDB key
-        {
-          // Only pick the {propertyName: propertyValue}
-          data[i] = where[i];
-        }
-      }
-    }
-  }
-
   // and it should have create/build methods with binded thisModelNameId param
   function build(data) {
     data = data || {};
@@ -300,60 +272,3 @@ function defineScope(cls, targetClass, name, params, methods, options) {
   
   return definition;
 }
-
-/*!
- * Merge query parameters
- * @param {Object} base The base object to contain the merged results
- * @param {Object} update The object containing updates to be merged
- * @returns {*|Object} The base object
- * @private
- */
-function mergeQuery(base, update) {
-  if (!update) {
-    return;
-  }
-  base = base || {};
-  if (update.where && Object.keys(update.where).length > 0) {
-    if (base.where && Object.keys(base.where).length > 0) {
-      base.where = {and: [base.where, update.where]};
-    } else {
-      base.where = update.where;
-    }
-  }
-
-  // Merge inclusion
-  if (update.include) {
-    if (!base.include) {
-      base.include = update.include;
-    } else {
-      var saved = base.include;
-      base.include = {};
-      base.include[update.include] = saved;
-    }
-  }
-  if (update.collect) {
-    base.collect = update.collect;
-  }
-
-  // set order
-  if (!base.order && update.order) {
-    base.order = update.order;
-  }
-  
-  // overwrite pagination
-  if (update.limit !== undefined) {
-    base.limit = update.limit;
-  }
-  if (update.skip !== undefined) {
-    base.skip = update.skip;
-  }
-  if (update.offset !== undefined) {
-    base.offset = update.offset;
-  }
-
-  // Overwrite fields
-  if (update.fields !== undefined) {
-    base.fields = update.fields;
-  }
-  return base;
-}
diff --git a/lib/utils.js b/lib/utils.js
index c774ecae..4e317058 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -7,6 +7,8 @@ exports.mergeSettings = mergeSettings;
 exports.isPlainObject = isPlainObject;
 exports.defineCachedRelations = defineCachedRelations;
 exports.sortObjectsByIds = sortObjectsByIds;
+exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
+exports.mergeQuery = mergeQuery;
 
 var traverse = require('traverse');
 
@@ -21,6 +23,101 @@ function safeRequire(module) {
   }
 }
 
+/*
+ * Extracting fixed property values for the scope from the where clause into
+ * the data object
+ *
+ * @param {Object} The data object
+ * @param {Object} The where clause
+ */
+function setScopeValuesFromWhere(data, where, targetModel) {
+  for (var i in where) {
+    if (i === 'and') {
+      // Find fixed property values from each subclauses
+      for (var w = 0, n = where[i].length; w < n; w++) {
+        setScopeValuesFromWhere(data, where[i][w], targetModel);
+      }
+      continue;
+    }
+    var prop = targetModel.definition.properties[i];
+    if (prop) {
+      var val = where[i];
+      if (typeof val !== 'object' || val instanceof prop.type
+        || prop.type.name === 'ObjectID') // MongoDB key
+      {
+        // Only pick the {propertyName: propertyValue}
+        data[i] = where[i];
+      }
+    }
+  }
+}
+
+/*!
+ * Merge query parameters
+ * @param {Object} base The base object to contain the merged results
+ * @param {Object} update The object containing updates to be merged
+ * @param {Object} spec Optionally specifies parameters to exclude (set to false)
+ * @returns {*|Object} The base object
+ * @private
+ */
+function mergeQuery(base, update, spec) {
+  if (!update) {
+    return;
+  }
+  spec = spec || {};
+  base = base || {};
+  
+  if (update.where && Object.keys(update.where).length > 0) {
+    if (base.where && Object.keys(base.where).length > 0) {
+      base.where = {and: [base.where, update.where]};
+    } else {
+      base.where = update.where;
+    }
+  }
+
+  // Merge inclusion
+  if (spec.include !== false && update.include) {
+    if (!base.include) {
+      base.include = update.include;
+    } else {
+      var saved = base.include;
+      base.include = {};
+      base.include[update.include] = saved;
+    }
+  }
+  
+  if (spec.collect !== false && update.collect) {
+    base.collect = update.collect;
+  }
+  
+  // Overwrite fields
+  if (spec.fields !== false && update.fields !== undefined) {
+    base.fields = update.fields;
+  }
+  
+  // set order
+  if ((!base.order || spec.order === false) && update.order) {
+    base.order = update.order;
+  }
+  
+  // overwrite pagination
+  if (spec.limit !== false && update.limit !== undefined) {
+    base.limit = update.limit;
+  }
+  
+  var skip = spec.skip !== false && spec.offset !== false;
+  
+  if (skip && update.skip !== undefined) {
+    base.skip = update.skip;
+  }
+  
+  if (skip && update.offset !== undefined) {
+    base.offset = update.offset;
+  }
+  
+  return base;
+}
+
 function fieldsToArray(fields, properties) {
   if (!fields) return;
 
diff --git a/test/default-scope.test.js b/test/default-scope.test.js
new file mode 100644
index 00000000..0d510cff
--- /dev/null
+++ b/test/default-scope.test.js
@@ -0,0 +1,808 @@
+// This test written in mocha+should.js
+var should = require('./init.js');
+var async = require('async');
+
+var db, Category, Product, Tool, Widget, Thing;
+
+// This test requires a connector that can
+// handle a custom collection or table name
+
+// TODO [fabien] add table for pgsql/mysql
+// TODO [fabien] change model definition - see #293
+
+var setupProducts = function(ids, done) {
+  async.series([
+    function(next) {
+      Tool.create({name: 'Tool Z'}, function(err, inst) {
+        ids.toolZ = inst.id;
+        next();
+      });
+    },
+    function(next) {
+      Widget.create({name: 'Widget Z'}, function(err, inst) {
+        ids.widgetZ = inst.id;
+        next();
+      });
+    },
+    function(next) {
+      Tool.create({name: 'Tool A', active: false}, function(err, inst) {
+        ids.toolA = inst.id;
+        next();
+      });
+    },
+    function(next) {
+      Widget.create({name: 'Widget A'}, function(err, inst) {
+        ids.widgetA = inst.id;
+        next();
+      });
+    },
+    function(next) {
+      Widget.create({name: 'Widget B', active: false}, function(err, inst) {
+        ids.widgetB = inst.id;
+        next();
+      });
+    }
+  ], done);
+};
+
+describe('default scope', function () {
+  
+  before(function (done) {
+    db = getSchema();
+    
+    Category = db.define('Category', {
+      name: String
+    });
+
+    Product = db.define('Product', {
+      name: String,
+      kind: String,
+      description: String,
+      active: { type: Boolean, default: true }
+    }, {
+      scope: { order: 'name' },
+      scopes: { active: { where: { active: true } } }
+    });
+    
+    Product.lookupModel = function(data) {
+      var m = this.dataSource.models[data.kind];
+      if (m.base === this) return m;
+      return this;
+    };
+    
+    Tool = db.define('Tool', Product.definition.properties, {
+        base: 'Product',
+        scope: { where: { kind: 'Tool' }, order: 'name' },
+        scopes: { active: { where: { active: true } } },
+        mongodb: { collection: 'Product' },
+        memory: { collection: 'Product' }
+    });
+    
+    Widget = db.define('Widget', Product.definition.properties, {
+        base: 'Product',
+        properties: { kind: 'Widget' },
+        scope: { where: { kind: 'Widget' }, order: 'name' },
+        scopes: { active: { where: { active: true } } },
+        mongodb: { collection: 'Product' },
+        memory: { collection: 'Product' }
+    });
+    
+    // inst is only valid for instance methods
+    // like save, updateAttributes
+    
+    var scopeFn = function(target, inst) {
+      return { where: { kind: this.modelName } };
+    };
+    
+    var propertiesFn = function(target, inst) {
+      return { kind: this.modelName };
+    };
+    
+    Thing = db.define('Thing', Product.definition.properties, {
+        base: 'Product',
+        attributes: propertiesFn,
+        scope: scopeFn,
+        mongodb: { collection: 'Product' },
+        memory: { collection: 'Product' }
+    });
+    
+    Category.hasMany(Product);
+    Category.hasMany(Tool, {scope: {order: 'name DESC'}});
+    Category.hasMany(Widget);
+    Category.hasMany(Thing);
+    
+    Product.belongsTo(Category);
+    Tool.belongsTo(Category);
+    Widget.belongsTo(Category);
+    Thing.belongsTo(Category);
+    
+    db.automigrate(done);
+  });
+  
+  describe('manipulation', function() {
+    
+    var ids = {};
+    
+    before(function(done) {
+      db.automigrate(done);
+    });
+    
+    it('should return a scoped instance', function() {
+      var p = new Tool({name: 'Product A', kind:'ignored'});
+      p.name.should.equal('Product A');
+      p.kind.should.equal('Tool');
+      p.setAttributes({ kind: 'ignored' });
+      p.kind.should.equal('Tool');
+      
+      p.setAttribute('kind', 'other'); // currently not enforced
+      p.kind.should.equal('other');
+    });
+    
+    it('should create a scoped instance - tool', function(done) {
+      Tool.create({name: 'Product A', kind: 'ignored'}, function(err, p) {
+        should.not.exist(err);
+        p.name.should.equal('Product A');
+        p.kind.should.equal('Tool');
+        ids.productA = p.id;
+        done();
+      });
+    });
+    
+    it('should create a scoped instance - widget', function(done) {
+      Widget.create({name: 'Product B', kind: 'ignored'}, function(err, p) {
+        should.not.exist(err);
+        p.name.should.equal('Product B');
+        p.kind.should.equal('Widget');
+        ids.productB = p.id;
+        done();
+      });
+    });
+  
+    it('should update a scoped instance - updateAttributes', function(done) {
+      Tool.findById(ids.productA, function(err, p) {
+        p.updateAttributes({description: 'A thing...', kind: 'ingored'}, function(err, inst) {
+          should.not.exist(err);
+          p.name.should.equal('Product A');
+          p.kind.should.equal('Tool');
+          p.description.should.equal('A thing...');
+          done();
+        });
+      });
+    });
+  
+    it('should update a scoped instance - save', function(done) {
+      Tool.findById(ids.productA, function(err, p) {
+        p.description = 'Something...';
+        p.kind = 'ignored';
+        p.save(function(err, inst) {
+          should.not.exist(err);
+          p.name.should.equal('Product A');
+          p.kind.should.equal('Tool');
+          p.description.should.equal('Something...');
+          Tool.findById(ids.productA, function(err, p) {
+            p.kind.should.equal('Tool');
+            done();
+          });
+        });
+      });
+    });
+  
+    it('should update a scoped instance - updateOrCreate', function(done) {
+      var data = {id: ids.productA, description: 'Anything...', kind: 'ingored'};
+      Tool.updateOrCreate(data, function(err, p) {
+          should.not.exist(err);
+          p.name.should.equal('Product A');
+          p.kind.should.equal('Tool');
+          p.description.should.equal('Anything...');
+          done();
+      });
+    });
+  
+  });
+  
+  describe('findById', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope', function(done) {
+      Product.findById(ids.toolA, function(err, inst) {
+        should.not.exist(err);
+        inst.name.should.equal('Tool A');
+        inst.should.be.instanceof(Tool);
+        done();
+      });
+    });
+    
+    it('should apply default scope - tool', function(done) {
+      Tool.findById(ids.toolA, function(err, inst) {
+        should.not.exist(err);
+        inst.name.should.equal('Tool A');
+        done();
+      });
+    });
+    
+    it('should apply default scope (no match)', function(done) {
+      Widget.findById(ids.toolA, function(err, inst) {
+        should.not.exist(err);
+        should.not.exist(inst);
+        done();
+      });
+    });
+    
+  });
+  
+  describe('find', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope - order', function(done) {
+      Product.find(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(5);
+        products[0].name.should.equal('Tool A');
+        products[1].name.should.equal('Tool Z');
+        products[2].name.should.equal('Widget A');
+        products[3].name.should.equal('Widget B');
+        products[4].name.should.equal('Widget Z');
+        
+        products[0].should.be.instanceof(Product);
+        products[0].should.be.instanceof(Tool);
+        
+        products[2].should.be.instanceof(Product);
+        products[2].should.be.instanceof(Widget);
+        
+        done();
+      });
+    });
+    
+    it('should apply default scope - order override', function(done) {
+      Product.find({ order: 'name DESC' }, function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(5);
+        products[0].name.should.equal('Widget Z');
+        products[1].name.should.equal('Widget B');
+        products[2].name.should.equal('Widget A');
+        products[3].name.should.equal('Tool Z');
+        products[4].name.should.equal('Tool A');
+        done();
+      });
+    });
+    
+    it('should apply default scope - tool', function(done) {
+      Tool.find(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(2);
+        products[0].name.should.equal('Tool A');
+        products[1].name.should.equal('Tool Z');
+        done();
+      });
+    });
+    
+    it('should apply default scope - where (widget)', function(done) {
+      Widget.find({ where: { active: true } }, function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(2);
+        products[0].name.should.equal('Widget A');
+        products[1].name.should.equal('Widget Z');
+        done();
+      });
+    });
+    
+    it('should apply default scope - order (widget)', function(done) {
+      Widget.find({ order: 'name DESC' }, function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(3);
+        products[0].name.should.equal('Widget Z');
+        products[1].name.should.equal('Widget B');
+        products[2].name.should.equal('Widget A');
+        done();
+      });
+    });
+  
+  });
+  
+  describe('exists', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope', function(done) {
+      Product.exists(ids.widgetA, function(err, exists) {
+        should.not.exist(err);
+        exists.should.be.true;
+        done();
+      });
+    });
+    
+    it('should apply default scope - tool', function(done) {
+      Tool.exists(ids.toolZ, function(err, exists) {
+        should.not.exist(err);
+        exists.should.be.true;
+        done();
+      });
+    });
+    
+    it('should apply default scope - widget', function(done) {
+      Widget.exists(ids.widgetA, function(err, exists) {
+        should.not.exist(err);
+        exists.should.be.true;
+        done();
+      });
+    });
+    
+    it('should apply default scope - tool (no match)', function(done) {
+      Tool.exists(ids.widgetA, function(err, exists) {
+        should.not.exist(err);
+        exists.should.be.false;
+        done();
+      });
+    });
+    
+    it('should apply default scope - widget (no match)', function(done) {
+      Widget.exists(ids.toolZ, function(err, exists) {
+        should.not.exist(err);
+        exists.should.be.false;
+        done();
+      });
+    });
+  
+  });
+  
+  describe('count', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope - order', function(done) {
+      Product.count(function(err, count) {
+        should.not.exist(err);
+        count.should.equal(5);
+        done();
+      });
+    });
+    
+    it('should apply default scope - tool', function(done) {
+      Tool.count(function(err, count) {
+        should.not.exist(err);
+        count.should.equal(2);
+        done();
+      });
+    });
+    
+    it('should apply default scope - widget', function(done) {
+      Widget.count(function(err, count) {
+        should.not.exist(err);
+        count.should.equal(3);
+        done();
+      });
+    });
+    
+    it('should apply default scope - where', function(done) {
+      Widget.count({name: 'Widget Z'}, function(err, count) {
+        should.not.exist(err);
+        count.should.equal(1);
+        done();
+      });
+    });
+    
+    it('should apply default scope - no match', function(done) {
+      Tool.count({name: 'Widget Z'}, function(err, count) {
+        should.not.exist(err);
+        count.should.equal(0);
+        done();
+      });
+    });
+  
+  });
+  
+  describe('removeById', function() {
+    
+    var ids = {};
+    
+    function isDeleted(id, done) {
+      Product.exists(id, function(err, exists) {
+        should.not.exist(err);
+        exists.should.be.false;
+        done();
+      });
+    };
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope', function(done) {
+      Product.removeById(ids.widgetZ, function(err) {
+        should.not.exist(err);
+        isDeleted(ids.widgetZ, done);
+      });
+    });
+    
+    it('should apply default scope - tool', function(done) {
+      Tool.removeById(ids.toolA, function(err) {
+        should.not.exist(err);
+        isDeleted(ids.toolA, done);
+      });
+    });
+    
+    it('should apply default scope - no match', function(done) {
+      Tool.removeById(ids.widgetA, function(err) {
+        should.not.exist(err);
+        Product.exists(ids.widgetA, function(err, exists) {
+          should.not.exist(err);
+          exists.should.be.true;
+          done();
+        });
+      });
+    });
+    
+    it('should apply default scope - widget', function(done) {
+      Widget.removeById(ids.widgetA, function(err) {
+        should.not.exist(err);
+        isDeleted(ids.widgetA, done);
+      });
+    });
+    
+    it('should apply default scope - verify', function(done) {
+      Product.find(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(2);
+        products[0].name.should.equal('Tool Z');
+        products[1].name.should.equal('Widget B');
+        done();
+      });
+    });
+    
+  });
+  
+  describe('update', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope', function(done) {
+      Widget.update({active: false},{active: true, kind: 'ignored'}, function(err) {
+        should.not.exist(err);
+        Widget.find({where: { active: true }}, function(err, products) {
+          should.not.exist(err);
+          products.should.have.length(3);
+          products[0].name.should.equal('Widget A');
+          products[1].name.should.equal('Widget B');
+          products[2].name.should.equal('Widget Z');
+          done();
+        });
+      });
+    });
+    
+    it('should apply default scope - no match', function(done) {
+      Tool.update({name: 'Widget A'},{name: 'Ignored'}, function(err) {
+        should.not.exist(err);
+        Product.findById(ids.widgetA, function(err, product) {
+          should.not.exist(err);
+          product.name.should.equal('Widget A');
+          done();
+        });
+      });
+    });
+    
+    it('should have updated within scope', function(done) {
+      Product.find({where: {active: true}}, function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(4);
+        products[0].name.should.equal('Tool Z');
+        products[1].name.should.equal('Widget A');
+        products[2].name.should.equal('Widget B');
+        products[3].name.should.equal('Widget Z');
+        done();
+      });
+    });
+    
+  });
+  
+  describe('remove', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should apply default scope - custom where', function(done) {
+      Widget.remove({name: 'Widget A'}, function(err) {
+        should.not.exist(err);
+        Product.find(function(err, products) {
+          products.should.have.length(4);
+          products[0].name.should.equal('Tool A');
+          products[1].name.should.equal('Tool Z');
+          products[2].name.should.equal('Widget B');
+          products[3].name.should.equal('Widget Z');
+          done();
+        });
+      });
+    });
+    
+    it('should apply default scope - custom where (no match)', function(done) {
+      Tool.remove({name: 'Widget Z'}, function(err) {
+        should.not.exist(err);
+        Product.find(function(err, products) {
+          products.should.have.length(4);
+          products[0].name.should.equal('Tool A');
+          products[1].name.should.equal('Tool Z');
+          products[2].name.should.equal('Widget B');
+          products[3].name.should.equal('Widget Z');
+          done();
+        });
+      });
+    });
+    
+    it('should apply default scope - deleteAll', function(done) {
+      Tool.deleteAll(function(err) {
+        should.not.exist(err);
+        Product.find(function(err, products) {
+          products.should.have.length(2);
+          products[0].name.should.equal('Widget B');
+          products[1].name.should.equal('Widget Z');
+          done();
+        });
+      });
+    });
+    
+    it('should create a scoped instance - tool', function(done) {
+      Tool.create({name: 'Tool B'}, function(err, p) {
+        should.not.exist(err);
+        Product.find(function(err, products) {
+          products.should.have.length(3);
+          products[0].name.should.equal('Tool B');
+          products[1].name.should.equal('Widget B');
+          products[2].name.should.equal('Widget Z');
+          done();
+        });
+      });
+    });
+    
+    it('should apply default scope - destroyAll', function(done) {
+      Widget.destroyAll(function(err) {
+        should.not.exist(err);
+        Product.find(function(err, products) {
+          products.should.have.length(1);
+          products[0].name.should.equal('Tool B');
+          done();
+        });
+      });
+    });
+    
+  });
+  
+  describe('scopes', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(setupProducts.bind(null, ids, done));
+    });
+    
+    it('should merge with default scope', function(done) {
+      Product.active(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(3);
+        products[0].name.should.equal('Tool Z');
+        products[1].name.should.equal('Widget A');
+        products[2].name.should.equal('Widget Z');
+        done();
+      });
+    });
+    
+    it('should merge with default scope - tool', function(done) {
+      Tool.active(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(1);
+        products[0].name.should.equal('Tool Z');
+        done();
+      });
+    });
+    
+    it('should merge with default scope - widget', function(done) {
+      Widget.active(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(2);
+        products[0].name.should.equal('Widget A');
+        products[1].name.should.equal('Widget Z');
+        done();
+      });
+    });
+    
+  });
+  
+  describe('scope function', function() {
+    
+    before(function(done) {
+      db.automigrate(done);
+    });
+    
+    it('should create a scoped instance - widget', function(done) {
+      Widget.create({name: 'Product', kind:'ignored'}, function(err, p) {
+        p.name.should.equal('Product');
+        p.kind.should.equal('Widget');
+        done();
+      });
+    });
+    
+    it('should create a scoped instance - thing', function(done) {
+      Thing.create({name: 'Product', kind:'ignored'}, function(err, p) {
+        p.name.should.equal('Product');
+        p.kind.should.equal('Thing');
+        done();
+      });
+    });
+    
+    it('should find a scoped instance - widget', function(done) {
+      Widget.findOne({where: {name: 'Product'}}, function(err, p) {
+        p.name.should.equal('Product');
+        p.kind.should.equal('Widget');
+        done();
+      });
+    });
+    
+    it('should find a scoped instance - thing', function(done) {
+      Thing.findOne({where: {name: 'Product'}}, function(err, p) {
+        p.name.should.equal('Product');
+        p.kind.should.equal('Thing');
+        done();
+      });
+    });
+    
+    it('should find a scoped instance - thing', function(done) {
+      Product.find({where: {name: 'Product'}}, function(err, products) {
+        products.should.have.length(2);
+        products[0].name.should.equal('Product');
+        products[1].name.should.equal('Product');
+        var kinds = products.map(function(p) { return p.kind; })
+        kinds.sort();
+        kinds.should.eql(['Thing', 'Widget']);
+        done();
+      });
+    });
+  
+  });
+  
+  describe('relations', function() {
+    
+    var ids = {};
+    
+    before(function (done) {
+      db.automigrate(done);
+    });
+    
+    before(function (done) {
+      Category.create({name: 'Category A'}, function(err, cat) {
+        ids.categoryA = cat.id;
+        async.series([
+          function(next) {
+            cat.widgets.create({name: 'Widget B', kind: 'ignored'}, next);
+          },
+          function(next) {
+            cat.widgets.create({name: 'Widget A'}, next);
+          },
+          function(next) {
+            cat.tools.create({name: 'Tool A'}, next);
+          },
+          function(next) {
+            cat.things.create({name: 'Thing A'}, next);
+          }
+        ], done);
+      });
+    });
+    
+    it('should apply default scope - products', function(done) {
+      Category.findById(ids.categoryA, function(err, cat) {
+        should.not.exist(err);
+        cat.products(function(err, products) {
+          should.not.exist(err);
+          products.should.have.length(4);
+          products[0].name.should.equal('Thing A');
+          products[1].name.should.equal('Tool A');
+          products[2].name.should.equal('Widget A');
+          products[3].name.should.equal('Widget B');
+          
+          products[0].should.be.instanceof(Product);
+          products[0].should.be.instanceof(Thing);
+          
+          products[1].should.be.instanceof(Product);
+          products[1].should.be.instanceof(Tool);
+          
+          products[2].should.be.instanceof(Product);
+          products[2].should.be.instanceof(Widget);
+          
+          done();
+        });
+      });
+    });
+    
+    it('should apply default scope - widgets', function(done) {
+      Category.findById(ids.categoryA, function(err, cat) {
+        should.not.exist(err);
+        cat.widgets(function(err, products) {
+          should.not.exist(err);
+          products.should.have.length(2);
+          products[0].should.be.instanceof(Widget);
+          products[0].name.should.equal('Widget A');
+          products[1].name.should.equal('Widget B');
+          products[0].category(function(err, inst) {
+            inst.name.should.equal('Category A');
+            done();
+          });
+        });
+      });
+    });
+    
+    it('should apply default scope - tools', function(done) {
+      Category.findById(ids.categoryA, function(err, cat) {
+        should.not.exist(err);
+        cat.tools(function(err, products) {
+          should.not.exist(err);
+          products.should.have.length(1);
+          products[0].should.be.instanceof(Tool);
+          products[0].name.should.equal('Tool A');
+          products[0].category(function(err, inst) {
+            inst.name.should.equal('Category A');
+            done();
+          });
+        });
+      });
+    });
+    
+    it('should apply default scope - things', function(done) {
+      Category.findById(ids.categoryA, function(err, cat) {
+        should.not.exist(err);
+        cat.things(function(err, products) {
+          should.not.exist(err);
+          products.should.have.length(1);
+          products[0].should.be.instanceof(Thing);
+          products[0].name.should.equal('Thing A');
+          products[0].category(function(err, inst) {
+            inst.name.should.equal('Category A');
+            done();
+          });
+        });
+      });
+    });
+    
+    it('should create related item with default scope', function(done) {
+      Category.findById(ids.categoryA, function(err, cat) {
+        cat.tools.create({name: 'Tool B'}, done);
+      });
+    });
+    
+    it('should use relation scope order', function(done) {
+      Category.findById(ids.categoryA, function(err, cat) {
+        should.not.exist(err);
+        cat.tools(function(err, products) {
+          should.not.exist(err);
+          products.should.have.length(2);
+          products[0].name.should.equal('Tool B');
+          products[1].name.should.equal('Tool A');
+          done();
+        });
+      });
+    });
+    
+  });
+  
+});
diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js
index 10c9c10e..41d600f6 100644
--- a/test/loopback-dl.test.js
+++ b/test/loopback-dl.test.js
@@ -639,7 +639,7 @@ describe('Load models with base', function () {
     assert(Customer.prototype.instanceMethod === User.prototype.instanceMethod);
     assert.equal(Customer.base, User);
     assert.equal(Customer.base, Customer.super_);
-
+    
     try {
       var Customer1 = ds.define('Customer1', {vip: Boolean}, {base: 'User1'});
     } catch (e) {
diff --git a/test/memory.test.js b/test/memory.test.js
index 453ac9e8..66ad2346 100644
--- a/test/memory.test.js
+++ b/test/memory.test.js
@@ -269,6 +269,48 @@ describe('Memory connector', function () {
     }
 
   });
+  
+  it('should use collection setting', function (done) {
+    var ds = new DataSource({
+      connector: 'memory'
+    });
+    
+    var Product = ds.createModel('Product', {
+      name: String
+    });
+    
+    var Tool = ds.createModel('Tool', {
+      name: String
+    }, {memory: {collection: 'Product'}});
+    
+    var Widget = ds.createModel('Widget', {
+      name: String
+    }, {memory: {collection: 'Product'}});
+    
+    ds.connector.getCollection('Tool').should.equal('Product');
+    ds.connector.getCollection('Widget').should.equal('Product');
+    
+    async.series([
+      function(next) {
+        Tool.create({ name: 'Tool A' }, next);
+      },
+      function(next) {
+        Tool.create({ name: 'Tool B' }, next);
+      },
+      function(next) {
+        Widget.create({ name: 'Widget A' }, next);
+      }
+    ], function(err) {
+      Product.find(function(err, products) {
+        should.not.exist(err);
+        products.should.have.length(3);
+        products[0].toObject().should.eql({ name: 'Tool A', id: 1 });
+        products[1].toObject().should.eql({ name: 'Tool B', id: 2 });
+        products[2].toObject().should.eql({ name: 'Widget A', id: 3 });
+        done();
+      });
+    });
+  });
 
 });
 
diff --git a/test/relations.test.js b/test/relations.test.js
index 12e78d68..f6848d11 100644
--- a/test/relations.test.js
+++ b/test/relations.test.js
@@ -335,7 +335,7 @@ describe('relations', function () {
         physician.patients.findById(id, function (err, ch) {
           should.not.exist(err);
           should.exist(ch);
-          ch.id.should.equal(id);
+          ch.id.should.eql(id);
           done();
         });
       }
@@ -387,7 +387,7 @@ describe('relations', function () {
         physician.patients.findById(id, function (err, ch) {
           should.not.exist(err);
           should.exist(ch);
-          ch.id.should.equal(id);
+          ch.id.should.eql(id);
           ch.name.should.equal('aa');
           done();
         });
@@ -1659,7 +1659,7 @@ describe('relations', function () {
           should.not.exist(e);
           should.exist(tags);
           
-          article.tags().should.eql(tags);
+          article.tagNames().should.eql(tags);
           
           done();
         });