Fantastico modulo comunitario

Mappatura relazionale di oggetti per fibjs

Stato build

Installare

1
npm install fib-orm

Test

1
npm run ci

Supporto DBMS

  • MySQL e MariaDB
  • SQLite

Caratteristiche

fib-orm aggiunge una serie di metodi di versione sincrona sull'oggetto node-orm.

introduzione

Questo è un modulo di mappatura relazionale di oggetti fibjs.

Un esempio:

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

La versione di node.js in questo modo:

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

Documentazione

Fibjs non ha aggiunto nuove funzioni, lo sviluppo di documenti può fare riferimento a node-orm, deve solo cambiare la chiamata asincrona in versione sincrona. Wiki .

impostazioni

Vedi le informazioni nel wiki .

Collegamento

Vedi le informazioni nel wiki .

Modelli

Un modello è un'astrazione su una o più tabelle del database.I modelli supportano le associazioni (più sotto) Si presume che il nome del modello corrisponda al nome della tabella.

I modelli supportano comportamenti per l'accesso e la manipolazione dei dati della tabella.

Definizione dei modelli

Vedi le informazioni nel wiki .

Properties

Vedi le informazioni nel wiki .

Instance Methods

Vengono passati durante la definizione del modello.

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

Sono definiti direttamente sul modello.

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

Caricamento dei modelli [NON SUPPORTO]

I modelli possono essere in moduli separati: assicurati semplicemente che il modulo che contiene i modelli utilizzi module.exports per pubblicare una funzione che accetta la connessione al database, quindi carica i tuoi modelli come preferisci.

Nota: utilizzando questa tecnica è possibile avere carichi a cascata.

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

Sincronizzazione dei modelli

Vedi le informazioni nel wiki .

Modelli cadenti

Vedi le informazioni nel wiki .

Opzioni avanzate

ORM2 consente alcune modifiche avanzate alle definizioni del modello, che è possibile configurare tramite impostazioni o nella chiamata per define quando si imposta il modello.

Ad esempio, ogni istanza del modello ha un ID univoco nel database: questa colonna della tabella viene aggiunta automaticamente e denominata "id" per impostazione predefinita.
Se definisci la tua key: true colonna key: true , "id" non verrà aggiunto:

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

Il modello di animale domestico avrà 2 colonne, un UID e un name .

È anche possibile avere chiavi composite:

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

Altre opzioni:

  • identityCache : (default: false ) Impostalo su true per abilitare la cache delle identità ( Singletons ) o impostare un valore di timeout (in secondi);
  • autoSave : (default: false ) Impostalo su true per salvare autoSave subito dopo aver modificato qualsiasi proprietà;
  • autoFetch : (default: false ) Impostalo su true per recuperare le associazioni quando recupera un'istanza dal database;
  • autoFetchLimit : (impostazione predefinita: 1 ) Se autoFetch è abilitato, questo definisce il numero di cerchi (associazioni di associazioni) che devono essere recuperati automaticamente.

ganci

Vedi le informazioni nel wiki .

Trovando gli oggetti

Model.getSync(id, [ options ])

Per ottenere un elemento specifico dal database utilizzare Model.get .

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

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

Trovare uno o più elementi è più opzioni, ciascuno può essere determinato senza un ordine specifico parametro. Solo le options deve essere successiva conditions (anche se è un oggetto vuoto).

1 2
var people = Person.findSync({ name: "John", surname: "Doe" }, 3); // finds people with name='John' AND surname='Doe' and returns the first 3

Se devi ordinare i risultati perché stai limitando o semplicemente perché vuoi che vengano ordinati, fai:

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)

Esistono più opzioni che puoi passare per trovare qualcosa: queste opzioni sono passate in un secondo oggetto:

1 2
var people = Person.findSync({ surname: "Doe" }, { offset: 2 }); // finds people with surname='Doe', skips the first 2 and returns the others

È inoltre possibile utilizzare SQL non elaborati durante la ricerca, documentato nella sezione Concatenamento di seguito.

Model.countSync([ conditions])

Se vuoi solo contare il numero di elementi che soddisfano una condizione, puoi semplicemente usare .count() invece di trovarli tutti e contare. Questo in realtà dirà al server del database di fare un conteggio (non verrà fatto in il processo nodo stesso).

1 2
var count = Person.countSync({ surname: "Doe" }); console.log("We have %d Does in our db", count);

Model.existsSync([ conditions])

Simile a .count() , questo metodo controlla solo se il conteggio è maggiore o uguale a zero.

1 2
var exists = Person.existsSync({ surname: "Doe" }); console.log("We %s Does in our db", exists ? "have" : "don't have");

Aggregating Functions

Una Array di proprietà può essere passata per selezionare solo alcune proprietà e viene accettato anche un Object per definire le condizioni.

