Impresionante módulo de comunidad

Mapeo relacional de objetos para fibjs

Estado de compilación

Instalar

1
npm install fib-orm

Prueba

1
npm run ci

Soporte DBMS

  • MySQL y MariaDB
  • SQLite

Caracteristicas

fib-orm agrega un conjunto de métodos de versión síncrona en el objeto nodo-orm

Introduccion

Este es un módulo de mapeo relacional de objetos fibjs.

Un ejemplo:

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 versión de node.js como esta:

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 no agregó nuevas funciones, el desarrollo de documentos puede referirse a nodo-orm, solo necesita cambiar la llamada asincrónica a la versión sincrónica. Wiki .

Configuraciones

Ver información en la wiki .

Conectando

Ver información en la wiki .

Modelos

Un modelo es una abstracción sobre una o más tablas de bases de datos. Los modelos admiten asociaciones (más abajo). Se supone que el nombre del modelo coincide con el nombre de la tabla.

Los modelos admiten comportamientos para acceder y manipular datos de la tabla.

Definiendo modelos

Ver información en la wiki .

Properties

Ver información en la wiki .

Instance Methods

Se pasan durante la definición del 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

Se definen directamente en el 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 [NO SOPORTE]

Los modelos pueden estar en módulos separados. Simplemente asegúrese de que el módulo que contiene los modelos use module.exports para publicar una función que acepte la conexión de la base de datos, luego cargue sus modelos como desee.

Nota: con esta técnica puede tener 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 }); };

Modelos de sincronización

Ver información en la wiki .

Dejar caer modelos

Ver información en la wiki .

Opciones avanzadas

ORM2 le permite algunos ajustes avanzados en las definiciones de su Modelo. Puede configurarlos a través de la configuración o en la llamada para define cuándo configura el Modelo.

Por ejemplo, cada instancia de Model tiene una ID única en la base de datos. Esta columna de tabla se agrega automáticamente y se denomina "id" de forma predeterminada.
Si define su propia key: true columna key: true , no se agregará "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 });

El modelo de mascota tendrá 2 columnas, un UID y un name .

También es posible tener claves compuestas:

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

Otras opciones:

  • identityCache : (predeterminado: false ) Configúrelo en true para habilitar el caché de identidad ( Singletons ) o establezca un valor de tiempo de espera (en segundos);
  • autoSave : (predeterminado: false ) autoSave en true para guardar una Instancia justo después de cambiar cualquier propiedad;
  • autoFetch : (predeterminado: false ) autoFetch en true para recuperar asociaciones al recuperar una instancia de la base de datos;
  • autoFetchLimit : (predeterminado: 1 ) Si autoFetch está habilitado, esto define cuántos aros (asociaciones de asociaciones) desea que autoFetch automáticamente.

Ganchos

Ver información en la wiki .

Encontrar artículos

Model.getSync(id, [ options ])

Para obtener un elemento específico de la base de datos, use Model.get .

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

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

Encontrar uno o más elementos tiene más opciones, cada una se puede dar sin un orden de parámetros específico. Solo las options deben ser posteriores a las conditions (incluso si es un objeto vacío).

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

Si necesita ordenar los resultados porque está limitando o simplemente porque desea ordenarlos, haga lo siguiente:

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)

Hay más opciones que puede pasar para encontrar algo. Estas opciones se pasan en un segundo objeto:

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

También puede usar SQL sin procesar al buscar. Está documentado en la sección Encadenamiento a continuación.

Model.countSync([ conditions])

Si solo desea contar la cantidad de elementos que coinciden con una condición, puede usar .count() lugar de encontrarlos y contarlos. Esto realmente le indicará al servidor de la base de datos que haga un recuento (no se hará en el proceso del nodo en sí mismo).

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

Model.existsSync([ conditions])

Similar a .count() , este método solo verifica si el conteo es mayor que cero o no.

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

Aggregating Functions

Se puede pasar una Array de propiedades para seleccionar solo unas pocas propiedades. También se acepta un Object para definir condiciones.

