Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
Reduce the number of times modelClone is called
Browse files Browse the repository at this point in the history
  • Loading branch information
cdmckay committed Sep 12, 2016
1 parent 6568b80 commit 2391ca7
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 59 deletions.
23 changes: 19 additions & 4 deletions benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,26 @@ var trees = database.createCollection('trees');
trees.put({ id: 0 });
trees.put({ id: 1 });

// Setup types

var types = database.createCollection('types');
types.put({ id: 1 });

// Setup apples

var MAX_APPLES = 100000;

var apples = database.createCollection('apples', { tree: 'trees' });
var apples = database.createCollection('apples', { tree: 'trees', type: 'types' });

console.time('put');
for (var i = 0; i < MAX_APPLES; i++) {
apples.put({ id: i, tree: { id: i % 2 } });
apples.put({ id: i, tree: { id: i % 2 }, type: { id: 1 } });
}
console.timeEnd('put');

// Benchmarks

var randomAppleId = parseInt(MAX_APPLES * Math.random());
console.log('random apple id = %d', randomAppleId);

console.time('get');
apples.get(randomAppleId);
Expand All @@ -46,5 +52,14 @@ apples.filter(function (apple) {
});
console.timeEnd('filter');

console.time('filter with include');
apples.filter(function (apple) {
return apple.tree.id === 1;
}, ['tree']);
console.timeEnd('filter with include');


console.time('filter with 2 includes');
apples.filter(function (apple) {
return apple.tree.id === 1;
}, ['tree', 'type']);
console.timeEnd('filter with 2 includes');
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "deebee.js",
"main": "deebee.js",
"version": "0.3.0",
"version": "0.4.0",
"authors": [
"Cameron McKay <cameron@process.st>"
],
Expand Down
136 changes: 84 additions & 52 deletions deebee.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,39 +83,47 @@

models.forEach(function (model) {

if (model.id === undefined || model.id === null) {
throw new Error('model requires an id: ' + JSON.stringify(model));
}

// We don't want our changes here to affect the originals, so we need to clone them
var clonedModel = self.database._modelClone(model);

// Collection relations first
Object.keys(self.relationships).forEach(function (key) {
self._putOne(clonedModel);

var collectionName = self.relationships[key];
});

var relation = clonedModel[key];
if (!relation) {
throw new Error('non-existent relation "' + key + '" on ' + JSON.stringify(clonedModel));
}
};

// Don't store references
if (!isReference(relation)) {
self.database.getCollection(collectionName).put(relation);
}
Collection.prototype._putOne = function (model) {

// As you store them, strip them down to id references
clonedModel[key] = { id: relation.id };
var self = this;

});
if (model.id === undefined || model.id === null) {
throw new Error('model requires an id: ' + JSON.stringify(model));
}

//console.log('put a model into %s: %s', self.name, JSON.stringify(model));
self.trigger('put', clonedModel);
self._modelMap.set(clonedModel.id, clonedModel);
// Collection relations first
Object.keys(self.relationships).forEach(function (key) {

var collectionName = self.relationships[key];

var relation = model[key];
if (!relation) {
throw new Error('non-existent relation "' + key + '" on ' + JSON.stringify(model));
}

// Don't store references
if (!isReference(relation)) {
self.database.getCollection(collectionName)._putOne(relation);
}

// As you store them, strip them down to id references
model[key] = { id: relation.id };

});

//console.log('put a model into %s: %s', self.name, JSON.stringify(model));
self.trigger('put', model);
self._modelMap.set(model.id, model);

};

Collection.prototype.get = function (id, includes) {
Expand All @@ -125,7 +133,8 @@
includes = includes || [];

var model = self._modelMap.get(id);
return model && self._join(model, includes);
var joinedModel = model && self._join(model, includes);
return joinedModel && self.database._modelClone(joinedModel);

};

Expand All @@ -137,19 +146,15 @@

var self = this;

if (f) {
f = isFunction(f) ? f : generatePredicate(f);
} else {
f = function () { return true; };
}
var pred = f ? generatePredicate(f) : alwaysTrue;

includes = includes || [];

var results = [];
self._modelMap.forEach(function (model) {
var joinedModel = self._join(model, includes);
if (f(joinedModel)) {
results.push(joinedModel);
if (pred(joinedModel)) {
results.push(self.database._modelClone(joinedModel));
}
});

Expand All @@ -158,31 +163,47 @@
};

