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の情報を参照してください。
高度なオプション
ORM2を使用すると、モデル定義を高度に微調整できます。これらは、設定を介して、またはdefine
モデルのセットアップ時の呼び出しで構成できます。
たとえば、各モデルインスタンスはデータベース内で一意の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
:(デフォルト:)false
設定し、それはへの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()
、このメソッドはカウントがゼロより大きいかどうかをチェックするだけです。
1
2var exists = Person.existsSync({ surname: "Doe" });
console.log("We %s Does in our db", exists ? "have" : "don't have");
Aggregating Functions
Array
いくつかのプロパティのみを選択するために、プロパティの1つを渡すことができます。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つの例外は、インスタンスが保存されていない場合は使用されないことです。たとえば、Personをフェッチして変更した場合、保存されていない間はキャッシュから渡されません。
アイテムの作成
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
Aは一つのために多くのそれは、ASは、同じで関係です。に属しています。
例:Animal.hasOne('owner', Person)
。
。動物は、唯一のワンオーナーを持っていことができる多くのことができますが、人の動物持っている
意志によって動物をで持ってき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
IS多対多の関係(参加表を含みます)。
のEg: 。Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true })
。
患者の各CANドクター医師がMANYできるさまざまな異なる患者が多くを持っていていています。
これにより、patient_doctors
次の呼び出し時に結合テーブルが作成されますPatient.sync()
。
列名 | タイプ |
---|---|
patient_id | 整数(複合キー) |
doctor_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
});
Aがこれを表ウィルを作成してperson
列にid
してname
。A表を作成する拡張のperson_address
列でperson_id
、street
そしてnumber
。使用可能にする方法でPerson
ANとモデルと同様ですhasOne
協会うあなたはこの例では呼び出すことができます.getAddress(cb)
、.setAddress(Address, cb)
..
注:からの結果を保存する必要はありませんPerson.extendsTo
。拡張モデルを返します。これを使用して、この拡張テーブルに直接クエリを実行できます(さらに、関連するモデルを見つけることもできます)が、それはあなた次第です。アクセスするだけの場合元のモデルを使用すると、返品を破棄できます。
Examples & options
1からnの関係がある場合は、関連付けを使用するhasOne
(に属する)必要があります。
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
});
アソシエーションは、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 詳細については。