Neo4j features

This commit is contained in:
Anatoliy Chakkaev 2011-10-16 21:21:08 +04:00
parent 8e05e59933
commit ece00ceaaa
2 changed files with 159 additions and 7 deletions

View File

@ -1,7 +1,7 @@
/**
* Module dependencies
*/
var neo4j = require('neo4j');
var neo4j = require('./neo4j-lib');
exports.initialize = function initializeSchema(schema, callback) {
schema.client = new neo4j.GraphDatabase(schema.settings.url);
@ -15,9 +15,107 @@ function Neo4j(client) {
}
Neo4j.prototype.define = function defineModel(descr) {
this.mixClassMethods(descr.model, descr.properties);
this.mixInstanceMethods(descr.model.prototype, descr.properties);
this._models[descr.model.modelName] = descr;
};
Neo4j.prototype.createIndexHelper = function (class, indexName) {
var db = this.client;
var method = 'findBy' + indexName[0].toUpperCase() + indexName.substr(1);
class[method] = function (value, cb) {
db.getIndexedNode(class.modelName, indexName, value, function (err, node) {
if (err) return cb(err);
if (node) {
node.data.id = node.id;
cb(null, new class(node.data));
} else {
cb(null, null);
}
});
};
};
Neo4j.prototype.mixClassMethods = function mixClassMethods(class, properties) {
var neo = this;
Object.keys(properties).forEach(function (name) {
if (properties[name].index) {
neo.createIndexHelper(class, name);
}
});
/**
* @param from - id of object to check relation from
* @param to - id of object to check relation to
* @param type - type of relation
* @param direction - all | incoming | outgoing
* @param cb - callback (err, rel || false)
*/
class.relationshipExists = function relationshipExists(from, to, type, direction, cb) {
neo.node(from, function (err, node) {
if (err) return cb(err);
node._getRelationships(direction, type, function (err, rels) {
if (err) return cb(err);
var found = false;
if (rels && rels.forEach) {
rels.forEach(function (r) {
if (r.start.id === from && r.end.id === to) {
found = true;
}
});
}
cb(err, found);
});
});
};
class.createRelationshipTo = function createRelationshipTo(id1, id2, type, data, cb) {
var fromNode, toNode;
neo.node(id1, function (err, node) {
if (err) return cb(err);
fromNode = node;
ok();
});
neo.node(id2, function (err, node) {
if (err) return cb(err);
toNode = node;
ok();
});
function ok() {
if (fromNode && toNode) {
fromNode.createRelationshipTo(toNode, type, cleanup(data), cb);
}
}
};
class.createRelationshipFrom = function createRelationshipFrom(id1, id2, type, data, cb) {
class.createRelationshipTo(id2, id1, type, data, cb);
}
// only create relationship if it is not exists
class.ensureRelationshipTo = function (id1, id2, type, data, cb) {
class.relationshipExists(id1, id2, type, 'outgoing', function (err, exists) {
if (err) return cb(err);
if (exists) return cb(null);
class.createRelationshipTo(id1, id2, type, data, cb);
});
}
};
Neo4j.prototype.mixInstanceMethods = function mixInstanceMethods(proto) {
var neo = this;
/**
* @param obj - Object or id of object to check relation with
* @param type - type of relation
* @param cb - callback (err, rel || false)
*/
proto.isInRelationWith = function isInRelationWith(obj, type, direction, cb) {
this.constructor.relationshipExists(this.id, obj.id || obj, type, 'all', cb);
};
};
Neo4j.prototype.node = function find(id, callback) {
if (this.cache[id]) {
callback(null, this.cache[id]);
@ -35,15 +133,40 @@ Neo4j.prototype.create = function create(model, data, callback) {
node.data = cleanup(data);
node.save(function (err) {
if (err) {
return callback && callback(err);
return callback(err);
}
this.cache[node.id] = node;
node.index(model, 'id', node.id, function (err) {
callback && callback(err, node.id);
});
if (err) return callback(err);
this.updateIndexes(model, node, function (err) {
if (err) return callback(err);
callback(null, node.id);
});
}.bind(this));
}.bind(this));
};
Neo4j.prototype.updateIndexes = function updateIndexes(model, node, cb) {
var props = this._models[model].properties;
var wait = 1;
Object.keys(props).forEach(function (key) {
if (props[key].index) {
wait += 1;
node.index(model, key, node.data[key], done);
}
});
done();
var error = false;
function done(err) {
error = error || err;
if (--wait === 0) {
cb(error);
}
}
};
Neo4j.prototype.save = function save(model, data, callback) {
this.node(data.id, function (err, node) {
if (err) return callback(err);
@ -82,12 +205,13 @@ Neo4j.prototype.readFromDb = function readFromDb(model, data) {
};
Neo4j.prototype.destroy = function destroy(model, id, callback) {
var force = true;
this.node(id, function (err, node) {
if (err) return callback(err);
node.delete(function (err) {
if (err) return callback(err);
delete this.cache[id];
}.bind(this), true);
}.bind(this), force);
});
};
@ -153,7 +277,7 @@ Neo4j.prototype.updateAttributes = function updateAttributes(model, id, data, cb
};
function cleanup(data) {
if (!data) return console.log('no data!') && {};
if (!data) return null;
var res = {};
Object.keys(data).forEach(function (key) {
var v = data[key];

View File

@ -17,11 +17,14 @@ var schemas = {
memory: {}
};
var specificTest = getSpecificTests();
Object.keys(schemas).forEach(function (schemaName) {
if (process.env.ONLY && process.env.ONLY !== schemaName) return;
context(schemaName, function () {
var schema = new Schema(schemaName, schemas[schemaName]);
testOrm(schema);
if (specificTest[schemaName]) specificTest[schemaName](schema);
});
});
@ -41,7 +44,7 @@ function testOrm(schema) {
});
Post = schema.define('Post', {
title: { type: String, length: 255 },
title: { type: String, length: 255, index: true },
content: { type: Text },
date: { type: Date, default: Date.now },
published: { type: Boolean, default: false }
@ -342,3 +345,28 @@ function testOrm(schema) {
}
}
function getSpecificTests() {
var sp = {};
sp['neo4j'] = function (schema) {
it('should create methods for searching by index', function (test) {
var Post = schema.models['Post'];
test.ok(typeof Post.findByTitle === 'function');
Post.create({title: 'Catcher in the rye'}, function (err, post) {
if (err) return console.log(err);
test.ok(!post.isNewRecord());
Post.findByTitle('Catcher in the rye', function (err, foundPost) {
if (err) return console.log(err);
if (foundPost) {
test.equal(post.id, foundPost.id);
test.done();
}
});
});
});
};
return sp;
}