Bỏ qua, đến nội dung

@digiforce-nc/database ​

npm@digiforce-nc/database
Importimport { Database, Collection, Repository } from '@digiforce-nc/database'
RuntimeNode.js
DB SupportPostgreSQL, MySQL, MariaDB, SQLite

Tổng quan ​

@digiforce-nc/database là ORM layer của Digiforce, xây trên Sequelize. Package quản lý:

  • Schema definition - Collection (table metadata) + Field (column metadata)
  • Data access - Repository pattern (CRUD + relation query)
  • Migration - tích hợp Umzug
  • Relation - HasOne, HasMany, BelongsTo, BelongsToMany
  • Filter & Sort - dot-path relation support, custom operators

Ba class cốt lõi tạo thành kiến trúc 3 tầng:

TầngClassTrách nhiệm
MetaDatabaseQuản lý collections, connection, sync, migration, event bus
SchemaCollectionĐịnh nghĩa table: fields, indexes, relations, model options
DataRepositoryCRUD operations, filter parsing, relation loading

Class diagram ​


Database API ​

Constructor ​

typescript
const db = new Database({
  dialect: 'postgres',
  host: 'localhost',
  port: 5432,
  database: 'my_db',
  username: 'admin',
  password: 'secret',
  logging: false,
});

Constructor thực hiện:

  1. Validate dialect (postgres, mysql, sqlite, mariadb)
  2. Setup logger cho DB queries
  3. Chuẩn hóa timezone theo dialect
  4. Khởi tạo Sequelize instance
  5. Đăng ký built-in field types, operators, interfaces
  6. Bind listener afterDefineCollection → resolve pending relations

Collection management ​

MethodMô tả
collection(options)Tạo collection mới. Emit beforeDefineCollection → tạo → emit afterDefineCollection.
extendCollection(options)Mở rộng collection đã có (thêm field, index). Nếu chưa tồn tại, lưu vào queue chờ.
getCollection(name)Lấy collection instance theo tên.
removeCollection(name)Xóa collection khỏi registry. Emit afterRemoveCollection.
import({ directory, from })Load tất cả collection definition từ thư mục. Module extend → extendCollection, module thường → collection().

Khi nào dùng extendCollection?

Plugin thường dùng extendCollection để thêm field vào collection của plugin khác mà không sửa source gốc.

typescript
db.extendCollection({
  name: 'users',
  fields: [
    { type: 'date', name: 'lastAuditAt' },
  ],
});

Database operations ​

MethodMô tả
sync(options?)Đồng bộ schema → DB (tạo/sửa bảng). Options: force, alter.
clean(options?)Xóa tất cả data trong DB (truncate).
close()Đóng connection pool.
auth()Kiểm tra kết nối DB.
reconnect()Đóng và mở lại connection.
runSQL(sql, options?)Chạy raw SQL query.

Migration ​

MethodMô tả
addMigrations(options)Đăng ký migration files. Tích hợp Umzug engine.
typescript
db.addMigrations({
  namespace: 'my-plugin',
  directory: path.resolve(__dirname, './migrations'),
  context: { db },
});

Events ​

MethodMô tả
on(event, listener)Lắng nghe DB-level event.
EventKhi nào
beforeDefineCollectionTrước khi tạo collection
afterDefineCollectionSau khi tạo collection
afterRemoveCollectionSau khi xóa collection
beforeSyncTrước khi sync schema
afterSyncSau khi sync schema

Collection API ​

Collection đại diện một bảng trong database, chứa metadata về fields, indexes, relations.

Định nghĩa collection ​

typescript
const tasks = db.collection({
  name: 'tasks',
  fields: [
    { type: 'string', name: 'title', required: true },
    { type: 'text', name: 'description' },
    { type: 'boolean', name: 'completed', defaultValue: false },
    { type: 'date', name: 'dueDate' },
    { type: 'integer', name: 'priority', defaultValue: 0 },
    { type: 'belongsTo', name: 'assignee', target: 'users' },
    { type: 'hasMany', name: 'comments', target: 'comments' },
  ],
});

Field management ​

MethodMô tả
addField(name, options)Thêm field vào collection. Trả về Field instance.
removeField(name)Xóa field.
setFields(fields)Set toàn bộ fields (replace).
hasField(name)Kiểm tra field tồn tại.
getField(name)Lấy field instance.

