Módulo comunitario incrible

Cartografía relacional de obxectos para fibjs

Estado de compilación

Instalar

1
npm install fib-orm

Proba

1
npm run ci

Soporte SGBD

  • MySQL e MariaDB
  • SQLite

características

fib-orm engade un conxunto de métodos de versión síncrona no obxecto node-orm.

Introdución

Este é un módulo de cartografía relacional de obxectos fibjs.

Un exemplo:

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

A versión node.js deste xeito:

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

Documentación

Fibjs non engadir novas funcións, o desenvolvemento de documentos poden referirse ao nó-ORM, só precisa cambiar a chamada asíncrona a versión síncrona. Wiki .

Configuración

Ver información na wiki .

Conectando

Ver información na wiki .

Modelos

Un modelo é unha abstracción sobre unha ou máis táboas de bases de datos. Os modelos admiten asociacións (máis abaixo). Suponse que o nome do modelo coincide co nome da táboa.

Os modelos admiten comportamentos para acceder e manipular datos de táboas.

Definición de modelos

Ver información na wiki .

Properties

Ver información na wiki .

Instance Methods

Pásanse durante a definición do modelo.

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

Defínense directamente no modelo.

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

Cargando modelos [NON SOPORTE]

Os modelos poden estar en módulos separados. Simplemente asegúrese de que o módulo que contén os modelos usa module.exports para publicar unha función que acepte a conexión á base de datos e logo cargue os modelos como queira.

Utilizando esta técnica, pode ter cargas en cascada.

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

Sincronización de modelos

Ver información na wiki .

Caída de modelos

Ver información na wiki .

Opcións avanzadas

ORM2 permítelle algúns axustes avanzados nas definicións do modelo. Pode configuralos mediante axustes ou na chamada ao defineconfigurar o modelo.

Por exemplo, cada instancia de modelo ten un ID único na base de datos. Esta columna da táboa engádese automaticamente e chámase "id" por defecto.
Se define a súa propia key: truecolumna, non se engadirá "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 });

O modelo de mascota terá 2 columnas, unha UIDe unha name.

Tamén é posible ter teclas compostas:

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

Outras opcións:

  • identityCache : (predeterminado :) falseConfigúrao truepara habilitar a caché de identidade ( Singletons ) ou define un valor de tempo de espera (en segundos);
  • autoSave : (predeterminado :) falseConfigúrao en truepara gardar unha instancia xusto despois de cambiar calquera propiedade;
  • autoFetch : (predeterminado :) falseConfigúreo trueen buscar asociacións ao buscar unha instancia da base de datos;
  • autoFetchLimit: (por defecto :) 1Se autoFetchestá activado, defínese cantos aros (asociacións de asociacións) desexa que busque automaticamente.

Ganchos

Ver información na wiki .

Buscar elementos

Model.getSync(id, [ options ])

Para obter un elemento específico do uso da base de datos Model.get.

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

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

Atopar un ou máis elementos ten máis opcións, cada un pode darse sen unha orde de parámetros específica. Só optionsten que ser despois conditions(aínda que sexa un obxecto baleiro).

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 precisas ordenar os resultados porque estás limitando ou simplemente porque queres que se ordenen, faino:

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)

Hai máis opcións que podes pasar para atopar algo. Estas opcións pásanse nun segundo obxecto:

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

Tamén podes usar SQL en bruto durante a busca. Está documentado na sección de encadeamento a continuación.

Model.countSync([ conditions])

Se só desexa contar o número de elementos que coinciden cunha condición, pode usar en .count()vez de atopalos todos e contalos. Isto indicará ao servidor de base de datos que faga un reconto (non se fará no propio proceso do nodo) ).

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

Model.existsSync([ conditions])

De xeito similar .count(), este método só comproba se o reconto é maior que cero ou non.

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

Aggregating Functions

ArrayPódese pasar unha das propiedades para seleccionar só algunhas propiedades. ObjectTamén se acepta unha para definir as condicións.