Aquí hay un ejemplo para ilustrar cómo 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() base .aggregate()

  • .limit() : puede pasar un número como límite, o dos números como desplazamiento y límite respectivamente
  • .order() : igual que Model.find().order()

.aggregate() adicionales .aggregate()

  • min
  • max
  • avg
  • sum
  • count (hay un atajo para esto: Model.count )

Hay más funciones agregadas según el controlador (funciones matemáticas, por ejemplo).

Chaining

Si prefiere una sintaxis menos complicada, puede encadenar .find() al no proporcionar un parámetro de devolución de llamada.

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

Si desea omitir solo una o dos propiedades, puede llamar a .omit() lugar de .only .

El encadenamiento permite consultas más complicadas. Por ejemplo, podemos buscar especificando SQL personalizado:

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

Es de mala práctica a la manual de escape SQL los parámetros del AS de error de TI de prono y expone su Aplicación a la inyección de SQL. En la ? Sintaxis toma el cuidado de escape para usted, por la sustitución con seguridad en signo de interrogación en al Query con al Los los parámetros proporcionados. Por También puede hacer cadena hotelera múltiple where cláusulas según sea necesario.

.find , .where y .all hacer cosa igual; todo lo que son intercambiables y conexión en cadena.

También puede order u 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( ... );

También puede encadenar y obtener el recuento al final. En este caso, se ignoran el desplazamiento, el límite y el orden.

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

También está disponible la opción para eliminar los elementos seleccionados. Tenga en cuenta que una eliminación encadenada no ejecutará ningún gancho.

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

También puede realizar modificaciones en sus instancias utilizando métodos de recorrido de Array comunes y guardar todo al final. [NO 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 supuesto, puede hacer esto directamente en .find() , pero para algunas tareas más complicadas, esto puede ser muy útil.

Model.find() no devuelve una matriz, por lo que no puede encadenar directamente. Para comenzar a encadenar debe llamar a .each() (con una devolución de llamada opcional si desea recorrer la lista). Luego puede usar el común funciones .filter() , .sort() y .forEach() más de una vez.

Al final (o durante el proceso ...) puede llamar:

  • .countSync() si solo quieres saber cuántos elementos hay;
  • .getSync() para recuperar la lista;
  • .saveSync() para guardar todos los cambios de elementos.

Condiciones

Las condiciones se definen como un objeto en el que cada clave es una propiedad (columna de tabla). Se supone que todas las claves están concatenadas por el AND lógico. Se considera que los valores coinciden exactamente, a menos que pase una Array . En este caso, se considera Una lista para comparar la propiedad.

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

Si necesita otras comparaciones, debe usar un objeto especial creado por algunas funciones auxiliares. Aquí hay algunos ejemplos para describirlo:

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 sin procesar

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

Puede usar el patrón de identidad (desactivado de manera predeterminada). Si está habilitado, múltiples consultas diferentes darán como resultado el mismo resultado: obtendrá el mismo objeto. Si tiene otros sistemas que pueden cambiar su base de datos o necesita llamar a algún manual Consultas SQL, no debe usar esta función. También se sabe que causa algunos problemas con las complejas relaciones de recuperación automática. Use bajo su propio riesgo.

Se puede habilitar / deshabilitar por modelo:

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

y también a nivel mundial:

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

El caché de identidad se puede configurar para que caduque después de un período de tiempo al pasar un número en lugar de un booleano. El número se considerará el tiempo de espera del caché en segundos (puede usar coma flotante).

Nota : Una excepción sobre el almacenamiento en caché es que no se usará si no se guarda una instancia. Por ejemplo, si busca una Persona y luego la cambia, mientras no se guarde, no se pasará de Caché.

Crear elementos

Model.createSync(items)

Para insertar nuevos elementos en la base de datos, use 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 artículos

Cada elemento devuelto tiene las propiedades que se definieron para el Modelo y también un par de métodos que puede 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!");

La actualización y luego guardar una instancia se puede hacer en una sola llamada:

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

Si desea eliminar una instancia, simplemente haga lo siguiente:

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

Validaciones

Ver información en la wiki .

Asociaciones

Una asociación es una relación entre una o más tablas.

hasOne

Es una relación de muchos a uno . Es lo mismo que pertenece.
Por ejemplo: Animal.hasOne('owner', Person) .
El animal solo puede tener un dueño, pero la persona puede tener muchos animales.
Animal tendrá la propiedad owner_id agregada automáticamente.

Las siguientes funciones estarán disponibles:

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

Encontrar cadena

La asociación hasOne también es compatible con la búsqueda en cadena. Usando el ejemplo anterior, podemos hacer esto para acceder a una nueva instancia de un objeto ChainFind:

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

Acceso inverso

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

agregará lo siguiente:

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

hasMany

Es una relación de muchos a muchos (incluye tabla de unión).
Por ejemplo: Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true }) .
El paciente puede tener muchos médicos diferentes, cada médico puede tener muchos pacientes diferentes.

