Permalink
Browse files

ODM starting

  • Loading branch information...
1 parent 7e0ef24 commit d02e87147925db3c057f12de0a05ee7cfea726f1 @pgte committed Jan 28, 2011
View
@@ -21,7 +21,8 @@ test: mkdirtmp
operators/test_nin operators/test_neq operators/test_or operators/test_global_or operators/test_order operators/test_desc_order operators/test_chainable \
operators/test_find_stream operators/test_find_stream_chained \
recovery/collection_recovery_test \
- replication/test_master replication/test_slave replication/test_slave_reconnect replication/test_master_temp_roll replication/test_master_seek
+ replication/test_master replication/test_slave replication/test_slave_reconnect replication/test_master_temp_roll replication/test_master_seek \
+ model/test_model_save
benchmark: mkdirtmp mkdirresults
node tools/benchmarks.js benchmark_find benchmark_collection benchmark_collection_filter benchmark_key_map benchmark_key_map_each_with_pos benchmark_indexed_key_map \
@@ -110,7 +110,7 @@ p.note Everything here is experimental, and the API is no exception, so expect t
ul
li <b>x</b>: integer >= 0
- h2 .bulk (callback)
+ h2 .all (callback)
p Get all the objects at once, on one callback.
p
ul
@@ -102,7 +102,7 @@
pre
code
| // find the with age > 28 and <= 35 OR that are female
- | db.users.find({age: {$gt: 28, $lte: 35}}).or({sex: {$eq: 'f'}}).bulk(function(err, users) {
+ | db.users.find({age: {$gt: 28, $lte: 35}}).or({sex: {$eq: 'f'}}).all(function(err, users) {
| if (err) { throw err; }
| users.forEach(function(user) {
| console.log(user);
@@ -5,7 +5,8 @@ var path = require('path'),
EventEmitter = require('events').EventEmitter,
options_merger = require('../util/options_merger'),
cached_key_map = require('../cached_key_map'),
- replication = require('./replication');
+ replication = require('./replication'),
+ Model = require('./model/model');
var default_meta_options = {
meta_compact_interval: 1000 * 60 * 60, // 1 hour, +- 50%
@@ -430,4 +431,10 @@ Database.prototype.stopListening = function(listeners) {
this[key_map_name].removeListener('put', listeners[key_map_name]);
}
}
-};
+};
+
+/* Model */
+
+Database.prototype.define = function(modelName, schema, options) {
+ return Model.define(this, modelName, schema, options)
+}
@@ -212,6 +212,10 @@ Finder.prototype.executeBulk = function(callback) {
module.exports.create = function(key_map, query) {
var finder = new Finder(key_map, query);
var chainable = function(callback) {
+ return chainable.execute(callback);
+ };
+
+ chainable.execute = function(callback) {
finder.execute(callback);
return chainable;
};
@@ -252,6 +256,10 @@ module.exports.create = function(key_map, query) {
};
chainable.bulk = function(callback) {
+ return chainable.all(callback);
+ };
+
+ chainable.all = function(callback) {
finder.executeBulk(callback);
return chainable;
};
@@ -0,0 +1,128 @@
+var validator = require('./validator'),
+ util = require('util');
+
+var Document = function(model, doc) {
+ this.model = model;
+ this.doc = doc;
+};
+
+module.exports.create = function(model, doc) {
+ return new Document(model, doc);
+};
+
+Document.prototype.isNew = function() {
+ return !this.id;
+};
+
+Document.prototype._assureNotNew = function() {
+ if (this.isNew()) { throw new Error('record is not saved yet'); }
+}
+
+var newId = function() {
+ function S4() {
+ return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
+ }
+ return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
+};
+
+Document.prototype.save = function(callback) {
+ var self = this;
+ if (self.isNew()) {
+ self.id = newId();
+ self.doc.id = self.id;
+ }
+ var validation = validator.validate(self.doc, self.model.schema);
+ if (!validation.valid) {
+ callback(validation.errors);
+ return;
+ }
+ self.model.put(self.id, self.doc, function() {
+ callback(null);
+ });
+};
+
+Document.prototype.atomic = function(callback, document_callback, saved_callback) {
+ var self = this;
+ self._assureNotNew();
+ self.model.atomic(self.id, document_callback, saved_callback);
+};
+
+Document.prototype.destroy = function(callback) {
+ this._assureNotNew();
+ this.model.delete(this.id, callback)
+};
+
+Document.prototype.reload = function(callback) {
+ var self = this;
+ self._assureNotNew();
+ self.model.get(self.id, function(record) {
+ this.doc = record;
+ this.doc.id = self.id;
+ callback();
+ });
+};
+
+Document.prototype.toString = function() {
+ return util.inspect(this.doc);
+};
+
+Document.prototype.equal = function(obj) {
+ if (!obj) {
+ console.log('B');
+ return false;
+ }
+
+ if (this.model != obj.model) {
+ console.log('C');
+ return false;
+ }
+
+ var pSlice = Array.prototype.slice;
+
+ return (function _deepEqual(a, b) {
+
+ if (a === b) {
+ return true;
+ }
+ // an identical 'prototype' property.
+ if (a.prototype !== b.prototype) {console.log('D'); return false;}
+ //~~~I've managed to break Object.keys through screwy arguments passing.
+ // Converting to array solves the problem.
+ try {
+ var ka = Object.keys(a),
+ kb = Object.keys(b),
+ key, i;
+ } catch (e) {//happens when one is a string literal and the other isn't
+ console.log(e)
+ console.log('E');
+ return false;
+ }
+ // having the same number of owned properties (keys incorporates
+ // hasOwnProperty)
+ if (ka.length != kb.length) {
+ console.log('F');
+ return false;
+ }
+
+ //the same set of keys (although not necessarily the same order),
+ ka.sort();
+ kb.sort();
+ //~~~cheap key test
+ for (i = ka.length - 1; i >= 0; i--) {
+ if (ka[i] != kb[i]) {
+ console.log('G');
+ return false;
+ }
+ }
+ //equivalent values for every corresponding key, and
+ //~~~possibly expensive deep test
+ for (i = ka.length - 1; i >= 0; i--) {
+ key = ka[i];
+ console.log(key);
+ if (!_deepEqual(a[key], b[key])) {console.log('A'); return false;}
+ }
+ return true;
+ })(this.doc, obj.doc);
+};
+
+module.exports.proto = Document.prototype;
@@ -0,0 +1,182 @@
+var registrar = require('./registrar'),
+ promise = require('../../util/promise'),
+ definers = require('./schema').definers,
+ Finder = require('../finder'),
+ mixin = require('../../util/mixin'),
+ Document = require('./document');
+
+var Model = function(database, name, schema, options) {
+ var self = this;
+ this.database = database;
+ this.name = name;
+ this.schema = schema || {};
+
+ this.protoBase = {};
+ this.protoBase.__proto__ = Document.proto;
+
+ registrar.put(name, this);
+ if (!this.database) {
+ throw new Error('No database connection defined');
+ }
+
+ this.initiatedPromise = promise(function(callback) {
+ database.ensure(self.name, options, function(err, coll) {
+ if (err) { callback(err); return; }
+ self.collection = coll;
+ callback(null);
+ });
+ }, function(err) {
+ self.database.emit('error', err);
+ });
+};
+
+module.exports.define = function(database, name, schema, options) {
+ return new Model(database, name, schema);
+};
+
+Model.prototype._error = function(err) {
+ this.database.emit('error', err);
+};
+
+Model.prototype.get = function(id, callback) {
+ var self = this;
+ this.initiatedPromise(function() {
+ self.collection.get(id, function(err, value) {
+ if (err) { self._error(err); return; }
+ if (value === null) {
+ callback(null); return;
+ }
+ var doc = self.instantiate(value);
+ doc.id = id;
+ doc.doc.id = id;
+ callback(doc);
+ });
+ });
+};
+
+Model.prototype.put = function(id, value, callback) {
+ var self = this;
+ this.initiatedPromise(function() {
+ delete value.id;
+ self.collection.put(id, value, function(err) {
+ if (err) { self._error(err); return; }
+ callback();
+ })
+ });
+};
+
+Model.prototype.atomic = function(id, doc_callback, final_callback) {
+ var self = this;
+ this.initiatedPromise(function() {
+ self.collection.atomic(id, function(value) {
+ var doc = self.instantiate(value);
+ doc.id = id;
+ return doc_callback(doc);
+ }, function(err) {
+ if (err) { self._error(err); return; }
+ final_callback();
+ });
+ });
+
+};
+
+Model.prototype.delete = function(id, callback) {
+ var self = this;
+ this.initiatedPromise(function() {
+ self.collection.delete(id, function(err) {
+ if (err) { self._error(err); return; }
+ callback();
+ });
+ });
+};
+
+Model.prototype.find = function(query) {
+ var self = this;
+ var finder = Finder.create(null, query);
+ var old_execute = finder.execute,
+ old_stream = finder.stream;
+ old_all = finder.all;
+
+ finder.execute = function(callback) {
+ this.initiatedPromise(function() {
+ finder.key_map = self.collection;
+ old_execute.call(finder, function(err, key, value) {
+ if (err) { self._error(err); return; }
+ var doc = self.instantiate(value);
+ doc.id = key;
+ callback(doc);
+ });
+ });
+ };
+
+ finder.all = function(callback) {
+ this.initiatedPromise(function() {
+ finder.key_map = self.collection;
+ old_all.call(finder, function(err, records) {
+ if (err) { self._error(err); return; }
+ var docs = [];
+ records.forEach(function(record) {
+ docs.push(self.instantiate(record));
+ });
+ callback(docs);
+ });
+ });
+ };
+
+ finder.stream = function(callback) {
+ this.initiatedPromise(function() {
+ finder.key_map = self.collection;
+ old_stream.call(finder, function(stream) {
+
+ stream.on('error', function(err) {
+ self._error(err);
+ });
+
+ stream.on('record', function(key, value) {
+ value.id = key;
+ stream.emit('document', self.instantiate(value));
+ });
+
+ return stream;
+ });
+ });
+ };
+ return finder;
+};
+
+Model.prototype.instantiate = function(doc) {
+ var doc = Document.create(this, doc);
+ doc.__proto__ = this.protoBase;
+ return doc;
+};
+
+Model.prototype.property = function(name, typeOrSchema, schema) {
+ var definer = {};
+ var type = (function () {
+ switch (typeof(typeOrSchema)) {
+ case "string": return typeOrSchema;
+ case "function": return typeOrSchema.name.toLowerCase();
+ case "object": schema = typeOrSchema;
+ case "undefined": return "string";
+ default: throw new(Error)("Argument Error");
+ }
+ })();
+ schema = schema || {};
+ schema.type = schema.type || type;
+ if (!this.schema.properties) {
+ this.schema.properties = {};
+ }
+
+ this.schema.properties[name] = definer.property = schema;
+
+ mixin(definer, definers.all, definers[schema.type] || {});
+
+ this.protoBase.__defineGetter__(name, function() {
+ return this.doc[name];
+ });
+ this.protoBase.__defineSetter__(name, function(value) {
+ this.doc[name] = value;
+ });
+
+ return definer;
+};
Oops, something went wrong.

0 comments on commit d02e871

Please sign in to comment.