fibjs のオブジェクト リレーショナル マッピング
インストール
1npm install fib-orm
テスト
1npm run ci
DBMSのサポート
- MySQL と MariaDB
- SQLite
特徴
fib-orm は、node-orm オブジェクトに同期バージョン メソッドのセットを追加します。
導入
これは fibjs オブジェクト リレーショナル マッピング モジュールです。
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37var orm = require("fib-orm");
var db = orm.connectSync("mysql://username:password@host/database");
var Person = db.define("person", {
name : String,
surname : String,
age : Number, // FLOAT
male : Boolean,
continent : [ "Europe", "America", "Asia", "Africa", "Australia", "Antartica" ], // ENUM type
photo : Buffer, // BLOB/BINARY
data : Object // JSON encoded
}, {
methods: {
fullName: function () {
return this.name + ' ' + this.surname;
}
},
validations: {
age: orm.enforce.ranges.number(18, undefined, "under-age")
}
});
// add the table to the database
db.syncSync();
// add a row to the person table
Person.createSync({ id: 1, name: "John", surname: "Doe", age: 27 });
// query the person table by surname
var people = Person.findSync({ surname: "Doe" });
// SQL: "SELECT * FROM person WHERE surname = 'Doe'"
console.log("People found: %d", people.length);
console.log("First person: %s, age %d", people[0].fullName(), people[0].age);
people[0].age = 16;
people[0].saveSync();
Node.js のバージョンは次のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49var orm = require("orm");
orm.connect("mysql://username:password@host/database", function (err, db) {
if (err) throw err;
var Person = db.define("person", {
name : String,
surname : String,
age : Number, // FLOAT
male : Boolean,
continent : [ "Europe", "America", "Asia", "Africa", "Australia", "Antartica" ], // ENUM type
photo : Buffer, // BLOB/BINARY
data : Object // JSON encoded
}, {
methods: {
fullName: function () {
return this.name + ' ' + this.surname;
}
},
validations: {
age: orm.enforce.ranges.number(18, undefined, "under-age")
}
});
// add the table to the database
db.sync(function(err) {
if (err) throw err;
// add a row to the person table
Person.create({ id: 1, name: "John", surname: "Doe", age: 27 }, function(err) {
if (err) throw err;
// query the person table by surname
Person.find({ surname: "Doe" }, function (err, people) {
// SQL: "SELECT * FROM person WHERE surname = 'Doe'"
if (err) throw err;
console.log("People found: %d", people.length);
console.log("First person: %s, age %d", people[0].fullName(), people[0].age);
people[0].age = 16;
people[0].save(function (err) {
// err.msg = "under-age";
});
});
});
});
});
ドキュメンテーション
Fibjs は新しい機能を追加せず、ドキュメントの開発はノード orm を参照でき、非同期呼び出しを同期バージョンに変更するだけで済み ます。
設定
Wikiの情報を参照してください。
接続中
Wikiの情報を参照してください。
モデル
モデルは 1 つ以上のデータベース テーブルを抽象化したものです。モデルは関連付けをサポートしています (詳細は後述)。モデルの名前はテーブル名と一致すると想定されます。
モデルは、テーブル データにアクセスして操作するための動作をサポートします。
モデルの定義
Wikiの情報を参照してください。
Properties
Wikiの情報を参照してください。
Instance Methods
モデル定義中に渡されます。
1
2
3
4
5
6
7
8
9
10
11
12
13var Person = db.define('person', {
name : String,
surname : String
}, {
methods: {
fullName: function () {
return this.name + ' ' + this.surname;
}
}
});
var person = Person.getSync(4);
console.log( person.fullName() );
Model Methods
モデル上で直接定義されます。
1
2
3
4
5
6
7
8
9var Person = db.define('person', {
name : String,
height : { type: 'integer' }
});
Person.tallerThan = function(height) {
return this.findSync({ height: orm.gt(height) });
};
var tallPeople = Person.tallerThan( 192);
モデルのロード [サポートされていません]
モデルは別個のモジュールに含めることができます。モデルを保持するモジュールが module.exports を使用してデータベース接続を受け入れる関数を公開していることを確認し、好きなようにモデルをロードします。
注 - この手法を使用すると、負荷をカスケードすることができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// your main file (after connecting)
db.loadSync("./models");
// loaded!
var Person = db.models.person;
var Pet = db.models.pet;
// models.js
module.exports = function (db) {
db.loadSync("./models-extra");
db.define('person', {
name : String
});
};
// models-extra.js
module.exports = function (db) {
db.define('pet', {
name : String
});
};
モデルの同期
Wikiの情報を参照してください。
モデルの削除
Wikiの情報を参照してください。
高度なオプション
define
ORM2 を使用すると、モデル定義で高度な調整を行うことができ、これらは設定を通じて、またはモデルをセットアップするときの呼び出しで構成できます。
たとえば、各 Model インスタンスにはデータベース内で一意の ID があります。このテーブル列は自動的に追加され、デフォルトでは「id」と呼ばれます。
独自のkey: true
列を定義する場合、「id」は追加されません。
1
2
3
4
5
6
7
8
9
10
11
12var Person = db.define("person", {
personId : { type: 'serial', key: true },
name : String
});
// You can also change the default "id" property name globally:
db.settings.set("properties.primary_key", "UID");
// ..and then define your Models
var Pet = db.define("pet", {
name : String
});
ペットUID
モデルには、 との2 つの列がありますname
。
複合キーを使用することも可能です。
1
2
3
4var Person = db.define("person", {
firstname : { type: 'text', key: true },
lastname : { type: 'text', key: true }
});
その他のオプション:
identityCache
: (デフォルト: ) ID キャッシュ ( Singletonsfalse
) を有効にするように設定するか、タイムアウト値 (秒単位) を設定します。true
autoSave
: (デフォルト:false
)true
プロパティを変更した直後にインスタンスを保存するように設定します。autoFetch
: (デフォルト:false
)true
データベースからインスタンスをフェッチするときに関連付けをフェッチするように設定します。autoFetchLimit
: (デフォルト:1
)autoFetch
が有効な場合、自動的にフェッチするフープ (関連付けの関連付け) の数を定義します。
フック
Wikiの情報を参照してください。
アイテムを見つけます
Model.getSync(id, [ options ])
データベースから特定の要素を取得するには、 を使用しますModel.get
。
1
2var person = Person.getSync(123);
// finds person with id = 123
Model.findSync([ conditions ] [, options ] [, limit ] [, order ])
1 つまたは複数の要素を検索するには、さらに多くのオプションがあり、それぞれの要素を特定のパラメーターの順序で指定することはできません(空のオブジェクトであっても)options
後でなければなりませんconditions
。
1
2var people = Person.findSync({ name: "John", surname: "Doe" }, 3);
// finds people with name='John' AND surname='Doe' and returns the first 3
制限しているため、または単に並べ替えたいという理由で結果を並べ替える必要がある場合は、次のようにします。
1
2
3
4
5
6var people = Person.findSync({ surname: "Doe" }, "name");
// finds people with surname='Doe' and returns sorted by name ascending
people = Person.findSync({ surname: "Doe" }, [ "name", "Z" ]);
// finds people with surname='Doe' and returns sorted by name descending
// ('Z' means DESC; 'A' means ASC - default)
何かを見つけるために渡すことができるオプションは他にもあります。これらのオプションは 2 番目のオブジェクトで渡されます。
1
2var people = Person.findSync({ surname: "Doe" }, { offset: 2 });
// finds people with surname='Doe', skips the first 2 and returns the others
検索時に生の SQL を使用することもできます。これについては、以下の「チェーン」セクションに記載されています。
Model.countSync([ conditions])
条件に一致するアイテムの数を数えたいだけの場合は、.count()
すべてのアイテムを見つけてカウントするのではなく、単に使用できます。これにより、実際にはデータベース サーバーにカウントを実行するように指示されます (ノード プロセス自体では実行されません)。 )。
1
2var count = Person.countSync({ surname: "Doe" });
console.log("We have %d Does in our db", count);
Model.existsSync([ conditions])
と同様に.count()
、このメソッドはカウントが 0 より大きいかどうかをチェックするだけです。
1
2var exists = Person.existsSync({ surname: "Doe" });
console.log("We %s Does in our db", exists ? "have" : "don't have");
Aggregating Functions
プロパティのArray
を渡すと、少数のプロパティのみを選択できます。また、Object
条件を定義するために を渡すこともできます。
の使用方法を示す例を次に示します.groupBy()
。
1
2
3//The same as "select avg(weight), age from person where country='someCountry' group by age;"
var stats = Person.aggregate(["age"], { country: "someCountry" }).avg("weight").groupBy("age").getSync();
// stats is an Array, each item should have 'age' and 'avg_weight'
基本.aggregate()
メソッド
.limit()
: 数値をリミットとして渡すことも、2 つの数値をそれぞれオフセットとリミットとして渡すこともできます.order()
: と同じModel.find().order()
追加の.aggregate()
メソッド
min
max
avg
sum
count
(これへのショートカットがあります -Model.count
)
ドライバーによっては、さらに多くの集計関数があります (数学関数など)。
Chaining
.find()
あまり複雑でない構文を希望する場合は、コールバック パラメーターを指定しないことによってチェーンすることができます。
1
2
3var people = Person.find({ surname: "Doe" }).limit(3).offset(2).only("name", "surname").runSync();
// finds people with surname='Doe', skips first 2 and limits to 3 elements,
// returning only 'name' and 'surname' properties
1 つまたは 2 つのプロパティだけをスキップしたい場合は、 の.omit()
代わりに を呼び出すことができます.only
。
チェーンを使用すると、より複雑なクエリが可能になります。たとえば、カスタム SQL を指定して検索できます。
1Person.find({ age: 18 }).where("LOWER(surname) LIKE ?", ['dea%']).allSync( ... );
SQL パラメータを手動でエスケープすることはエラーが発生しやすく、アプリケーションが SQL インジェクションの危険にさらされるため、推奨されません。クエリ内の疑問符を指定されたパラメータで安全に置き換えることにより、構文がエスケープを処理します。複数の句を次のように?
連結することもできます。where
必要です。
.find
、.where
& は.all
同じことを行います。これらはすべて交換可能であり、連鎖可能です。
order
または次のこともできますorderRaw
。
1
2
3Person.find({ age: 18 }).order('-name').allSync( ... );
// see the 'Raw queries' section below for more details
Person.find({ age: 18 }).orderRaw("?? DESC", ['age']).allSync( ... );
チェーンして最後にカウントだけを取得することもできますが、この場合、オフセット、リミット、順序は無視されます。
1
2var people = Person.find({ surname: "Doe" }).countSync();
// people = number of people with surname="Doe"
選択した項目を削除するオプションも利用できます。連鎖削除ではフックは実行されないことに注意してください。
1
2Person.find({ surname: "Doe" }).removeSync();
// Does gone..
一般的な配列走査メソッドを使用してインスタンスを変更し、最終的にすべてを保存することもできます。
1
2
3
4
5
6
7
8
9
10
11
12
13Person.find({ surname: "Doe" }).each(function (person) {
person.surname = "Dean";
}).save(function (err) {
// done!
});
Person.find({ surname: "Doe" }).each().filter(function (person) {
return person.age >= 18;
}).sort(function (person1, person2) {
return person1.age < person2.age;
}).get(function (people) {
// get all people with at least 18 years, sorted by age
});
もちろん、これを で直接実行することもできます.find()
が、より複雑なタスクの場合、これは非常に便利です。
Model.find()
は配列を返さないため、直接チェーンすることはできません。チェーンを開始するには、 (リストをトラバースする場合はオプションのコールバックを使用して) を呼び出す必要があります
。その後、共通関数、、 を複数回.each()
使用できます
。.filter()
.sort()
.forEach()
最後に (またはプロセス中...) 次のように呼び出すことができます。
.countSync()
アイテムの数を知りたいだけの場合。.getSync()
リストを取得します。.saveSync()
すべての項目の変更を保存します。
条件
条件は、すべてのキーがプロパティ (テーブル列) であるオブジェクトとして定義されます。すべてのキーは論理 によって連結されることになっていますAND
。値は、 を渡さない限り、正確に一致するとみなされますArray
。この場合、それは考慮されますプロパティを比較するリスト。
1
2{ col1: 123, col2: "foo" } // `col1` = 123 AND `col2` = 'foo'
{ col1: [ 1, 3, 5 ] } // `col1` IN (1, 3, 5)
他の比較が必要な場合は、いくつかのヘルパー関数によって作成された特別なオブジェクトを使用する必要があります。これを説明するための例をいくつか示します。
1
2
3
4
5
6
7
8
9
10
11{ col1: orm.eq(123) } // `col1` = 123 (default)
{ col1: orm.ne(123) } // `col1` <> 123
{ col1: orm.gt(123) } // `col1` > 123
{ col1: orm.gte(123) } // `col1` >= 123
{ col1: orm.lt(123) } // `col1` < 123
{ col1: orm.lte(123) } // `col1` <= 123
{ col1: orm.between(123, 456) } // `col1` BETWEEN 123 AND 456
{ col1: orm.not_between(123, 456) } // `col1` NOT BETWEEN 123 AND 456
{ col1: orm.like(12 + "%") } // `col1` LIKE '12%'
{ col1: orm.not_like(12 + "%") } // `col1` NOT LIKE '12%'
{ col1: orm.not_in([1, 4, 8]) } // `col1` NOT IN (1, 4, 8)
生のクエリ
1
2
3
4
5
6
7
8
9
10
11
12
13var data = db.driver.execQuerySync("SELECT id, email FROM user")
// You can escape identifiers and values.
// For identifier substitution use: ??
// For value substitution use: ?
var data = db.driver.execQuerySync(
"SELECT user.??, user.?? FROM user WHERE user.?? LIKE ? AND user.?? > ?",
['id', 'name', 'name', 'john', 'id', 55])
// Identifiers don't need to be scaped most of the time
var data = db.driver.execQuerySync(
"SELECT user.id, user.name FROM user WHERE user.name LIKE ? AND user.id > ?",
['john', 55])
Identity pattern
ID パターンを使用できます (デフォルトではオフになっています)。有効にすると、複数の異なるクエリが同じ結果になり、同じオブジェクトが得られます。データベースを変更できる他のシステムがある場合、またはマニュアルを呼び出す必要がある場合は、 SQL クエリの場合、この機能は使用しないでください。また、複雑な自動フェッチ関係で問題が発生することも知られています。ご自身の責任で使用してください。
モデルごとに有効/無効にできます。
1
2
3
4
5var Person = db.define('person', {
name : String
}, {
identityCache : true
});
そして世界的にも:
1
2var db = orm.connectSync('...');
db.settings.set('instance.identityCache', true);
ID キャッシュは、ブール値の代わりに数値を渡すことで、一定期間後に期限切れになるように構成できます。この数値は、秒単位のキャッシュ タイムアウトとみなされます (浮動小数点を使用できます)。
注: キャッシュに関する 1 つの例外は、インスタンスが保存されていない場合は使用されないことです。たとえば、人物をフェッチして変更した場合、保存されていない間はキャッシュから渡されません。
アイテムの作成
Model.createSync(items)
新しい要素をデータベースに挿入するには、 を使用しますModel.create
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var items = Person.createSync([
{
name: "John",
surname: "Doe",
age: 25,
male: true
},
{
name: "Liza",
surname: "Kollan",
age: 19,
male: false
}
]);
// items - array of inserted items
アイテムの更新
返されるすべての項目には、モデルに定義されたプロパティと、各項目を変更するために使用できるいくつかのメソッドがあります。
1
2
3
4
5var John = Person.getSync(1);
John.name = "Joe";
John.surname = "Doe";
John.saveSync();
console.log("saved!");
インスタンスの更新と保存は 1 回の呼び出しで実行できます。
1
2
3var John = Person.getSync(1);
John.saveSync({ name: "Joe", surname: "Doe" });
console.log("saved!");
インスタンスを削除したい場合は、次のようにします。
1
2
3
4// you could do this without even fetching it, look at Chaining section above
var John = Person.getSync(1);
John.removeSync();
console.log("removed!");
検証
Wikiの情報を参照してください。
協会
アソシエーションは、1 つ以上のテーブル間の関係です。
hasOne
多対 1 の関係です。所属するのと同じです。
例: Animal.hasOne('owner', Person)
。Animal
は所有者を 1 人だけ持つことができますが、person は複数の動物を持つことができます。Animal にはプロパティが自動的に追加
されますowner_id
。
以下の機能が利用可能になります。
1
2
3
4animal.getOwnerSync() // Gets owner
animal.setOwnerSync(person) // Sets owner_id
animal.hasOwnerSync() // Checks if owner exists
animal.removeOwnerSync() // Sets owner_id to 0
チェーン検索
hasOne 関連付けもチェーン検索と互換性があるため、上記の例を使用すると、ChainFind オブジェクトの新しいインスタンスにアクセスできます。
1Animal.findByOwner({ /* options */ })
逆アクセス
1Animal.hasOne('owner', Person, {reverse: 'pets'})
以下を追加します:
1
2
3
4
5
6// Instance methods
person.getPetsSync(function..)
person.setPetsSync(cat, function..)
// Model methods
Person.findByPets({ /* options */ }) // returns ChainFind object
hasMany
多対多の関係です(結合テーブルを含む)。
例: Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true })
。
患者にはさまざまな医師が対応できます。各医師はさまざまな患者を担当できます。
patient_doctors
これにより、以下を呼び出すと結合テーブルが作成されますPatient.sync()
。
列名 | タイプ |
---|---|
患者ID | 整数(複合キー) |
医師ID | 整数(複合キー) |
なぜ | varchar(255) |
次の機能が利用可能になります。
1
2
3
4
5
6
7
8
9
10
11
12patient.getDoctorsSync() // List of doctors
patient.addDoctorsSync(docs) // Adds entries to join table
patient.setDoctorsSync(docs) // Removes existing entries in join table, adds new ones
patient.hasDoctorsSync(docs) // Checks if patient is associated to specified doctors
patient.removeDoctorsSync(docs) // Removes specified doctors from join table
doctor.getPatientsSync()
etc...
// You can also do:
patient.doctors = [doc1, doc2];
patient.saveSync()
医師を患者に関連付けるには:
1patient.addDoctorSync(surgeon, {why: "remove appendix"})
{patient_id: 4, doctor_id: 6, why: "remove appendix"}
これは結合テーブルに追加されます。
getAccessor
このタイプの関連付けのこのアクセサーは、ChainFind
コールバックを渡さない場合に を返します。これは、次のようなことができることを意味します。
1
2var doctors = patient.getDoctors().order("name").offset(1).runSync());
// ... all doctors, ordered by name, excluding first one
extendsTo
オプションのプロパティを別のテーブルまたはコレクションに分割したい場合は、すべての拡張機能が新しいテーブルに含まれ、各行の一意の識別子がメイン モデル インスタンス ID になります。例:
1
2
3
4
5
6
7var Person = db.define("person", {
name : String
});
var PersonAddress = Person.extendsTo("address", {
street : String,
number : Number
});
person
これにより、列id
と を含むテーブルが作成されますname
。拡張機能はperson_address
列とを含むテーブルを作成しますperson_id
。モデルで使用できるメソッドは関連付けに似ています。この例では、、 、 ..
を呼び出すことができます。street
number
Person
hasOne
.getAddress(cb)
.setAddress(Address, cb)
注:からの結果を保存する必要はありませんPerson.extendsTo
。拡張モデルが返されます。これを使用して、この拡張テーブルを直接クエリすることもできます (関連モデルを見つけることもできます)。しかし、それはあなた次第です。アクセスしたいだけの場合は、元のモデルを使用する場合は、戻り値を破棄するだけです。
Examples & options
1 対 n の関係がある場合は、hasOne
(belongs to) association を使用する必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13var Person = db.define('person', {
name : String
});
var Animal = db.define('animal', {
name : String
});
Animal.hasOne("owner", Person); // creates column 'owner_id' in 'animal' table
// get animal with id = 123
var animal = Animal.getSync(123);
// animal is the animal model instance, if found
var person = animal.getOwnerSync();
// if animal has really an owner, person points to it
owner_id
次のオプションを指定することで、データベース内でフィールドを必須としてマークできますrequired
。
1Animal.hasOne("owner", Person, { required: true });
フィールドが必須ではないが、フィールドが存在しない場合でも検証する必要がある場合は、オプションを指定しますalwaysValidate
(これは、たとえば、null フィールドの検証がレコード内の他のフィールドに依存している場合に発生する可能性があります)。
1Animal.hasOne("owner", Person, { required: false, alwaysValidate: true });
フィールド (owner_id) に別の名前を使用したい場合は、設定でこのパラメータを変更できます。
1db.settings.set("properties.association_key", "{field}_{name}"); // {name} will be replaced by 'owner' and {field} will be replaced by 'id' in this case
注: これは、関連付けを指定する前に行う必要があります。
関連付けhasMany
には、関連付けテーブルに追加のプロパティを含めることができます。
1
2
3
4
5
6
7
8
9
10
11var Person = db.define('person', {
name : String
});
Person.hasMany("friends", {
rate : Number
}, {}, { key: true });
var John = Person.getSync(123);
var friends = John.getFriendsSync();
// assumes rate is another column on table person_friends
// you can access it by going to friends[N].extra.rate
これによりautoFetch
、モデルのインスタンスを取得または検索したときに、関連付けが自動的に取得されます。
1
2
3
4
5
6
7
8
9
10
11
12var Person = db.define('person', {
name : String
});
Person.hasMany("friends", {
rate : Number
}, {
key : true, // Turns the foreign keys in the join table into a composite key
autoFetch : true
});
var John = Person.getSync(123);
// no need to do John.getFriends() , John already has John.friends Array
このオプションは、関連付けごとではなくグローバルに定義することもできます。
1
2
3
4
5
6
7
8
9
10var Person = db.define('person', {
name : String
}, {
autoFetch : true
});
Person.hasMany("friends", {
rate : Number
}, {
key: true
});
アソシエーションは、オプションを使用して、関連付けられた Model を呼び出すことができますreverse
。たとえば、ModelA から ModelB へのアソシエーションがある場合、ModelB にアクセサーを作成して、ModelA からインスタンスを取得できます。わかりにくいですか? 次の例を見てください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var Pet = db.define('pet', {
name : String
});
var Person = db.define('person', {
name : String
});
Pet.hasOne("owner", Person, {
reverse : "pets"
});
var pets = Person(4).getPetsSync();
// although the association was made on Pet,
// Person will have an accessor (getPets)
//
// In this example, ORM will fetch all pets
// whose owner_id = 4
これは、多対多の
関連付けを両側からhasMany
管理できるため、関連付けがある場合にはさらに意味があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var Pet = db.define('pet', {
name : String
});
var Person = db.define('person', {
name : String
});
Person.hasMany("pets", Pet, {
bought : Date
}, {
key : true,
reverse : "owners"
});
Person(1).getPetsSync(...);
Pet(2).getOwnersSync(...);
トランザクションサポート
低レベルのトランザクション関数を使用して DB トランザクションを処理できます。
1
2
3
4
5
6db.begin();
...
if(err)
db.rollback();
else
db.commit();
または、trans を使用して処理を簡素化することもできます。
1
2
3
4var result = db.trans(() => {
...
return result;
});
外部データベースアダプターの追加
外部データベース アダプタを に追加するにはorm
、addAdapter
メソッドを呼び出し、このアダプタとの接続に使用するエイリアスとアダプタのコンストラクタを渡します。
1require('orm').addAdapter('cassandra', CassandraAdapter);
見るthe documentation for creating adapters詳細については。