Esto creará una tabla de unión patient_doctors cuando llame a Patient.sync() :

nombre de columna tipo
id_paciente Entero (clave compuesta)
doctor_id Entero (clave compuesta)
porque varchar (255)

Las siguientes funciones estarán disponibles:

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 agregará {patient_id: 4, doctor_id: 6, why: "remove appendix"} a la tabla de unión.

getAccessor

Este ChainFind acceso en este tipo de asociación devuelve un ChainFind si no pasa una devolución de llamada. Esto significa que puede hacer cosas como:

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

extendsTo

Si desea dividir quizás propiedades opcionales en diferentes tablas o colecciones. Cada extensión estará en una nueva tabla, donde el identificador único de cada fila es el ID de la instancia del modelo principal. Por ejemplo:

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

Esto creará una tabla person con columnas id y name . La extensión creará una tabla person_address con columnas person_id , street y number . Los métodos disponibles en el modelo Person son similares a una asociación hasOne . En este ejemplo usted podría llamar .getAddress(cb) , .setAddress(Address, cb) , ..

Nota: no tiene que guardar el resultado de Person.extendsTo . Devuelve un modelo extendido. Puede usarlo para consultar directamente esta tabla extendida (e incluso encontrar el modelo relacionado) pero eso depende de usted. Si solo desea para acceder usando el modelo original, simplemente puede descartar la devolución.

Examples & options

Si tiene una relación de 1 a n, debe usar la hasOne (pertenece 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

Puede marcar el campo owner_id como se requiere en la base de datos especificando la opción required :

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

Si no se requiere un campo, pero debe validarse incluso si no está presente, especifique la opción alwaysValidate (esto puede suceder, por ejemplo, cuando la validación de un campo nulo depende de otros campos en el registro)

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

Si prefiere usar otro nombre para el campo (owner_id), puede cambiar este parámetro en la 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: Esto debe hacerse antes de que se especifique la asociación.

Las asociaciones hasMany pueden tener propiedades adicionales en la tabla de asociación.

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

Si lo prefiere, puede activar autoFetch . De esta forma, las asociaciones se obtienen automáticamente cuando obtiene o encuentra instancias de un 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

También puede definir esta opción globalmente en lugar 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 });

Las asociaciones pueden realizar llamadas al Modelo asociado mediante el uso de la opción reverse . Por ejemplo, si tiene una asociación de Modelo A a Modelo B, puede crear un descriptor de acceso en el Modelo B para obtener instancias del Modelo A. ¿Confuso? Mire el siguiente ejemplo.

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

Esto tiene aún más sentido cuando se tienen hasMany asociaciones, ya que puede administrar las asociaciones de muchos a muchos de ambos 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 transacciones

Puede utilizar la función de transacción de bajo nivel para procesar la transcripción de db.

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

O puede usar trans para procesarlo fácilmente.

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

Agregar adaptadores de bases de datos externas

Para agregar un adaptador de base de datos externo a orm , llame al método addAdapter , pasando el alias que se usará para conectarse con este adaptador, junto con el constructor del adaptador:

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

Consulte the documentation for creating adapters para obtener más detalles.