素晴らしいコミュニティモジュール

fibjs のオブジェクト リレーショナル マッピング

ビルドステータス

インストール

1
npm install fib-orm

テスト

1
npm 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 37
var 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 49
var 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 13
var 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 9
var 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の情報を参照してください

高度なオプション

defineORM2 を使用すると、モデル定義で高度な調整を行うことができ、これらは設定を通じて、またはモデルをセットアップするときの呼び出しで構成できます。

たとえば、各 Model インスタンスにはデータベース内で一意の ID があります。このテーブル列は自動的に追加され、デフォルトでは「id」と呼ばれます。
独自のkey: true列を定義する場合、「id」は追加されません。

1 2 3 4 5 6 7 8 9 10 11 12
var 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 4
var 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 2
var person = Person.getSync(123); // finds person with id = 123

Model.findSync([ conditions ] [, options ] [, limit ] [, order ])

1 つまたは複数の要素を検索するには、さらに多くのオプションがあり、それぞれの要素を特定のパラメーターの順序で指定することはできません(空のオブジェクトであっても)options後でなければなりませんconditions

1 2
var 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 6
var 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 2
var 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 2
var count = Person.countSync({ surname: "Doe" }); console.log("We have %d Does in our db", count);

Model.existsSync([ conditions])

と同様に.count()、このメソッドはカウントが 0 より大きいかどうかをチェックするだけです。

1 2
var 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 3
var 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 を指定して検索できます。

1
Person.find({ age: 18 }).where("LOWER(surname) LIKE ?", ['dea%']).allSync( ... );

SQL パラメータを手動でエスケープすることはエラーが発生しやすく、アプリケーションが SQL インジェクションの危険にさらされるため、推奨されません。クエリ内の疑問符を指定されたパラメータで安全に置き換えることにより、構文がエスケープを処理します。複数の句を次のように?連結することもできます。where必要です。

.find.where& は.all同じことを行います。これらはすべて交換可能であり、連鎖可能です。

orderまたは次のこともできますorderRaw

1 2 3
Person.find({ age: 18 }).order('-name').allSync( ... ); // see the 'Raw queries' section below for more details Person.find({ age: 18 }).orderRaw("?? DESC", ['age']).allSync( ... );

チェーンして最後にカウントだけを取得することもできますが、この場合、オフセット、リミット、順序は無視されます。

1 2
var people = Person.find({ surname: "Doe" }).countSync(); // people = number of people with surname="Doe"

選択した項目を削除するオプションも利用できます。連鎖削除ではフックは実行されないことに注意してください。

1 2
Person.find({ surname: "Doe" }).removeSync(); // Does gone..

一般的な配列走査メソッドを使用してインスタンスを変更し、最終的にすべてを保存することもできます。

1 2 3 4 5 6 7 8 9 10 11 12 13
Person.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 13
var 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 5
var Person = db.define('person', { name : String }, { identityCache : true });

そして世界的にも:

1 2
var 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 15
var 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 5
var John = Person.getSync(1); John.name = "Joe"; John.surname = "Doe"; John.saveSync(); console.log("saved!");

インスタンスの更新と保存は 1 回の呼び出しで実行できます。

1 2 3
var 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 4
animal.getOwnerSync() // Gets owner animal.setOwnerSync(person) // Sets owner_id animal.hasOwnerSync() // Checks if owner exists animal.removeOwnerSync() // Sets owner_id to 0

チェーン検索

hasOne 関連付けもチェーン検索と互換性があるため、上記の例を使用すると、ChainFind オブジェクトの新しいインスタンスにアクセスできます。

1
Animal.findByOwner({ /* options */ })

逆アクセス

1
Animal.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 12
patient.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()

医師を患者に関連付けるには:

1
patient.addDoctorSync(surgeon, {why: "remove appendix"})

{patient_id: 4, doctor_id: 6, why: "remove appendix"}これは結合テーブルに追加されます。

getAccessor

このタイプの関連付けのこのアクセサーは、ChainFindコールバックを渡さない場合に を返します。これは、次のようなことができることを意味します。

1 2
var doctors = patient.getDoctors().order("name").offset(1).runSync()); // ... all doctors, ordered by name, excluding first one

extendsTo

オプションのプロパティを別のテーブルまたはコレクションに分割したい場合は、すべての拡張機能が新しいテーブルに含まれ、各行の一意の識別子がメイン モデル インスタンス ID になります。例:

1 2 3 4 5 6 7
var Person = db.define("person", { name : String }); var PersonAddress = Person.extendsTo("address", { street : String, number : Number });

personこれにより、列idと を含むテーブルが作成されますname。拡張機能はperson_addressとを含むテーブルを作成しますperson_id。モデルで使用できるメソッドは関連付けに似ています。この例では、 、 .. を呼び出すことができます。streetnumberPersonhasOne.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 13
var 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

1
Animal.hasOne("owner", Person, { required: true });

フィールドが必須ではないが、フィールドが存在しない場合でも検証する必要がある場合は、オプションを指定しますalwaysValidate(これは、たとえば、null フィールドの検証がレコード内の他のフィールドに依存している場合に発生する可能性があります)。

1
Animal.hasOne("owner", Person, { required: false, alwaysValidate: true });

フィールド (owner_id) に別の名前を使用したい場合は、設定でこのパラメータを変更できます。

1
db.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 11
var 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 12
var 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 10
var 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 16
var 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 15
var 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 6
db.begin(); ... if(err) db.rollback(); else db.commit();

または、trans を使用して処理を簡素化することもできます。

1 2 3 4
var result = db.trans(() => { ... return result; });

外部データベースアダプターの追加

外部データベース アダプタを に追加するにはormaddAdapterメソッドを呼び出し、このアダプタとの接続に使用するエイリアスとアダプタのコンストラクタを渡します。

1
require('orm').addAdapter('cassandra', CassandraAdapter);

見るthe documentation for creating adapters詳細については。