Aquí tes un exemplo para ilustrar como usar .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()Métodos base

  • .limit(): pode pasar un número como límite ou dous números como compensación e límite respectivamente
  • .order(): o mesmo que Model.find().order()

.aggregate()Métodos adicionais

  • min
  • max
  • avg
  • sum
  • count(hai un atallo para isto- Model.count)

Hai máis funcións agregadas dependendo do controlador (por exemplo, funcións matemáticas).

Chaining

Se prefires unha sintaxe menos complicada, podes encadeala .find()sen dar un parámetro de devolución de chamada.

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 quere saltar só unha ou dúas propiedades, pode chamar en .omit()vez de .only.

A cadea permite realizar consultas máis complicadas. Por exemplo, podemos buscar especificando SQL personalizado:

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

É unha mala práctica escapar manualmente dos parámetros SQL xa que é propenso a erros e expón a súa aplicación a inxección SQL. A ?sintaxe encárgase de escapar por vostede, substituíndo de forma segura o signo de interrogación na consulta cos parámetros proporcionados. Tamén pode encadear varias wherecláusulas como necesario.

.find, .whereE .allfacer o mesmo; son todos intercambiables e en cadea.

Tamén podes orderou 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( ... );

Tamén podes encadear e obter o reconto ao final. Neste caso, ignóranse o desprazamento, o límite e a orde.

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

Tamén está dispoñible a opción de eliminar os elementos seleccionados. Ten en conta que unha eliminación encadeada non executará ningún enganche.

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

Tamén podes facer modificacións nas túas instancias usando métodos comúns de percorrido de matrices e gardar todo ao final. [NON SOPORTE]

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

Por suposto, podes facelo directamente .find(), pero para algunhas tarefas máis complicadas isto pode ser moi útil.

Model.find()non voltar un array para que non pode simplemente acorrentar directamente. Para iniciar o fío ten que chamar .each()(cun callback opcional se quere percorrer a lista). A continuación, pode utilizar as funcións comúns .filter(), .sort()e .forEach()máis dunha vez.

Ao final (ou durante o proceso ..) pode chamar a:

  • .countSync() se só quere saber cantos elementos hai;
  • .getSync() para recuperar a lista;
  • .saveSync() para gardar todos os cambios de elementos.

Condicións

As condicións defínense como un obxecto onde cada clave é unha propiedade (columna de táboa). Suponse que todas as claves están concatenadas polo lóxico AND. Considéranse que os valores coinciden exactamente, a non ser que estea pasando un Array. Neste caso considérase unha lista coa que comparar a propiedade.

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

Se precisas outras comparacións, tes que empregar un obxecto especial creado por algunhas funcións de axuda. Aquí tes algúns exemplos para describilo:

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)

Consultas en bruto

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

Podes usar o patrón de identidade (desactivado por defecto). Se está activado, varias consultas diferentes darán o mesmo resultado. Obterás o mesmo obxecto. Se tes outros sistemas que poden cambiar a túa base de datos ou tes que chamar a algún manual Consultas SQL, non debes empregar esta función. Tamén se sabe que causa algúns problemas con relacións complexas de recuperación automática. Utilízao baixo o teu propio risco.

Pode habilitarse / desactivarse por modelo:

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

e tamén a nivel mundial:

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

A caché de identidade pódese configurar para que caduca despois dun período de tempo pasando un número en lugar dun booleano. O número considerarase o tempo de espera da caché en segundos (pode usar o punto flotante).

Nota : unha excepción sobre a caché é que non se usará se non se garda unha instancia. Por exemplo, se buscas unha persoa e a cambias, mentres non se garda, non se pasará da caché.

Creando elementos

Model.createSync(items)

Para inserir novos elementos no uso da base de datos 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

Actualizando elementos

Cada elemento devolto ten as propiedades definidas no modelo e tamén un par de métodos que pode usar para cambiar cada elemento.

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

A actualización e logo gardar unha instancia pódese facer nunha soa chamada:

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

Se desexa eliminar unha instancia, faga o seguinte:

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

Validacións

Ver información na wiki .

Asociacións