Properties ​

PropertyKiểuMô tả
fieldsMap<string, Field>Tất cả field definitions
repositoryRepositoryRepository instance cho collection này
modelModelSequelize Model tương ứng
namestringTên collection (= tên bảng)
dbDatabaseTham chiếu ngược Database

Repository API ​

Repository cung cấp data access layer chuẩn hóa cho mỗi collection.

Query methods ​

MethodMô tả
find(options?)Tìm nhiều record. Trả về array.
findOne(options?)Tìm một record.
findAndCount(options?)Tìm + đếm tổng (cho pagination). Trả về [rows, count].
count(options?)Đếm số record thỏa filter.

Mutation methods ​

MethodMô tả
create(options)Tạo record mới.
update(options)Cập nhật record.
destroy(options)Xóa record.
firstOrCreate(options)Tìm record đầu tiên, tạo mới nếu không có.
updateOrCreate(options)Tìm và cập nhật, tạo mới nếu không có.

Options phổ biến ​

typescript
interface FindOptions {
  filter?: FilterType;       // Điều kiện lọc
  fields?: string[];         // Chọn fields (SELECT)
  except?: string[];         // Loại trừ fields
  appends?: string[];        // Load relation
  sort?: string[];           // Sắp xếp: ['createdAt'] hoặc ['-createdAt']
  limit?: number;            // Giới hạn
  offset?: number;           // Bỏ qua N record đầu
  filterByTk?: any;          // Filter theo primary key
  context?: any;             // Koa context (cho hook)
  transaction?: Transaction; // Sequelize transaction
}

Ví dụ sử dụng Repository ​

typescript
const repo = db.getRepository('tasks');

// Tìm tất cả task chưa hoàn thành, kèm thông tin assignee
const tasks = await repo.find({
  filter: {
    completed: false,
    'assignee.department': 'engineering',
  },
  appends: ['assignee', 'comments'],
  sort: ['-priority', 'dueDate'],
  limit: 20,
});

// Tạo task mới
const task = await repo.create({
  values: {
    title: 'Review PR #42',
    assignee: 1,
    priority: 5,
  },
});

// Cập nhật theo primary key
await repo.update({
  filterByTk: task.id,
  values: { completed: true },
});

// Tạo hoặc cập nhật (upsert)
await repo.updateOrCreate({
  filterKeys: ['title'],
  values: { title: 'Daily standup', priority: 3 },
});

// Xóa theo filter
await repo.destroy({
  filter: { completed: true, updatedAt: { $lt: '2024-01-01' } },
});

Relation Repositories ​

Mỗi kiểu relation có repository chuyên biệt cho thao tác qua association:

RepositoryRelationThao tác đặc biệt
HasOneRepository1:1 owner sideset(), remove()
HasManyRepository1:Nadd(), remove(), set(), toggle()
BelongsToRepositoryN:1set(), remove()
BelongsToManyRepositoryM:Nadd(), remove(), set(), toggle()
typescript
// HasMany: thêm comment vào task
const commentsRepo = db.getRepository('tasks.comments', taskId);
await commentsRepo.create({
  values: { content: 'LGTM!', author: userId },
});

// BelongsToMany: gắn tags
const tagsRepo = db.getRepository('tasks.tags', taskId);
await tagsRepo.add([tagId1, tagId2]);
await tagsRepo.toggle(tagId3); // thêm nếu chưa có, bỏ nếu đã có

Field types ​

TypeCategoryMô tả
stringScalarVARCHAR
textScalarTEXT (không giới hạn)
integerScalarINTEGER
bigIntScalarBIGINT
floatScalarFLOAT
doubleScalarDOUBLE
decimalScalarDECIMAL
booleanScalarBOOLEAN
dateScalarDATEONLY
dateTimeScalarDATE (có time)
timeScalarTIME
jsonScalarJSON / JSONB
jsonbScalarJSONB (PostgreSQL)
arrayScalarARRAY (PostgreSQL)
uuidScalarUUID
uidScalarUID (nanoid)
enumScalarENUM
setScalarSET (MySQL)
passwordScalarHashed password field
sortScalarSort order field
virtualVirtualKhông lưu DB
formulaVirtualTính toán từ field khác
belongsToRelationN:1
hasOneRelation1:1
hasManyRelation1:N
belongsToManyRelationM:N (qua junction table)