Ecco un esempio per illustrare come utilizzare .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() base .aggregate()

  • .limit() : puoi passare un numero come limite o due numeri rispettivamente come offset e limite
  • .order() : uguale a Model.find().order()

.aggregate() aggiuntivi

  • min
  • max
  • avg
  • sum
  • count (c'è un collegamento a Model.count )

Esistono più funzioni aggregate a seconda del driver (ad esempio funzioni matematiche).

Chaining

Se preferisci una sintassi meno complicata, puoi concatenare .find() non fornendo un parametro di callback.

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

Se vuoi saltare solo una o due proprietà, puoi chiamare .omit() invece di .only .

Il concatenamento consente query più complicate, ad esempio possiamo cercare specificando l'SQL personalizzato:

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

E 'cattiva pratica per sfuggire manualmente i parametri SQL in quanto del soggetto a errori ed espone l'applicazione per SQL injection. L' ? Syntax si prende cura di fuga per voi, sostituendo in modo sicuro il punto di domanda nella query con i parametri previsti. È inoltre possibile catena multipla where clausole secondo necessità.

.find , .where & .all fare cosa stessi; tutti sono intercambiabili e chainable.

Puoi anche order o 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( ... );

Puoi anche concatenare e ottenere il conteggio alla fine, in questo caso offset, limite e ordine vengono ignorati.

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

È disponibile anche l'opzione per rimuovere gli elementi selezionati. Nota che una rimozione concatenata non eseguirà alcun hook.

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

Puoi anche apportare modifiche alle tue istanze usando i comuni metodi di attraversamento dell'array e salvare tutto alla fine. [NON SUPPORTO]

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

Ovviamente potresti farlo direttamente su .find() , ma per alcune attività più complicate questo può essere molto utile.

Model.find() non restituisce un array, quindi non puoi semplicemente concatenare direttamente. Per iniziare il concatenamento devi chiamare .each() (con un callback opzionale se vuoi attraversare l'elenco). Puoi quindi usare il comune funzioni .filter() , .sort() e .forEach() più di una volta.

Alla fine (o durante il processo ..) puoi chiamare:

  • .countSync() se vuoi solo sapere quanti elementi ci sono;
  • .getSync() per recuperare l'elenco;
  • .saveSync() per salvare tutte le modifiche agli articoli.

condizioni

Le condizioni sono definite come un oggetto in cui ogni chiave è una proprietà (colonna della tabella). Tutte le chiavi devono essere concatenate dal logico AND . I valori sono considerati corrispondere esattamente, a meno che non si stia passando una Array . In questo caso è considerata un elenco con cui confrontare la proprietà.

1 2
{ col1: 123, col2: "foo" } // `col1` = 123 AND `col2` = 'foo' { col1: [ 1, 3, 5 ] } // `col1` IN (1, 3, 5)

Se hai bisogno di altri confronti, devi usare un oggetto speciale creato da alcune funzioni di aiuto, qui di seguito alcuni esempi per descriverlo:

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)

Query non elaborate

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

È possibile utilizzare il modello di identità (disattivato per impostazione predefinita). Se abilitato, più query diverse comporteranno lo stesso risultato, si otterrà lo stesso oggetto. Se si dispone di altri sistemi che possono modificare il database o è necessario chiamare un manuale Query SQL, non dovresti usare questa funzione, ma è anche noto per causare alcuni problemi con relazioni di recupero automatico complesse. Usa a tuo rischio.

Può essere abilitato / disabilitato per modello:

1 2 3 4 5
var Person = db.define('person', { name : String }, { identityCache : true });

e anche a livello globale:

1 2
var db = orm.connectSync('...'); db.settings.set('instance.identityCache', true);

La cache delle identità può essere configurata per scadere dopo un certo periodo di tempo passando un numero anziché un valore booleano. Il numero verrà considerato il timeout della cache in secondi (è possibile utilizzare il virgola mobile).

Nota : un'eccezione sulla memorizzazione nella cache è che non verrà utilizzata se un'istanza non viene salvata, ad esempio se si recupera una persona e la si modifica, mentre non viene salvata, non verrà passata dalla cache.

Creazione di articoli

Model.createSync(items)

Per inserire nuovi elementi nel database utilizzare 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

Aggiornamento degli articoli

Ogni articolo restituito ha le proprietà definite nel Modello e anche un paio di metodi che è possibile utilizzare per modificare ciascun articolo.

1 2 3 4 5
var John = Person.getSync(1); John.name = "Joe"; John.surname = "Doe"; John.saveSync(); console.log("saved!");

L'aggiornamento e il salvataggio di un'istanza possono essere effettuati in una singola chiamata:

1 2 3
var John = Person.getSync(1); John.saveSync({ name: "Joe", surname: "Doe" }); console.log("saved!");

Se vuoi rimuovere un'istanza, fai semplicemente:

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!");

Validazioni

Vedi le informazioni nel wiki .

associazioni

Un'associazione è una relazione tra una o più tabelle.

hasOne

È una relazione da molti a uno . È lo stesso di cui appartiene.
Ad esempio: Animal.hasOne('owner', Person) .
L'animale può avere un solo proprietario, ma la persona può avere molti animali.
owner_id verrà aggiunta automaticamente la proprietà owner_id .

Saranno disponibili le seguenti funzioni:

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

Trova a catena

Anche l'associazione hasOne è compatibile con la catena. Usando l'esempio sopra, possiamo farlo per accedere a una nuova istanza di un oggetto ChainFind:

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

Accesso inverso

1
Animal.hasOne('owner', Person, {reverse: 'pets'})

aggiungerà quanto segue:

1 2 3 4 5 6
// Instance methods person.getPetsSync(function..) person.setPetsSync(cat, function..) // Model methods Person.findByPets({ /* options */ }) // returns ChainFind object

hasMany

È una relazione da molte a molte (include la tabella di join).
Ad esempio: Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true }) .
I pazienti possono avere molti medici diversi, ogni medico può avere molti pazienti diversi.

