A simple ORM for node.js in Huaban, Souche and Sohu with ❤️. For performance, this ORM does not provide operations like , in
group by
, join
and so on. (for some reason, we have to support in
operation)
$ npm install toshihiko
You 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
.cache
: if you want to cache support, let it be an cache layer object or cache layer configuration which will be mentioned below. Defaults to undefined.- etc... (All options in module mysql will be OK)
Toshihiko now is using new cache layer! You can choose your cache layer by your self!
Pass an object to cache
of options like:
var toshihiko = new T.Toshihiko(database, username, password, {
cache: YOUR_CACHE_LAYER
});
The YOUR_CACHE_LAYER
may be an instance of Toshihiko cache layer object like toshihiko-memcacehd (you can implement a cache layer by yourself).
What's more, YOUR_CACHE_LAYER
may be a configuration object which should include name
or path
.
For an example,
var toshihiko = new T.Toshihiko(database, username, password, {
cache: {
name: "memcached",
servers: "...",
options: {}
}
});
will search for package toshihiko-memcached
and pass servers
, options
to create a toshihiko-memcached
object. By default, Toshihiko support memcached as cache layer by using package toshihiko-memcacehd.
You can get the cache object in Toshihiko by getting the variable:
var cache = toshihiko.cache;
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...
}
] },
{ name: "key5", type: Toshihiko.Type.String, allowNull: true }
], options);
You can add extra model functions by yourself:
Model.sayHello = function() {
this.find(function(err, rows) {
console.log(err);
console.log(rows);
});
};
options
is optional. You can specifycache
here if you haven't defined it inToshihiko
. Otherwise, you can let it benull
when you don't want to usecache
in thisModel
but 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
or condition
can be an array that includes object mentioned above. (New feature since v0.4.1)
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
, $in
.
Eg:
{
keys1: {
$neq: value,
$in: [ value ]
}
}
value
can be a certain value or an array with logicAND
.Eg.
$neq: 5
or$neq: [ 5, 2 ]
.
You can use logic symbols as well:
{
keys1: {
$or: {
$eq: 1,
$neq: 2
}
}
}
Notice: you can define
logic
andoperators
with 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 1
For 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
withJson
is an optional parameter. If it's true, elements inrows
are JSON objects. Otherwise, they are allYukari
objects.
It's similar with find
, but it will just find only one record.
foo.where(condition).findOne(function(err, row) {
//...
}, withJson);
Notice:
withJson
is the same as above.
foo.findById(primaryKeysId, function(err, bar) {
}, withJson);
primaryKeysId
can 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:
result
is 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) { /** ... */ });
You may use $promise
to get a BlueBird
promise object after querying.
foo.find().$promise.then(callback).catch(callback);
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.
You can pass through a JSON object to update yukari object.
var object = { id: 1, key: 2 };
yukari.updateByJson(object, function(err, yukari) {
// this is the same as:
//
// yukari.id = 1;
// yukari.key = 2;
// yukari.update(function() {});
});
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 value
You can refers to lib/fieldType/json.js to get more information.
You're welcome to make pull requests!
Thanks to:
「雖然我覺得不怎麼可能有人會關注我」