Unha asociación é unha relación entre unha ou máis táboas.

hasOne

A É MOITO para un . É a mesma relación, como pertence.
Por exemplo: Animal.hasOne('owner', Person).. O
animal PODE ter só un dono, ten MOITO. Pero a persoa que os
animais de Will terán na owner_idpropiedade engádese automaticamente.

Estarán dispoñibles as seguintes funcións:

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

Busca de cadeas

A asociación hasOne tamén é compatible coa busca en cadea. Usando o exemplo anterior, podemos facelo para acceder a unha nova instancia dun obxecto ChainFind:

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

Acceso inverso

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

engadirá o seguinte:

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

hasMany

A IS moitos para moitos relación (inclúe a táboa de unión).
Por exemplo, de: Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true }).
. O Paciente cada un pode Doutor médicos ten moitos poden Diferentes pacientes diferentes teñen teñen moitas.

Isto creará unha táboa de unión patient_doctorscando chame a Patient.sync():

nome da columna tipo
ID_paciente Entero (tecla composta)
médico_id Entero (tecla composta)
por que varchar (255)

Estarán dispoñibles as seguintes funcións:

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

Para asociar un médico a un paciente:

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

que se engadirá {patient_id: 4, doctor_id: 6, why: "remove appendix"}á táboa de unión.

getAccessor

Este accesor neste tipo de asociación devolve ChainFindse non pasa unha devolución de chamada. Isto significa que podes facer cousas como:

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

extendsTo

Se queres dividir propiedades opcionais en táboas ou coleccións diferentes. Todas as extensións estarán nunha nova táboa, onde o identificador único de cada fila é o identificador de instancia do modelo principal. Por exemplo:

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

Unha orde CREATE TABLE a este personcon columnas ide name. Extensión do creará unha táboa person_addresscon columnas person_id, streete number. Dispoñible nos métodos de PersonModelo semellante a un son hasOne Association. Vostede o No presente exemplo poder chamar .getAddress(cb), .setAddress(Address, cb), ..

Nota: non tes que gardar o resultado Person.extendsTo. Devolve un modelo estendido. Podes usalo para consultar directamente esta táboa estendida (e incluso atopar o modelo relacionado) pero depende de ti. Se só queres acceder a ela usando o modelo orixinal só pode descartar a devolución.

Examples & options

Se tes unha relación de 1 a n, debes empregar hasOne(pertence a) asociación.

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

Pode marcar o owner_idcampo segundo o requirido na base de datos especificando a requiredopción:

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

Se non se require un campo, pero debería validarse aínda que non estea presente, especifique a alwaysValidateopción (isto pode ocorrer, por exemplo, cando a validación dun campo nulo depende doutros campos do rexistro)

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

Se prefires usar outro nome para o campo (propietario_id), podes cambiar este parámetro na configuración.

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: Isto debe facerse antes de que se especifique a asociación.

As hasManyasociacións poden ter propiedades adicionais na táboa de asociacións.

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 o prefires, podes activalo autoFetch. Deste xeito, as asociacións obtéñense automaticamente cando obteñas ou atopas instancias dun modelo.

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

Tamén pode definir esta opción globalmente en vez de por asociación.

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

As asociacións poden facer chamadas ao modelo asociado usando a reverseopción. Por exemplo, se tes unha asociación de ModelA a ModelB, podes crear un accesor en ModelB para obter instancias de ModelA. ¿Confuso? Mira o seguinte exemplo.

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

Isto ten aínda máis sentido cando se teñen hasManyasociacións, xa que pode xestionar moitas asociacións de ambos os dous lados.

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

Soporte de transaccións

Podes usar a función de transacción de baixo nivel para procesar a transcrición de db.

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

Ou podes usar trans para procesalo en simpile.

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

Engadindo adaptadores de base de datos externos

Para engadir un adaptador de base de datos externo orm, chame ao addAdaptermétodo, pasando o alias para usar para conectarse con este adaptador, xunto co construtor do adaptador:

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

Ver the documentation for creating adapters para máis detalles.