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

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の情報を参照してください。

高度なオプション

ORM2を使用すると、モデル定義を高度に微調整できます。これらは、設定を介して、または呼び出しでモデルをセットアップdefineときにdefineするように構成できます。

たとえば、各モデルインスタンスはデータベース内で一意のIDを持っています。このテーブル列は自動的に追加され、デフォルトで「id」と呼ばれます。
独自のkey: trueを定義する場合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 });

ペットモデルには、 UIDname 2つの列があります。

複合キーを持つことも可能です:

1 2 3 4
var Person = db.define("person", { firstname : { type: 'text', key: true }, lastname : { type: 'text', key: true } });

別のオプション:

  • identityCache :(デフォルト: false )IDキャッシュを有効にするにはtrueに設定し(シングルトン)、タイムアウト値を設定します(秒単位)。
  • autoSave :(デフォルト: false )プロパティを変更した直後にインスタンスを保存するには、 trueに設定します。
  • autoFetch :(デフォルト: false )データベースからインスタンスをフェッチするときに関連付けをフェッチするには、 trueに設定します。
  • autoFetchLimit :(デフォルト: 1autoFetchが有効になっている場合、これは自動的にフェッチするフープ(関連付けの関連付け)の数を定義します。

フック

wikiの情報を参照してください。

アイテムを見つけます

Model.getSync(id, [ options ])

データベースから特定の要素を取得するには、 Model.get使用しModel.get

1 2
var person = Person.getSync(123); // finds person with id = 123

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

1つ以上の要素を見つけるには、より多くのオプションがあり、各要素を特定のパラメーターの順序で指定することはできません。 optionsのみをconditions後に指定する必要がoptions (空のオブジェクトであっても)。

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()と同様に、このメソッドはカウントがゼロより大きいかどうかをチェックするだけです。

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

ドライバーに応じて、より多くの集約関数があります(たとえば、Math関数)。

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つのプロパティだけをスキップする場合は、 .only代わりに.only .omit()呼び出すことができます。

連鎖により、より複雑なクエリが可能になります。たとえば、カスタム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つの例外は、インスタンスが保存されていない場合は使用されないことです。たとえば、Personをフェッチして変更した場合、保存されていない間はキャッシュから渡されません。

アイテムの作成

Model.createSync(items)

データベースに新しい要素を挿入するには、 Model.create使用し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

多対一の関係です。所属するのと同じです。
例: Animal.hasOne('owner', Person)
動物は1人の所有者しか持つことができませんが、人は多くの動物を持つことができます。
動物には、 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.sync()を呼び出すときに結合テーブルpatient_doctorsが作成されます。

列名タイプ
patient_id整数(複合キー)
doctor_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列とidname 。拡張子は、テーブルを作成しますperson_address列にperson_idstreetnumber 。で利用可能な方法Personモデルがに似ている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 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

requiredオプションを指定することにより、データベースでowner_idフィールドを必須としてマークできます。

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 });

アソシエーションは、 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; });

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

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

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

詳細についてthe documentation for creating adapters参照しthe documentation for creating adaptersください。