Замечательный модуль сообщества

Объектно-реляционное отображение для fibjs

Статус сборки

Установить

1
npm install fib-orm

Тестовое задание

1
npm run ci

Поддержка СУБД

  • 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 не добавлял новых функций, при разработке документов можно ссылаться на node-orm, нужно только изменить асинхронный вызов на синхронную версию wiki .

Настройки

См. Информацию в вики .

Подключение

См. Информацию в вики .

Модели

Модель - это абстракция над одной или несколькими таблицами базы данных. Модели поддерживают ассоциации (подробнее см. Ниже). Предполагается, что имя модели соответствует имени таблицы.

Модели поддерживают поведение для доступа к табличным данным и управления ими.

Определение моделей

См. Информацию в вики .

Properties

См. Информацию в вики .

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

Синхронизация моделей

См. Информацию в вики .

Отбрасывание моделей

См. Информацию в вики .

Расширенные настройки

ORM2 позволяет вам продвинуть некоторые дополнительные настройки в определениях вашей модели. Вы можете настроить их через настройки или в вызове defineпри настройке модели.

Например, каждый экземпляр модели имеет уникальный идентификатор в базе данных. Этот столбец таблицы добавляется автоматически и по умолчанию называется «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 });

Модель питомца будет иметь 2 столбца: an UIDи a name.

Также возможны составные ключи:

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

Другие варианты:

  • identityCache : (по умолчанию :) falseУстановите trueдля включения кеширования идентификационных данных ( Синглтоны ) или установите значение тайм-аута (в секундах);
  • autoSave : (по умолчанию :) falseУстановите trueдля сохранения экземпляра сразу после изменения любого свойства;
  • autoFetch : (по умолчанию :) falseУстановите trueдля получения ассоциаций при выборке экземпляра из базы данных;
  • autoFetchLimit: (по умолчанию :) 1Если autoFetchвключено, это определяет, сколько обручей (ассоциаций ассоциаций) вы хотите, чтобы он автоматически извлекал.

Крючки

См. Информацию в вики .

Нахождение предметов

Model.getSync(id, [ options ])

Чтобы получить конкретный элемент из базы данных, используйте Model.get.

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

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

У поиска одного или нескольких элементов есть несколько вариантов, каждый из которых может быть указан без определенного порядка параметров. Только 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)

Есть и другие параметры, которые можно передать, чтобы что-то найти. Эти параметры передаются во втором объекте:

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(): вы можете передать число в качестве ограничения или два числа в качестве смещения и ограничения соответственно
  • .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

Если вы хотите пропустить только одно или два свойства, вы можете вызвать .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

Вы можете использовать шаблон идентификации (по умолчанию отключен). Если он включен, несколько разных запросов приведут к одному и тому же результату - вы получите один и тот же объект. Если у вас есть другие системы, которые могут изменить вашу базу данных, или вам нужно вызвать какое-то руководство 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);

Кэш удостоверений можно настроить так, чтобы он истекал через определенный период времени, передав число вместо логического. Число будет считаться тайм-аутом кеша в секундах (можно использовать числа с плавающей запятой).

Примечание . Одним из исключений кеширования является то, что оно не будет использоваться, если экземпляр не сохранен. Например, если вы извлекаете объект Person, а затем изменяете его, пока он не будет сохранен, он не будет передан из Cache.

Создание предметов

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

Валидации

См. Информацию в вики .

Ассоциации

Ассоциация - это связь между одной или несколькими таблицами.

hasOne

A МНОЖЕСТВО к одному . Это отношение одновременно, КАК принадлежит к.
Например: Animal.hasOne('owner', Person)...
Животное МОЖЕТ иметь только одного владельца, иметь МНОГО МОЖЕТ, но лицо, которое
животное животное по желанию будет иметь в 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

А является многими ко многим отношений (включает Join Table).
Например , из: Patient.hasMany('doctors', Doctor, { why: String }, { reverse: 'patients', key: true }).
. Пациента Каждого врача может иметь доктора есть МНОГО CAN Различные Различные пациенты имеют много.

Это создаст объединенную таблицу patient_doctorsпри вызове Patient.sync():

имя столбца тип
пациент_ид Целое число (составной ключ)
doctor_id Целое число (составной ключ)
Почему варчар (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

Если вы хотите разделить необязательные свойства на разные таблицы или коллекции. Каждое расширение будет в новой таблице, где уникальный идентификатор каждой строки является идентификатором основного экземпляра модели. Например:

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, streetи number. Доступные в методах Personмодели, аналогичные AN, являются 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

Вы можете пометить owner_idполе как обязательное в базе данных, указав requiredопцию:

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

Если поле не является обязательным, но должно быть проверено, даже если оно отсутствует, укажите alwaysValidateопцию. (Это может произойти, например, когда проверка пустого поля зависит от других полей в записи)

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

Сопровождение сделки

Вы можете использовать функцию транзакции низкого уровня для обработки транзакции базы данных.

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 Больше подробностей.