Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added observable cursors

  • Loading branch information...
commit 82b2ad93599707f4448aac13a62e97394a31c435 1 parent f278085
@crcn authored
Showing with 327 additions and 375 deletions.
  1. +0 −1  README.md
  2. +17 −0 docs/pseudo-schema.js
  3. +45 −0 examples/memory.js
  4. +6 −0 lib/abstract/abstract.js
  5. +2 −11 {src/common → lib}/abstract/collection.js
  6. +2 −2 {src/common → lib}/abstract/cursor.js
  7. +11 −0 lib/abstract/db.js
  8. +16 −4 {src/common → lib}/chainable.js
  9. +38 −7 {src/common → lib}/collection.js
  10. +22 −5 {src/common → lib}/cursor.js
  11. +44 −0 lib/cursorObserver.js
  12. +4 −2 {src/common → lib}/database.js
  13. 0  {src/common → lib}/drivers/core/collection.js
  14. 0  {src/common → lib}/drivers/core/database.js
  15. +84 −0 lib/drivers/memory/collection.js
  16. +4 −4 {src/common → lib}/drivers/memory/cursor.js
  17. +24 −0 lib/drivers/memory/index.js
  18. 0  {src/common → lib}/index.js
  19. 0  {src/common → lib}/model.js
  20. +0 −20 lib/node/collection.js
  21. +0 −23 lib/node/database.js
  22. 0  lib/node/model.js
  23. +0 −24 lib/node/package.json
  24. 0  lib/node/persist/core/collection.js
  25. 0  lib/node/persist/core/database.js
  26. +0 −42 lib/node/persist/memory/collection.js
  27. +8 −2 {src/common → lib}/types/objectid.js
  28. 0  {src/common → lib}/utils/binaryParser.js
  29. +0 −98 lib/web/abstract/collection.js
  30. +0 −20 lib/web/collection.js
  31. +0 −23 lib/web/database.js
  32. 0  lib/web/model.js
  33. +0 −25 lib/web/package.json
  34. 0  lib/web/persist/core/collection.js
  35. 0  lib/web/persist/core/database.js
  36. +0 −42 lib/web/persist/memory/collection.js
  37. +0 −4 package.json
  38. +0 −15 src/common/drivers/memory/collection.js
  39. +0 −1  src/common/drivers/memory/index.js