Collection.prototype.count = function (f, includes) {

var self = this;

if (f) {
return this.filter(f, includes).length;

var pred = f ? generatePredicate(f) : alwaysTrue;

includes = includes || [];

var count = 0;
self._modelMap.forEach(function (model) {
var joinedModel = self._join(model, includes);
if (pred(joinedModel)) {
count++;
}
});

return count;

} else {
return this._modelMap.size;

return self._modelMap.size;

}

};

Collection.prototype.find = function (f, includes) {

var self = this;

if (f) {
f = isFunction(f) ? f : generatePredicate(f);
} else {
f = function () { return true; };
}
var pred = f ? generatePredicate(f) : alwaysTrue;

includes = includes || [];

var result;
for (var it = self._modelMap.values(), o = it.next(); !o.done; o = it.next()) {
var model = o.value;
var joinedModel = self._join(model, includes);
if (f(joinedModel)) {
result = joinedModel;
if (pred(joinedModel)) {
result = self.database._modelClone(joinedModel);
break;
}
}
Expand Down Expand Up @@ -257,7 +278,10 @@

var self = this;

var clonedModel = self.database._modelClone(model);
// Deference all relationships that might have been set in a previous join
Object.keys(self.relationships).forEach(function (key) {
model[key] = { id: model[key].id };
});

includes.forEach(function (include) {

Expand All @@ -271,33 +295,41 @@
throw new Error('could not find collection: ' + collectionName);
}

var relation = collection.get(clonedModel[key].id);
var relation = collection._modelMap.get(model[key].id);

if (!relation) {
throw new Error('could not join key: ' + key);
}

clonedModel[key] = keys.length > 1 ? collection._join(relation, keys.slice(1)) : relation;
model[key] = keys.length > 1 ? collection._join(relation, keys.slice(1)) : relation;

});

return clonedModel;
return model;

};

// Helpers

function generatePredicate(object) {
var keys = Object.keys(object);
return function (model) {
var result = true;
var len = keys.length;
for (var i = 0; i < len; i++) {
result = result && elvis(model, keys[i]) === object[keys[i]];
if (!result) break;
}
return result;
};
if (isFunction(object)) {
return object;
} else {
var keys = Object.keys(object);
return function (model) {
var result = true;
var len = keys.length;
for (var i = 0; i < len; i++) {
result = result && elvis(model, keys[i]) === object[keys[i]];
if (!result) break;
}
return result;
};
}
}

function alwaysTrue() {
return true;
}

function elvis(object, path) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "deebee.js",
"version": "0.3.0",
"version": "0.4.0",
"description": "A JavaScript in-memory database",
"main": "deebee.js",
"directories": {
Expand Down
50 changes: 49 additions & 1 deletion test/collectionSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,52 @@ describe('Collection', function () {
}).toThrowError(Error);
});

});
it('should not have un-asked for relations', function () {

var nationCollection = database.createCollection('nations');
var benderCollection = database.createCollection('benders', {
nation: nationCollection.name
});
var avatar = {
id: 42,
name: 'Korra',
nation: {
id: 1,
name: 'Water Tribe'
}
};
benderCollection.put(avatar);

var avatarWithNation = benderCollection.get(42, ['nation']);
expect(avatarWithNation.nation.name).toBe(avatar.nation.name);

var avatarWithoutNation = benderCollection.get(42);
expect(avatarWithoutNation.nation.name).toBeUndefined();

});

it('should return the proper count with no predicate', function () {

var nationCollection = database.createCollection('nations');
nationCollection.put({ id: 1, name: 'Water Tribe', floating: false });
nationCollection.put({ id: 2, name: 'Fire Nation', floating: false });
nationCollection.put({ id: 3, name: 'Earth Empire', floating: false });
nationCollection.put({ id: 4, name: 'Air Nation', floating: true });

expect(nationCollection.count()).toBe(4);

});

it('should return the proper count with no predicate', function () {

var nationCollection = database.createCollection('nations');
nationCollection.put({ id: 1, name: 'Water Tribe', floating: false });
nationCollection.put({ id: 2, name: 'Fire Nation', floating: false });
nationCollection.put({ id: 3, name: 'Earth Empire', floating: false });
nationCollection.put({ id: 4, name: 'Air Nation', floating: true });

expect(nationCollection.count({ floating: false })).toBe(3);

});

});

0 comments on commit 2391ca7

Please sign in to comment.