FilterParser ​

FilterParser chuyển đổi filter object thành Sequelize where clause, hỗ trợ:

Dot-path relation support ​

Filter tự động resolve qua relation:

typescript
// Filter theo field của relation lồng nhau
const filter = {
  'assignee.department.name': 'Engineering',
  'tags.name.$includes': 'urgent',
};

Operators ​

OperatorSQL tương đươngVí dụ
$eq={ age: { $eq: 18 } }
$ne!={ status: { $ne: 'deleted' } }
$gt>{ price: { $gt: 100 } }
$gte>={ price: { $gte: 100 } }
$lt<{ age: { $lt: 30 } }
$lte<={ age: { $lte: 30 } }
$inIN{ status: { $in: ['active', 'pending'] } }
$notInNOT IN{ role: { $notIn: ['admin'] } }
$likeLIKE{ name: { $like: '%john%' } }
$notLikeNOT LIKE{ name: { $notLike: '%test%' } }
$includesLIKE %v%{ title: { $includes: 'report' } }
$startsWithLIKE v%{ code: { $startsWith: 'PRJ' } }
$endsWithLIKE %v{ email: { $endsWith: '@company.com' } }
$existsIS NOT NULL / IS NULL{ avatar: { $exists: true } }
$empty= '' OR IS NULL{ bio: { $empty: true } }
$betweenBETWEEN{ age: { $between: [18, 60] } }

Logical operators ​

typescript
const filter = {
  $and: [
    { status: 'active' },
    {
      $or: [
        { priority: { $gte: 5 } },
        { 'assignee.role': 'manager' },
      ],
    },
  ],
};

Migration support ​

Tích hợp Umzug cho database migration:

typescript
db.addMigrations({
  namespace: 'my-plugin',
  directory: path.resolve(__dirname, './migrations'),
  context: { db },
});

// Migration file: migrations/20240101-add-status-field.ts
export async function up(db) {
  const collection = db.getCollection('tasks');
  collection.addField('status', {
    type: 'enum',
    values: ['draft', 'active', 'archived'],
    defaultValue: 'draft',
  });
  await collection.sync({ alter: true });
}

export async function down(db) {
  const collection = db.getCollection('tasks');
  collection.removeField('status');
  await collection.sync({ alter: true });
}

Re-exports từ Sequelize ​

Package re-export các utility phổ dùng từ Sequelize:

typescript
import { Op, DataTypes, Transaction, Sequelize } from '@digiforce-nc/database';

// Sử dụng Op trong raw query
const results = await model.findAll({
  where: {
    createdAt: { [Op.gte]: new Date('2024-01-01') },
  },
});

// Transaction
await db.sequelize.transaction(async (transaction) => {
  await repo.create({ values: {...}, transaction });
  await repo.update({ filterByTk: id, values: {...}, transaction });
});
ExportMô tả
OpSequelize operators (Op.and, Op.or, Op.gte, …)
DataTypesColumn type definitions
TransactionTransaction class
SequelizeSequelize class

Ví dụ tổng hợp ​

typescript
import { Database } from '@digiforce-nc/database';

const db = new Database({
  dialect: 'postgres',
  host: 'localhost',
  database: 'myapp',
  username: 'admin',
  password: 'secret',
});

// Định nghĩa collection
db.collection({
  name: 'projects',
  fields: [
    { type: 'string', name: 'name' },
    { type: 'text', name: 'description' },
    { type: 'belongsTo', name: 'owner', target: 'users' },
    { type: 'hasMany', name: 'tasks', target: 'tasks' },
    { type: 'belongsToMany', name: 'members', target: 'users', through: 'project_members' },
  ],
});

db.collection({
  name: 'tasks',
  fields: [
    { type: 'string', name: 'title' },
    { type: 'boolean', name: 'completed', defaultValue: false },
    { type: 'integer', name: 'priority' },
    { type: 'belongsTo', name: 'project', target: 'projects' },
  ],
});

// Sync schema
await db.sync();

// Query
const projects = await db.getRepository('projects').find({
  filter: { 'owner.role': 'admin' },
  appends: ['tasks', 'members'],
  sort: ['-createdAt'],
});

Xem thêm ​