A simple ORM for node.js in Huaban. For performance, this ORM does not provide operations like in, group by, join
and so on.
$ npm install toshihikoYou should create a Toshihiko object to connect to MySQL:
var T = require("toshihiko");
var toshihiko = new T.Toshihiko(database, username, password, options);Options can include these things:
host: hostname or IP of MySQL. Defaults tolocalhost.port: port of MySQL. Defaults to3306.memcached: if you want to memcached support, let it be anMemcachedobject which will be mentioned below. Defaults to undefined.- etc... (All options in module mysql will be OK)
If you want to have memcached supported, you should create a Memcached object and then put it into options:
var Memcached = T.Memcached;
var toshihiko = new T.Toshihiko(database, username, password, {
memcached : new Memcached(servers, options);
});Notice: the
serversandoptionsparameters can be referenced at https://www.npmjs.org/package/memcached#setting-up-the-client.And what's more, you can give a
prefixinoptionsto let your memcached for this Toshihiko has a certain prefix.Eg.
new Memcached(servers, { prefix: "tshk_", ... });
A new feature for memcached is that you can custom your memcached key generate function now!
You may pass the function at the very beginning:
new Memcached(servers, { custormizeKey: function(db, table, keys) { return ...; } });Another way is you can pass throw the function below:
memcached.setCustomizeKeyFunc(function(db, table, keys) { return ...; });You should pay attention to db, table and keys which stand for database name, table name, primary keys with their value.
keysmaybe a single value (whentypeof keys !== "object"); it maybe an object contains key-value pairskey name -> value.Eg.
{ userId: 12, boardId: 12 }
So here's an example customize function:
function(db, table, keys) {
var base = this.prefix + db + "_" + table;
if(typeof keys !== "object") return base + ":" + keys;
for(var key in keys) {
base += ":";
base += key;
base += keys[key];
}
return base;
}Define a model schema:
var Model = toshihiko.define(tableName, [
{ name: "key1", column: "key_one", primaryKey: true, type: Toshihiko.Type.Integer },
{ name: "key2", type: Toshihiko.Type.String, defaultValue: "Ha~" },
{ name: "key3", type: Toshihiko.Type.Json, defaultValue: [] },
{ name: "key4", validators: [
function(v) {
if(v > 100) return "`key4` can't be greater than 100";
},
function(v) {
// blahblah...
}
] }
], options);You can add extra model functions by yourself:
Model.sayHello = function() {
this.find(function(err, rows) {
console.log(err);
console.log(rows);
});
};
optionsis optional. You can specifyMemcachedhere if you haven't defined it inToshihiko. Otherwise, you can let it benullwhen you don't want to useMemcachedin thisModelbut you had specify it inToshihiko.
Toshihiko uses chain operations. Eg:
Model.where(condition).limit(limit).orderBy(order).find(callback);
Model.where(condition).limit(limit).delete(callback);
Model.findById(primaryKeysId, callback);
Model.where(condition).update(data, callback);condition is an JSON object with keys:
- A field name
$and$or
For field name, the value can be a certain value. Eg:
{
key1: 1
}The value can be a JSON object with comparison operators $eq / ===, $neq / !==, $gt(e) / >(=), $lt(e) / <(=), $like.
Eg:
{
keys1: {
$neq: value
}
}
valuecan be a certain value or an array with logicAND.Eg.
$neq: 5or$neq: [ 5, 2 ].
You can use logic symbols as well:
{
keys1: {
$or: {
$eq: 1,
$neq: 2
}
}
}Notice: you can define
logicandoperatorswith many many levels.
You can use these two logic with many many levels.
{
$or: {
$or: { $or: ... },
}
}And the last level can be like that:
{
$and: {
KEY: { REFER TO ABOVE `Field Name` }
}
}For examples:
foo.limit("1"); ///< skip 1
foo.limit("0,30"); ///< skip 0, limit 30
foo.limit([ 0, 30 ]); ///< skip 0, limit 30
foo.limit([ 1 ]); ///< skip 1
foo.limit({ skip: 0, limit: 1 }); ///< skip 0, limit 1
foo.limit({ skip: 1 }); ///< skip 1
foo.limit({ limit: 1 });///< limit 1For examples:
foo.orderBy("key1 asc");
foo.orderBy([ "key1 asc", "key2 desc" ]);
foo.orderBy({ key1: "asc", key2: "desc", key3: 1, key4: -1 });Count the records with a certain condition:
foo.where(condition).count(function(err, count) {});With the conditions, limit and orders to find all records:
foo.where(condition).find(function(err, rows) {
//...
}, withJson);Notice: the parameter
withJsonis an optional parameter. If it's true, elements inrowsare JSON objects. Otherwise, they are allYukariobjects.
It's similar with find, but it will just find only one record.
foo.where(condition).findOne(function(err, row) {
//...
}, withJson);Notice:
withJsonis the same as above.
foo.findById(primaryKeysId, function(err, bar) {
}, withJson);
primaryKeysIdcan be a string or an object.When there're several primary keys in one table, this value may be like:
{ key1: 1, key2: 2, }If there's only one primary key, you can just pass a string, number or some other base type value.
For examples:
foo.findById({ key1: 1, key2: 2 }, callback);
foo.findById(1, callback);foo.where(condition).update(data, function(err, result) {});data is an object that includes your changed data. Eg:
{
key1: 12,
key2: "123",
key3: "{{key3 + 1}}"
}String with {{...}} will be parsed as SQL statement. For example, you can let it be {{CONCAT(`key3`, ".suffix")}}
or any others statement you want to use.
Notice:
resultis something like:{ fieldCount: 0, affectedRows: 1, insertId: 0, serverStatus: 2, warningCount: 0, message: '(Rows matched: 1 Changed: 1 Warnings: 0', protocol41: true, changedRows: 1 }
foo.where(condition).delete(function(err, result) { /** ... */ });For find, findOne, findById, update and delete, you can use it without callback function.
Whether you used callback function or not, these function will return a ResultPromisor object. You can use it like:
var Q = foo.find();
Q.success(function(result) { /** ... */ });var Q = foo.find();
Q.error(function(err) { /** ... */ });var Q = foo.find();
Q.finished(function(err, result) { /** ... */ });Yukari object is the data entity object.
rows in Model.find(function(err, rows) {}) is an array with Yukari objects unless you use withJson parameter.
Also, you can get a new Yukari object by calling Model.build().
We assume all Yukari(s) below are created from Model.find() except Model.build().
You can pass a JSON object to this function to generate a new Yukari object:
Model.build({
key1 : 1,
key2 : 2,
key3 : "3"
});Transform Yukari object to a simple original JSON object:
var json = yukari.toJSON();
console.log(json);If your Yukari object is created from Model.build(), you should use this function to insert data to database.
var yukari = Model.build({ ... });
yukari.insert(function(err, yukari) {
//...
});Change this Yukari data to database.
yukari.update(function(err, yukari) {
//...
});Notice:
"{{..}}"operation is not supported here.
If it's a new Yukari object, it will call insert. Otherwise, it will call update.
yukari.save(function(err, yukari) {
//...
});Delete this record from database.
yukari.delete(function(err, affectedRows) {});There're 4 kind of types in Toshihiko as default.
- Type.Float
- Type.Integer
- Type.Json
- Type.String
You can code a custom field type by yourself.
Here's the template:
var Type = {};
Type.name = "type";
Type.needQuotes = false; ///< Is this type need quotes in SQL statement?
Type.restore = function(v) {
// v is a parsed value,
// you should transform
// it to the type that
// SQL can recognize
return v;
};
Type.parse = function(v) {
// v is a original value,
// you should parse it
// into your own type
return v;
};
Type.defaultValue = 0.1; ///< Default valueYou can refers to lib/fieldType/json.js to get more information.
You're welcome to pull requests!
Thanks to:
「雖然我覺得不怎麼可能有人會關注我」