Skip to content

Commit fd2db1f

Browse files
committed
更新 Knex 驱动的数据库初始化逻辑,添加表和列的创建功能
1 parent d659208 commit fd2db1f

File tree

4 files changed

+123
-2
lines changed

4 files changed

+123
-2
lines changed

examples/apps/nestjs/src/objectql/objectql.instance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const objectql = new ObjectQL({
55
datasources: {
66
default: new KnexDriver({
77
client: 'pg',
8-
connection: process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/objectql_example'
8+
connection: process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/objectql'
99
})
1010
},
1111
packages: [

packages/core/src/driver.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export interface Driver {
66
delete(objectName: string, id: string | number, options?: any): Promise<any>;
77
count(objectName: string, filters: any, options?: any): Promise<number>;
88

9+
// Schema / Lifecycle
10+
init?(objects: any[]): Promise<void>;
11+
912
// Advanced
1013
aggregate?(objectName: string, query: any, options?: any): Promise<any>;
1114
distinct?(objectName: string, field: string, filters?: any, options?: any): Promise<any[]>;

packages/core/src/index.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,23 @@ export class ObjectQL implements IObjectQL {
133133
}
134134

135135
async init() {
136-
const ctx = this.createContext({ isSystem: true });
137136
const objects = this.metadata.list<ObjectConfig>('object');
137+
138+
// 1. Init Drivers (e.g. Sync Schema)
139+
// We probably should pass all objects to all drivers, or split them by datasource if objects were assigned to specific DS.
140+
// For now, assume all objects go to default driver or driver handles filtering if needed (but driver doesn't know mapping usually).
141+
// Actually, ObjectQL currently doesn't seem to support multiple datasources per object mapping strictly yet (default is used).
142+
// Let's pass all objects to all configured drivers.
143+
for (const [name, driver] of Object.entries(this.datasources)) {
144+
if (driver.init) {
145+
console.log(`Initializing driver '${name}'...`);
146+
await driver.init(objects);
147+
}
148+
}
149+
150+
const ctx = this.createContext({ isSystem: true });
151+
152+
// 2. Init Data
138153
for (const obj of objects) {
139154
if (obj.data && obj.data.length > 0) {
140155
console.log(`Initializing data for object ${obj.name}...`);

packages/driver-knex/src/index.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import knex, { Knex } from 'knex';
33

44
export class KnexDriver implements Driver {
55
private knex: Knex;
6+
private config: any;
67

78
constructor(config: any) {
9+
this.config = config;
810
this.knex = knex(config);
911
}
1012

@@ -167,5 +169,106 @@ export class KnexDriver implements Driver {
167169
if(filters) this.applyFilters(builder, filters);
168170
return await builder.delete();
169171
}
172+
173+
async init(objects: any[]): Promise<void> {
174+
await this.ensureDatabaseExists();
175+
176+
for (const obj of objects) {
177+
const tableName = obj.name;
178+
const exists = await this.knex.schema.hasTable(tableName);
179+
180+
if (!exists) {
181+
await this.knex.schema.createTable(tableName, (table) => {
182+
table.string('id').primary();
183+
table.timestamp('created_at').defaultTo(this.knex.fn.now());
184+
table.timestamp('updated_at').defaultTo(this.knex.fn.now());
185+
if (obj.fields) {
186+
for (const [name, field] of Object.entries(obj.fields)) {
187+
this.createColumn(table, name, field);
188+
}
189+
}
190+
});
191+
console.log(`[KnexDriver] Created table '${tableName}'`);
192+
} else {
193+
const columnInfo = await this.knex(tableName).columnInfo();
194+
const existingColumns = Object.keys(columnInfo);
195+
196+
await this.knex.schema.alterTable(tableName, (table) => {
197+
if (obj.fields) {
198+
for (const [name, field] of Object.entries(obj.fields)) {
199+
if (!existingColumns.includes(name)) {
200+
this.createColumn(table, name, field);
201+
console.log(`[KnexDriver] Added column '${name}' to '${tableName}'`);
202+
}
203+
}
204+
}
205+
});
206+
}
207+
}
208+
}
209+
210+
private createColumn(table: Knex.CreateTableBuilder, name: string, field: any) {
211+
const type = field.type || 'string';
212+
let col;
213+
switch(type) {
214+
case 'string': col = table.string(name); break;
215+
case 'text': col = table.text(name); break;
216+
case 'integer':
217+
case 'int': col = table.integer(name); break;
218+
case 'float':
219+
case 'number': col = table.float(name); break;
220+
case 'boolean': col = table.boolean(name); break;
221+
case 'date': col = table.date(name); break;
222+
case 'datetime': col = table.timestamp(name); break;
223+
case 'json':
224+
case 'object':
225+
case 'array': col = table.json(name); break;
226+
default: col = table.string(name);
227+
}
228+
}
229+
230+
private async ensureDatabaseExists() {
231+
if (this.config.client !== 'pg' && this.config.client !== 'postgresql') return;
232+
233+
try {
234+
await this.knex.raw('SELECT 1');
235+
} catch (e: any) {
236+
if (e.code === '3D000') { // Database does not exist
237+
await this.createDatabase();
238+
} else {
239+
throw e;
240+
}
241+
}
242+
}
243+
244+
private async createDatabase() {
245+
const config = this.config;
246+
const connection = config.connection;
247+
let dbName = '';
248+
let adminConfig = { ...config };
249+
250+
if (typeof connection === 'string') {
251+
const url = new URL(connection);
252+
dbName = url.pathname.slice(1);
253+
url.pathname = '/postgres';
254+
adminConfig.connection = url.toString();
255+
} else {
256+
dbName = connection.database;
257+
adminConfig.connection = { ...connection, database: 'postgres' };
258+
}
259+
260+
console.log(`[KnexDriver] Database '${dbName}' does not exist. Creating...`);
261+
262+
const adminKnex = knex(adminConfig);
263+
try {
264+
await adminKnex.raw(`CREATE DATABASE "${dbName}"`);
265+
console.log(`[KnexDriver] Database '${dbName}' created successfully.`);
266+
} catch (e: any) {
267+
console.error(`[KnexDriver] Failed to create database '${dbName}':`, e.message);
268+
throw e;
269+
} finally {
270+
await adminKnex.destroy();
271+
}
272+
}
170273
}
171274

0 commit comments

Comments
 (0)