Questo creerà una tabella di join patient_doctors quando si chiama Patient.sync() :

nome della colonna genere
patient_id Numero intero (chiave composita)
doctor_id Numero intero (chiave composita)
perché varchar (255)

Saranno disponibili le seguenti funzioni:

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()

Per associare un medico a un paziente:

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

che aggiungerà {patient_id: 4, doctor_id: 6, why: "remove appendix"} alla tabella di join.

getAccessor

Questo accessor in questo tipo di associazione restituisce un ChainFind se non passa un callback, il che significa che puoi fare cose come:

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

extendsTo

Se vuoi dividere forse le proprietà opzionali in diverse tabelle o raccolte. Ogni estensione sarà in una nuova tabella, dove l'identificatore univoco di ogni riga è l'id dell'istanza del modello principale, ad esempio:

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

Questo creerà una person tabella con colonne id e name . L'estensione creerà una tabella di person_address con colonne person_id , street e number . I metodi disponibili nel modello Person sono simili a un'associazione hasOne . In questo esempio puoi chiamare .getAddress(cb) , .setAddress(Address, cb) , ..

Nota: non è necessario salvare il risultato da Person.extendsTo . Restituisce un modello esteso. È possibile utilizzarlo per interrogare direttamente questa tabella estesa (e persino trovare il modello correlato), ma Person.extendsTo da te. Se vuoi solo per accedervi usando il modello originale puoi semplicemente scartare il reso.

Examples & options

Se hai una relazione da 1 a n, dovresti usare l' hasOne (appartiene a).

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

È possibile contrassegnare il campo owner_id come richiesto nel database specificando l'opzione required :

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

Se un campo non è obbligatorio, ma deve essere convalidato anche se non è presente, specificare l'opzione alwaysValidate (ciò può accadere, ad esempio, quando la convalida di un campo nullo dipende da altri campi nel record)

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

Se si preferisce utilizzare un altro nome per il campo (owner_id), è possibile modificare questo parametro nelle impostazioni.

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

Nota: questa operazione deve essere eseguita prima di specificare l'associazione.

Le associazioni hasMany possono avere proprietà aggiuntive nella tabella delle associazioni.

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

Se preferisci, puoi attivare il autoFetch automatico, in questo modo le associazioni vengono recuperate automaticamente quando ottieni o trovi istanze di un modello.

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

È inoltre possibile definire questa opzione a livello globale anziché in base all'associazione.

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

Le associazioni possono effettuare chiamate al modello associato utilizzando l' reverse dell'opzione. Ad esempio, se si dispone di un'associazione da ModelA a ModelB, è possibile creare una funzione di accesso in ModelB per ottenere istanze da ModelA. Confondere? Guardate il prossimo esempio.

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

Questo ha ancora più senso quando si hanno hasMany Associazioni poiché è possibile gestire molte o molte associazioni da entrambe le parti.

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(...);

Supporto per le transazioni

È possibile utilizzare la funzione di transazione di basso livello per elaborare la transazione db.

1 2 3 4 5 6
db.begin(); ... if(err) db.rollback(); else db.commit();

Oppure puoi usare trans per semplificarlo.

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

Aggiunta di adattatori di database esterni

Per aggiungere un adattatore di database esterno a orm , chiama il metodo addAdapter , passando l'alias da utilizzare per la connessione con questo adattatore, insieme al costruttore per l'adattatore:

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

Consultare the documentation for creating adapters per maggiori dettagli.