View
1  README.md
@@ -6,4 +6,3 @@ Inspired by [meteor](http://meteor.com)
- I wanted a decoupled library with the same level of functionality provided in [meteor](http://meteor.com)
- Cached on the client-side, and server-side. Less hits to mongodb itself.
- Allows for users to be sandboxed in their own collection (authentication).
-- Ability
View
17 docs/pseudo-schema.js
@@ -0,0 +1,17 @@
+var Profile = new Schema({
+ comments: { ref: 'Comment' },
+ email: String,
+ password: String
+});
+
+
+var Comment = new Schema({
+ profile: { ref: 'Profile' },
+ title: String,
+ message: String
+});
+
+
+
+
+
View
45 examples/memory.js
@@ -0,0 +1,45 @@
+var mdblite = require("../lib"),
+db = mdblite.db(new mdblite.drivers.Memory());
+
+var helloCollection = db.collection("hello");
+
+
+helloCollection.insert([
+{
+ name: "Craig",
+ age: 22
+},
+{
+ name: "Tim",
+ age:21
+},
+{
+ name: "John",
+ age:22
+}
+]);
+
+setTimeout(function() {
+ helloCollection.insert([
+ {
+ name: "Sarah",
+ age: 22
+ }
+ ]);
+}, 1000);
+
+var cursor = helloCollection.find({age:{$gt:21}});
+
+cursor.on("insert", function(item) {
+ console.log("new item added!");
+ console.log(item)
+});
+
+
+
+
+cursor.each(function(err, item) {
+ if(item) console.log(item);
+})
+
+
View
6 lib/abstract/abstract.js
@@ -0,0 +1,6 @@
+
+module.exports = {
+ _abstract: function() {
+ throw new Error("Not implemented");
+ }
+}
View
13 src/common/abstract/collection.js → lib/abstract/collection.js
@@ -3,7 +3,7 @@ EventEmitter = require('events').EventEmiter,
ObjectId = require('../types/objectid'),
_ = require('underscore');
-var Collection = module.exports = structr(EventEmitter, {
+var Collection = module.exports = structr(EventEmitter, require("./abstract"), {
/**
* constructor.
@@ -14,7 +14,7 @@ var Collection = module.exports = structr(EventEmitter, {
__construct: function(database, name) {
this.db = database;
this.name = name;
- }
+ },
/**
* finds more than one item in a collection
@@ -88,14 +88,5 @@ var Collection = module.exports = structr(EventEmitter, {
reset: function(items) {
this._abstract();
- },
-
-
- /**
- * throws an error
- */
-
- __abstract: function() {
- throw new Error("Not implemented");
}
});
View
4 src/common/abstract/cursor.js → lib/abstract/cursor.js
@@ -11,7 +11,7 @@ module.exports = structr({
this.selector = selector;
this.collection = collection;
this.options = options || {};
- this._sift = sift(this.selector);
+ this.sift = sift(this.selector);
this._position = 0;
},
@@ -82,7 +82,7 @@ module.exports = structr({
if(!document) return fn(err, docs);
docs.push(document);
});
- },
+ }
/**
*/
View
11 lib/abstract/db.js
@@ -0,0 +1,11 @@
+var structr = require("structr");
+
+module.exports = structr(require("./abstract"), {
+
+ /**
+ */
+
+ connect: function() {
+ this._abstract();
+ }
+});
View
20 src/common/chainable.js → lib/chainable.js
@@ -14,9 +14,9 @@ module.exports = structr({
*/
"__construct": function() {
- this._queue = tq.queue();
this._isReady = false;
- this._em = new EventEmitter();
+ this._queue = tq.queue().start();
+ this._em = new EventEmitter();
},
/**
@@ -27,6 +27,13 @@ module.exports = structr({
},
/**
+ */
+
+ "emit": function(type, value) {
+ this._em.emit(type, value);
+ },
+
+ /**
* pushes a callback to the queue
*/
@@ -39,12 +46,15 @@ module.exports = structr({
*/
"target": function(value) {
+
if(!arguments.length) return this._target;
+
if(!this._target) {
this._target = value;
this._em.emit("ready");
this._em.removeAllListeners("ready");
}
+
return value;
},
@@ -65,11 +75,13 @@ module.exports = structr({
*/
"_wrap": function() {
+
var args = arguments, self = this;
+
return function() {
var i = 0;
- for(; n = args.length; i < n; i++) {
- args[i].apply(self, arguments);
+ for(n = args.length; i < n; i++) {
+ (args[i] || function(){}).apply(self, arguments);
}
}
}
View
45 src/common/collection.js → lib/collection.js
@@ -1,4 +1,6 @@
-var Cursor = require('./cursor');
+var Cursor = require('./cursor'),
+_ = require("underscore"),
+ObjectId = require("./types/objectid");
/**
* wrapper for the drivers
@@ -24,13 +26,22 @@ var Cursor = require('./cursor');
//ready? get the collection
this.next(function() {
var next = this;
- db.target.collection(name, function(target) {
+ db.target().collection(name, function(err, target) {
self.target(target);
next();
- })
+ });
});
},
+
+ /**
+ */
+
+ "modelClass": function(value) {
+ if(!arguments.length) return this._modelClass;
+ return this._modelClass = value;
+ },
+
/**
*/
@@ -66,27 +77,46 @@ var Cursor = require('./cursor');
"insert": function(item, next) {
+
var items = item instanceof Array ? item : [item],
self = this;
this.next(function() {
- self.collection.insert(items, self._wrap(next || function(){}, this));
+
+ _.each(items, function(item) {
+ if(!item._id) item._id = new ObjectId();
+ })
+
+ self.target().insert(items, self._wrap(next || function(){}, this, function(err, items) {
+ if(err) return;
+ self.emit("insert", items);
+ }));
});
},
/**
*/
+ "ensureIndex": function(key) {
+ this.next(function() {
+ self.target().ensureIndex(key);
+ })
+ },
+
+ /**
+ */
+
"update": function(selector, toUpdate, options, next) {
+
var self = this;
if(typeof options == 'function') {
- next = options;
+ next = options;
options = undefined;
}
this.next(function() {
- self.collection.update(selector, toUpdate, options || {}, self._wrap(next || function(){}, this))
+ self.target().update(selector, toUpdate, options || {}, self._wrap(next || function(){}, this))
});
},
@@ -94,9 +124,10 @@ var Cursor = require('./cursor');
*/
"remove": function(selector, next) {
+
var self = this;
this.next(function() {
- self.colletion.remove(selector, self._wrap(next || function(){}, next));
+ self.target().remove(selector, self._wrap(next || function(){}, next));
});
}
View
27 src/common/cursor.js → lib/cursor.js
@@ -1,5 +1,6 @@
var tq = require('tq'),
-Chainable = require('./chainable');
+Chainable = require('./chainable'),
+Observer = require("./cursorObserver");
module.exports = require("./abstract/cursor").extend({
@@ -14,7 +15,7 @@ module.exports = require("./abstract/cursor").extend({
this._chain.next(function() {
collection.ready(this);
- })
+ });
},
/**
@@ -29,15 +30,30 @@ module.exports = require("./abstract/cursor").extend({
*/
"target": function() {
- return this._chain.apply(this._chain, arguments);
+ return this._chain.target.apply(this._chain, arguments);
+ },
+
+
+
+ /**
+ * listens for insert / update / remove
+ */
+
+ on: function(type, callback) {
+ if(!this._em) {
+ this._observer = new Observer(this);
+ }
+ this._observer.on(type, callback);
},
+
/**
*/
"nextObject": function(fn) {
var self = this;
+
this._cursor(function(cursor) {
cursor.nextObject(function(err, item) {
if(!item) return fn(err);
@@ -56,8 +72,8 @@ module.exports = require("./abstract/cursor").extend({
}
this._waiting = true;
-
var self = this;
+
this._chain.next(function() {
var next = this;
self.collection.target().find(self.selector, self.options, function(err, cursor) {
@@ -67,4 +83,5 @@ module.exports = require("./abstract/cursor").extend({
});
});
}
-})
+});
+
View
44 lib/cursorObserver.js
@@ -0,0 +1,44 @@
+var structr = require('structr'),
+_ = require('underscore'),
+EventEmitter = require('events').EventEmitter;
+
+module.exports = structr(EventEmitter, {
+
+ /**
+ */
+
+ __construct: function(cursor) {
+ this.cursor = cursor;
+ this.sift = cursor.sift;
+ this.collection = cursor.collection;
+
+ this.listen();
+ },
+
+ /**
+ */
+
+ listen: function() {
+
+ var sift = this.sift, em = this, col = this.collection;
+
+
+ ["insert", "update", "remove"].forEach(function(type) {
+
+ col.on(type, function(items) {
+ var usable = sift(items);
+
+ if(!items.length) return;
+
+ em.emit(type, usable);
+
+
+ em.emit('change', {
+ type: type,
+ items: usable
+ })
+ })
+ });
+ }
+
+})
View
6 src/common/database.js → lib/database.js
@@ -17,6 +17,8 @@ var Database = module.exports = require("./chainable").extend({
this._super();
this._collections = {};
this._driver = driver;
+
+ this.connect();
},
/**
@@ -33,8 +35,8 @@ var Database = module.exports = require("./chainable").extend({
var self = this;
this.next(function() {
var next = this;
- self._driver.connect(self._wrap(fn, function(err, target) {
- if(target) self.target(target);
+ self._driver.connect(self._wrap(fn, function(err) {
+ if(!err) self.target(self._driver);
next();
}));
});
View
0  src/common/drivers/core/collection.js → lib/drivers/core/collection.js
File renamed without changes
View
0  src/common/drivers/core/database.js → lib/drivers/core/database.js
File renamed without changes
View
84 lib/drivers/memory/collection.js
@@ -0,0 +1,84 @@
+var Cursor = require("./cursor");
+
+
+var Collection = module.exports = require('../../abstract/collection').extend({
+
+ /**
+ */
+
+ "override __construct": function() {
+ this._super.apply(null, arguments);
+ this.source = [];
+
+ //indexed items via ensureIndex
+ this._dictionary = {};
+
+ //indexes to ensure
+ this._indexes = {};
+
+ this.ensureIndex("_id");
+ },
+
+
+ /**
+ */
+
+
+ find: function(selector, options, next) {
+ next(null, new Cursor(this, selector, options));
+ },
+
+ /**
+ */
+
+ insert: function(items, onInsert) {
+ for(var i = 0, n = items.length; i < n; i++) {
+ try {
+ this._insertItem(items[i]);
+ } catch(e) {
+ return onInsert(e);
+ }
+ }
+
+ onInsert(null, items);
+ },
+
+
+ /**
+ */
+
+ ensureIndex: function(key) {
+ this._indexes[key] = 1;
+ this._dictionary[key] = this._dictionary[key] || {};
+ },
+
+
+ /**
+ */
+
+ _insertItem: function(item) {
+ this._indexItem(item);
+ this.source.push(item);
+ },
+
+
+
+ /**
+ * validates the collection to make sure there are no overlapping items
+ */
+
+
+ _indexItem: function(item) {
+
+ //validate
+ for(var key in this._dictionary) {
+ if(this._dictionary[key][item[key]]) throw new Error(key+" "+item[key]+" already exists");
+ }
+
+ //insert
+ for(var key in this._dictionary) {
+ this._dictionary[key][item[key]] = item;
+ }
+ }
+
+});
View
8 src/common/drivers/memory/cursor.js → lib/drivers/memory/cursor.js
@@ -1,7 +1,7 @@
var structr = require('structr'),
sift = require('sift');
-var Cursor = require("../../abstract/cursor").extend({
+var Cursor = module.exports = require("../../abstract/cursor").extend({
/**
@@ -25,7 +25,7 @@ var Cursor = require("../../abstract/cursor").extend({
//TODO - async chunk
if(this._sifted) return fn(null, this._sifted);
- sifted = this._sort(this._sift(this.collection.target));
+ sifted = this._sort(this.sift(this.collection.source));
if(this.options.limit && sifted.length > this._options.limit) {
sifted = sifted.splice(0, this.options.limit);
@@ -43,7 +43,7 @@ var Cursor = require("../../abstract/cursor").extend({
var self = this;
return stack.sort(function(a, b) {
- return self._sortScore(a) < self._sortScore(b) : -1 : 1;
+ return self._sortScore(a) < self._sortScore(b) ? -1 : 1;
});
},
@@ -52,7 +52,7 @@ var Cursor = require("../../abstract/cursor").extend({
*/
_sortScore: function(item) {
- var field, desc, sort = this._options.sort, score = 0;
+ var field, desc, sort = this.options.sort, score = 0;
for(field in sort) {
score += Number((sort[field] == 'desc') || (sort[field] == -1));
View
24 lib/drivers/memory/index.js
@@ -0,0 +1,24 @@
+var structr = require('structr'),
+Collection = require("./collection");
+
+
+module.exports = structr(require("../../abstract/db"), {
+
+ /**
+ */
+
+ connect: function(fn) {
+ //nothing to connect. carry on.
+ fn();
+ },
+
+
+ /**
+ */
+
+ collection: function(name, next) {
+ next(null, new Collection(this, name));
+ }
+
+
+});
View
0  src/common/index.js → lib/index.js
File renamed without changes
View
0  src/common/model.js → lib/model.js
File renamed without changes
View
20 lib/node/collection.js
@@ -1,20 +0,0 @@
-/**
- * the collection
- */
-
-
-var Collection = function(db, name) {
- this.name = name;
- this.persist(db.persist());
-}
-
-
-Collection.prototype.persist = function(persist) {
- this._persist = persist.collection(this.name);
-
- //find, findOne, insert, update, upsert
- for(var property in persist) {
- this[property] = persist[property];
- }
-}
-
View
23 lib/node/database.js
@@ -1,23 +0,0 @@
-var Collection = require('./collection');
-
-var Database = function() {
-
-}
-
-/**
- * persistence (mongodb / fs / memory)
- */
-
-Database.prototype.persist = function(persist) {
- if(!arguments.length) return this._persist;
- this._persist = persist;
-}
-
-/**
- * returns a new collection
- */
-
-Database.prototype.collection = function(name) {
- return new Collection(this, name);
-}
-
View
0  lib/node/model.js
No changes.
View
24 lib/node/package.json
@@ -1,24 +0,0 @@
-{
- "author": "Craig Condon",
- "name": "mongodblite",
- "description": "self-contained mongodb implementation",
- "version": "0.0.0",
- "repository": {
- "type": "git",
- "url": "git://github.com/crcn/node-mongodblite.git"
- },
- "main": "./lib/index.js",
- "engines": {
- "node": "~0.6.14"
- },
- "directories": {
- "src": "./src",
- "lib": "./lib"
- },
- "dependencies": {
- "sift": "*",
- "structr": "*"
- },
- "devDependencies": {},
- "mesh": true
-}
View
0  lib/node/persist/core/collection.js
No changes.
View
0  lib/node/persist/core/database.js
No changes.
View
42 lib/node/persist/memory/collection.js
@@ -1,42 +0,0 @@
-var structr = require('structr');
-
-var Collection = function(name) {
- this.name = name;
-}
-
-/**
- * finds more than one item
- */
-
-Collection.prototype.find = function(selector, options, next) {
-
-};
-
-
-/**
- * finds one item
- */
-
-Collection.prototype.findOne = function(selector, options, next) {
-
-};
-
-
-/**
- * inserts one, or more items
- */
-
-Collection.prototype.insert = function(items, next) {
-
-}
-
-
-/**
- * updates one, or more items
- */
-
- Collection.prototype.update = function(selector, toUpdate, options, next) {
-
- }
-
-
View
10 src/common/types/objectid.js → lib/types/objectid.js
@@ -20,6 +20,10 @@ var ObjectId = module.exports = structr({
toString: function() {
return this._value;
+ },
+
+ toJSON: function() {
+ return this._value;
}
});
@@ -27,6 +31,7 @@ var ObjectId = module.exports = structr({
var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10),
PID = typeof process != 'undefined' ? process.pid : parseInt(Math.random() * 0xFFFFFF, 8);
+inc = 0;
/**
@@ -38,7 +43,7 @@ function generateId() {
time4Bytes = BinaryParser.encodeInt(unixTime, 32, true, true),
machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false),
pid2Bytes = BinaryParser.fromShort(PID),
- index3Bytes = BinaryParser.encodeInt(this.inc(), 24, false, true);
+ index3Bytes = BinaryParser.encodeInt(inc++, 24, false, true);
return toHexString(time4Bytes + machine3Bytes + pid2Bytes + index3Bytes);
}
@@ -47,7 +52,7 @@ function generateId() {
*/
-function hexToString() {
+function toHexString(id) {
var hexString = '', number, value;
for (var index = 0, len = id.length; index < len; index++)
@@ -59,6 +64,7 @@ function hexToString() {
hexString = hexString + number;
}
+
return hexString;
}
View
0  src/common/utils/binaryParser.js → lib/utils/binaryParser.js
File renamed without changes
View
98 lib/web/abstract/collection.js
@@ -1,98 +0,0 @@
-var structr = require('structr'),
-EventEmitter = require('events').EventEmiter;
-
-var Collection = module.exports = structr(EventEmitter, {
-
- /**
- * constructor.
- * @param db the database
- * @param name name of the collection
- */
-
- __construct: function(database, name) {
- this.db = database;
- this.name = name;
- }
-
- /**
- * finds more than one item in a collection
- * @param selector the search term
- * @param options additional options such as sorting
- * @param onCursor called when a cursor is ready. Omitting it returns a chained collection
- */
-
-
- find: function(selector, options, onCursor) {
- this._abstract();
- },
-
-
- /**
- * finds one item
- * @see find
- */
-
- findOne: function(selector, options, onItem) {
- this._abstract();
- },
-
- /**
- * inserts one or more updates
- * @param items the items to insert
- * @param called when an item is inserted
- */
-
- insert: function(items, onInsert) {
- this.emit('insert', {
- items: items
- });
- },
-
- /**
- * updates one or more items
- * @param selector the query for searching against items
- * @param modify items to update: $set, $inc, etc.
- * @param options options, such as upsert, and multi
- * @param onUpdate called on update
- */
-
- update: function(selector, modify, options, onUpdate) {
- this.emit('update', {
- selector: selector,
- modify: modify,
- options: options
- });
- },
-
- /**
- * removes one or more items
- * @param selector the query to match against items to remove
- * @param options options for removal, such as max items to remove
- * @param onRemove called once items have been removed
- */
-
- remove: function(selector, options, onRemove) {
- this.emit('remove', {
- selector: selector,
- options: options
- });
- },
-
-
- /**
- * drops the collection
- */
-
- drop: function() {
- this._abstract();
- },
-
-
- /**
- * throws an error
- */
-
- __abstract: function() {
- throw new Error("Not implemented");
- }
-});
View
20 lib/web/collection.js
@@ -1,20 +0,0 @@
-/**
- * the collection
- */
-
-
-var Collection = function(db, name) {
- this.name = name;
- this.persist(db.persist());
-}
-
-
-Collection.prototype.persist = function(persist) {
- this._persist = persist.collection(this.name);
-
- //find, findOne, insert, update, upsert
- for(var property in persist) {
- this[property] = persist[property];
- }
-}
-
View
23 lib/web/database.js
@@ -1,23 +0,0 @@
-var Collection = require('./collection');
-
-var Database = function() {
-
-}
-
-/**
- * persistence (mongodb / fs / memory)
- */
-
-Database.prototype.persist = function(persist) {
- if(!arguments.length) return this._persist;
- this._persist = persist;
-}
-
-/**
- * returns a new collection
- */
-
-Database.prototype.collection = function(name) {
- return new Collection(this, name);
-}
-
View
0  lib/web/model.js
No changes.
View
25 lib/web/package.json
@@ -1,25 +0,0 @@
-{
- "author": "Craig Condon",
- "name": "mongodblite",
- "description": "self-contained mongodb implementation",
- "version": "0.0.0",
- "repository": {
- "type": "git",
- "url": "git://github.com/crcn/node-mongodblite.git"
- },
- "main": "./lib/index.js",
- "engines": {
- "node": "~0.6.14"
- },
- "directories": {
- "src": "./src",
- "lib": "./lib"
- },
- "dependencies": {
- "sift": "*",
- "structr": "*",
- "underscore": "1.2.x"
- },
- "devDependencies": {},
- "mesh": true
-}
View
0  lib/web/persist/core/collection.js
No changes.
View
0  lib/web/persist/core/database.js
No changes.
View
42 lib/web/persist/memory/collection.js
@@ -1,42 +0,0 @@
-var structr = require('structr');
-
-var Collection = function(name) {
- this.name = name;
-}
-
-/**
- * finds more than one item
- */
-
-Collection.prototype.find = function(selector, options, next) {
-
-};
-
-
-/**
- * finds one item
- */
-
-Collection.prototype.findOne = function(selector, options, next) {
-
-};
-
-
-/**
- * inserts one, or more items
- */
-
-Collection.prototype.insert = function(items, next) {
-
-}
-
-
-/**
- * updates one, or more items
- */
-
- Collection.prototype.update = function(selector, toUpdate, options, next) {
-
- }
-
-
View
4 package.json
@@ -11,10 +11,6 @@
"engines": {
"node": "~0.6.14"
},
- "directories": {
- "src": "./src",
- "lib": "./lib"
- },
"dependencies": {
"sift": "*",
"structr": "*",
View
15 src/common/drivers/memory/collection.js
@@ -1,15 +0,0 @@
-var AbstractCollection = require('../../abstract/collection');
-
-
-var Collection = module.exports = AbstractCollection.extend({
-
- /**
- */
-
-
- /**
- */
-
-
- find: function(selector, )
-});
View
1  src/common/drivers/memory/index.js
@@ -1 +0,0 @@
-var structr = require('structr')
Please sign in to comment.
Something went wrong with that request. Please try again.