@mantiq/database 0.1.4 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/DatabaseManager.ts +10 -11
- package/src/contracts/Connection.ts +15 -2
- package/src/drivers/BaseSQLConnection.ts +111 -0
- package/src/drivers/MSSQLConnection.ts +15 -19
- package/src/drivers/MongoConnection.ts +289 -114
- package/src/drivers/MongoQueryBuilderImpl.ts +4 -4
- package/src/drivers/MySQLConnection.ts +17 -23
- package/src/drivers/PostgresConnection.ts +17 -24
- package/src/drivers/SQLiteConnection.ts +4 -10
- package/src/errors/ConnectionError.ts +5 -5
- package/src/errors/DriverNotSupportedError.ts +6 -0
- package/src/index.ts +6 -1
- package/src/orm/Document.ts +14 -13
- package/src/orm/Model.ts +11 -2
- package/src/orm/ModelQueryBuilder.ts +8 -7
- package/src/query/Builder.ts +44 -51
- package/src/schema/Blueprint.ts +3 -3
- package/src/schema/ColumnDefinition.ts +5 -5
- package/src/schema/SchemaBuilder.ts +5 -2
- package/src/types.d.ts +21 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { DatabaseConnection } from '../contracts/Connection.ts'
|
|
2
|
+
import type { SchemaBuilder } from '../schema/SchemaBuilder.ts'
|
|
1
3
|
import type {
|
|
2
|
-
MongoDatabaseConnection,
|
|
3
4
|
MongoCollectionContract,
|
|
4
5
|
MongoFilter,
|
|
5
6
|
MongoUpdateDoc,
|
|
@@ -10,159 +11,160 @@ import type {
|
|
|
10
11
|
MongoDeleteResult,
|
|
11
12
|
MongoQueryBuilder,
|
|
12
13
|
} from '../contracts/MongoConnection.ts'
|
|
14
|
+
import type { QueryState, WhereClause } from '../query/Builder.ts'
|
|
15
|
+
import { QueryBuilder } from '../query/Builder.ts'
|
|
16
|
+
import { Expression } from '../query/Expression.ts'
|
|
13
17
|
import { MongoQueryBuilderImpl } from './MongoQueryBuilderImpl.ts'
|
|
14
18
|
import { ConnectionError } from '../errors/ConnectionError.ts'
|
|
15
|
-
import {
|
|
19
|
+
import { DriverNotSupportedError } from '../errors/DriverNotSupportedError.ts'
|
|
16
20
|
|
|
17
21
|
export interface MongoConfig {
|
|
18
22
|
uri: string
|
|
19
23
|
database: string
|
|
20
|
-
options?: Record<string, any>
|
|
24
|
+
options?: Record<string, any> | undefined
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
constructor(
|
|
25
|
-
private readonly col: any,
|
|
26
|
-
private readonly name: string,
|
|
27
|
-
) {}
|
|
27
|
+
// ── Operator translation map ──────────────────────────────────────────────────
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return cursor.toArray()
|
|
39
|
-
},
|
|
40
|
-
async (f) => this.col.countDocuments(f),
|
|
41
|
-
)
|
|
42
|
-
}
|
|
29
|
+
const OPERATOR_MAP: Record<string, string> = {
|
|
30
|
+
'=': '$eq',
|
|
31
|
+
'!=': '$ne',
|
|
32
|
+
'<>': '$ne',
|
|
33
|
+
'>': '$gt',
|
|
34
|
+
'>=': '$gte',
|
|
35
|
+
'<': '$lt',
|
|
36
|
+
'<=': '$lte',
|
|
37
|
+
}
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
return this.col.findOne(filter)
|
|
46
|
-
}
|
|
39
|
+
// ── MongoConnection — implements the universal DatabaseConnection interface ──
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
export class MongoConnection implements DatabaseConnection {
|
|
42
|
+
private client: any = null
|
|
43
|
+
private db: any = null
|
|
44
|
+
private config: MongoConfig
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return { insertedId: result.insertedId, acknowledged: result.acknowledged }
|
|
46
|
+
constructor(config: MongoConfig) {
|
|
47
|
+
this.config = config
|
|
56
48
|
}
|
|
57
49
|
|
|
58
|
-
async
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
private async getDb(): Promise<any> {
|
|
51
|
+
if (!this.db) {
|
|
52
|
+
try {
|
|
53
|
+
const mongoModule = 'mongodb'
|
|
54
|
+
const { MongoClient } = await import(/* webpackIgnore: true */ mongoModule)
|
|
55
|
+
this.client = new MongoClient(this.config.uri, this.config.options ?? {})
|
|
56
|
+
await this.client.connect()
|
|
57
|
+
this.db = this.client.db(this.config.database)
|
|
58
|
+
} catch (e: any) {
|
|
59
|
+
throw new ConnectionError(`MongoDB connection failed: ${e.message}`, 'mongodb', e)
|
|
60
|
+
}
|
|
64
61
|
}
|
|
62
|
+
return this.db
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
const result = await this.col.updateOne(filter, update)
|
|
69
|
-
return { matchedCount: result.matchedCount, modifiedCount: result.modifiedCount, acknowledged: result.acknowledged }
|
|
70
|
-
}
|
|
65
|
+
// ── Universal executeXxx methods ──────────────────────────────────────────
|
|
71
66
|
|
|
72
|
-
async
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
67
|
+
async executeSelect(state: QueryState): Promise<Record<string, any>[]> {
|
|
68
|
+
this.guardNoJoins(state)
|
|
69
|
+
this.guardNoHavings(state)
|
|
76
70
|
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
71
|
+
const db = await this.getDb()
|
|
72
|
+
const col = db.collection(state.table)
|
|
73
|
+
const filter = this.translateWheres(state.wheres)
|
|
74
|
+
const projection = this.translateColumns(state.columns)
|
|
75
|
+
const sort = this.translateOrders(state.orders)
|
|
76
|
+
|
|
77
|
+
let cursor = col.find(filter)
|
|
78
|
+
if (projection) cursor = cursor.project(projection)
|
|
79
|
+
if (sort) cursor = cursor.sort(sort)
|
|
80
|
+
if (state.offsetValue !== null) cursor = cursor.skip(state.offsetValue)
|
|
81
|
+
if (state.limitValue !== null) cursor = cursor.limit(state.limitValue)
|
|
82
|
+
|
|
83
|
+
return cursor.toArray()
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
async
|
|
83
|
-
const
|
|
84
|
-
|
|
86
|
+
async executeInsert(table: string, data: Record<string, any>): Promise<number> {
|
|
87
|
+
const db = await this.getDb()
|
|
88
|
+
const result = await db.collection(table).insertOne(data)
|
|
89
|
+
return result.acknowledged ? 1 : 0
|
|
85
90
|
}
|
|
86
91
|
|
|
87
|
-
async
|
|
88
|
-
const
|
|
89
|
-
|
|
92
|
+
async executeInsertGetId(table: string, data: Record<string, any>): Promise<number | string> {
|
|
93
|
+
const db = await this.getDb()
|
|
94
|
+
const result = await db.collection(table).insertOne(data)
|
|
95
|
+
const id = result.insertedId
|
|
96
|
+
return typeof id === 'object' ? id.toString() : id
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
async
|
|
93
|
-
const
|
|
94
|
-
|
|
99
|
+
async executeUpdate(table: string, state: QueryState, data: Record<string, any>): Promise<number> {
|
|
100
|
+
const db = await this.getDb()
|
|
101
|
+
const filter = this.translateWheres(state.wheres)
|
|
102
|
+
const result = await db.collection(table).updateMany(filter, { $set: data })
|
|
103
|
+
return result.modifiedCount
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
async
|
|
98
|
-
|
|
106
|
+
async executeDelete(table: string, state: QueryState): Promise<number> {
|
|
107
|
+
const db = await this.getDb()
|
|
108
|
+
const filter = this.translateWheres(state.wheres)
|
|
109
|
+
const result = await db.collection(table).deleteMany(filter)
|
|
110
|
+
return result.deletedCount
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
async
|
|
102
|
-
|
|
113
|
+
async executeTruncate(table: string): Promise<void> {
|
|
114
|
+
const db = await this.getDb()
|
|
115
|
+
await db.collection(table).deleteMany({})
|
|
103
116
|
}
|
|
104
117
|
|
|
105
|
-
async
|
|
106
|
-
|
|
118
|
+
async executeAggregate(state: QueryState, fn: 'count' | 'sum' | 'avg' | 'min' | 'max', column: string): Promise<number> {
|
|
119
|
+
const db = await this.getDb()
|
|
120
|
+
const filter = this.translateWheres(state.wheres)
|
|
121
|
+
|
|
122
|
+
if (fn === 'count') {
|
|
123
|
+
return db.collection(state.table).countDocuments(filter)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const aggOp = `$${fn}`
|
|
127
|
+
const aggField = column === '*' ? 1 : `$${column}`
|
|
128
|
+
const pipeline: any[] = [
|
|
129
|
+
{ $match: filter },
|
|
130
|
+
{ $group: { _id: null, result: { [aggOp]: aggField } } },
|
|
131
|
+
]
|
|
132
|
+
const [row] = await db.collection(state.table).aggregate(pipeline).toArray()
|
|
133
|
+
return Number(row?.result ?? 0)
|
|
107
134
|
}
|
|
108
135
|
|
|
109
|
-
async
|
|
110
|
-
|
|
136
|
+
async executeExists(state: QueryState): Promise<boolean> {
|
|
137
|
+
const db = await this.getDb()
|
|
138
|
+
const filter = this.translateWheres(state.wheres)
|
|
139
|
+
const count = await db.collection(state.table).countDocuments(filter, { limit: 1 })
|
|
140
|
+
return count > 0
|
|
111
141
|
}
|
|
112
|
-
}
|
|
113
142
|
|
|
114
|
-
|
|
115
|
-
private client: any = null
|
|
116
|
-
private db: any = null
|
|
117
|
-
private config: MongoConfig
|
|
143
|
+
// ── Raw SQL methods — throw on MongoDB ────────────────────────────────────
|
|
118
144
|
|
|
119
|
-
|
|
120
|
-
|
|
145
|
+
async select(sql: string, bindings?: any[]): Promise<Record<string, any>[]> {
|
|
146
|
+
throw new DriverNotSupportedError('mongodb', 'raw SQL queries')
|
|
121
147
|
}
|
|
122
148
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const { MongoClient } = await import('mongodb')
|
|
127
|
-
this.client = new MongoClient(this.config.uri, this.config.options ?? {})
|
|
128
|
-
await this.client.connect()
|
|
129
|
-
this.db = this.client.db(this.config.database)
|
|
130
|
-
} catch (e: any) {
|
|
131
|
-
throw new ConnectionError(`MongoDB connection failed: ${e.message}`, 'mongodb', e)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return this.db
|
|
149
|
+
async statement(sql: string, bindings?: any[]): Promise<number> {
|
|
150
|
+
throw new DriverNotSupportedError('mongodb', 'raw SQL queries')
|
|
135
151
|
}
|
|
136
152
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const self = this
|
|
140
|
-
const col = {
|
|
141
|
-
async getCol() {
|
|
142
|
-
const db = await self.getDb()
|
|
143
|
-
return db.collection(name)
|
|
144
|
-
},
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// We need to return a proxy that defers the actual collection resolution
|
|
148
|
-
return new LazyMongoCollection(name, async () => {
|
|
149
|
-
const db = await self.getDb()
|
|
150
|
-
return db.collection(name)
|
|
151
|
-
})
|
|
153
|
+
async insertGetId(sql: string, bindings?: any[]): Promise<number | bigint | string> {
|
|
154
|
+
throw new DriverNotSupportedError('mongodb', 'raw SQL queries')
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
// ── Shared interface ──────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
table(name: string): QueryBuilder {
|
|
160
|
+
return new QueryBuilder(this, name)
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const collections = await db.listCollections().toArray()
|
|
162
|
-
return collections.map((c: any) => c.name)
|
|
163
|
+
schema(): SchemaBuilder {
|
|
164
|
+
throw new DriverNotSupportedError('mongodb', 'schema builder (MongoDB is schemaless — use native() for indexes)')
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
async transaction<T>(callback: (conn:
|
|
167
|
+
async transaction<T>(callback: (conn: DatabaseConnection) => Promise<T>): Promise<T> {
|
|
166
168
|
const client = await this.getClient()
|
|
167
169
|
const session = client.startSession()
|
|
168
170
|
try {
|
|
@@ -179,22 +181,194 @@ export class MongoConnection implements MongoDatabaseConnection {
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
|
182
|
-
private async getClient(): Promise<any> {
|
|
183
|
-
await this.getDb()
|
|
184
|
-
return this.client
|
|
185
|
-
}
|
|
186
|
-
|
|
187
184
|
getDriverName(): string {
|
|
188
185
|
return 'mongodb'
|
|
189
186
|
}
|
|
190
187
|
|
|
188
|
+
getTablePrefix(): string {
|
|
189
|
+
return ''
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── Native escape hatch — direct MongoDB access ───────────────────────────
|
|
193
|
+
|
|
194
|
+
/** Returns the native MongoDB collection for advanced operations. */
|
|
195
|
+
collection(name: string): MongoCollectionContract {
|
|
196
|
+
return new LazyMongoCollection(name, async () => {
|
|
197
|
+
const db = await this.getDb()
|
|
198
|
+
return db.collection(name)
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Returns the underlying MongoDB Db instance. */
|
|
203
|
+
async native(): Promise<any> {
|
|
204
|
+
return this.getDb()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async command(command: Record<string, any>): Promise<any> {
|
|
208
|
+
const db = await this.getDb()
|
|
209
|
+
return db.command(command)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async listCollections(): Promise<string[]> {
|
|
213
|
+
const db = await this.getDb()
|
|
214
|
+
const collections = await db.listCollections().toArray()
|
|
215
|
+
return collections.map((c: any) => c.name)
|
|
216
|
+
}
|
|
217
|
+
|
|
191
218
|
async disconnect(): Promise<void> {
|
|
192
219
|
await this.client?.close()
|
|
193
220
|
this.client = null
|
|
194
221
|
this.db = null
|
|
195
222
|
}
|
|
223
|
+
|
|
224
|
+
private async getClient(): Promise<any> {
|
|
225
|
+
await this.getDb()
|
|
226
|
+
return this.client
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── QueryState → MongoDB translation ──────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
private translateWheres(wheres: WhereClause[]): Record<string, any> {
|
|
232
|
+
if (wheres.length === 0) return {}
|
|
233
|
+
|
|
234
|
+
const andClauses: Record<string, any>[] = []
|
|
235
|
+
const orGroups: Record<string, any>[][] = []
|
|
236
|
+
let currentAnd: Record<string, any>[] = []
|
|
237
|
+
|
|
238
|
+
for (const w of wheres) {
|
|
239
|
+
const clause = this.translateSingleWhere(w)
|
|
240
|
+
|
|
241
|
+
if (w.boolean === 'or' && currentAnd.length > 0) {
|
|
242
|
+
// Push accumulated AND clauses as one OR branch
|
|
243
|
+
orGroups.push(currentAnd)
|
|
244
|
+
currentAnd = [clause]
|
|
245
|
+
} else {
|
|
246
|
+
currentAnd.push(clause)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Final group
|
|
251
|
+
if (orGroups.length > 0) {
|
|
252
|
+
orGroups.push(currentAnd)
|
|
253
|
+
return { $or: orGroups.map((group) => group.length === 1 ? group[0]! : { $and: group }) }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (currentAnd.length === 1) return currentAnd[0]!
|
|
257
|
+
return { $and: currentAnd }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private translateSingleWhere(w: WhereClause): Record<string, any> {
|
|
261
|
+
switch (w.type) {
|
|
262
|
+
case 'basic': {
|
|
263
|
+
const col = w.column!
|
|
264
|
+
const op = w.operator ?? '='
|
|
265
|
+
const val = w.value
|
|
266
|
+
|
|
267
|
+
if (op === '=' || op === '$eq') {
|
|
268
|
+
return { [col]: val }
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (op === 'like' || op === 'LIKE') {
|
|
272
|
+
return { [col]: { $regex: this.likeToRegex(val), $options: 'i' } }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (op === 'not like' || op === 'NOT LIKE') {
|
|
276
|
+
return { [col]: { $not: { $regex: this.likeToRegex(val), $options: 'i' } } }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const mongoOp = OPERATOR_MAP[op]
|
|
280
|
+
if (mongoOp) {
|
|
281
|
+
return { [col]: { [mongoOp]: val } }
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
throw new DriverNotSupportedError('mongodb', `operator "${op}"`)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
case 'in':
|
|
288
|
+
return { [w.column!]: { $in: w.values! } }
|
|
289
|
+
|
|
290
|
+
case 'notIn':
|
|
291
|
+
return { [w.column!]: { $nin: w.values! } }
|
|
292
|
+
|
|
293
|
+
case 'null':
|
|
294
|
+
return { [w.column!]: null }
|
|
295
|
+
|
|
296
|
+
case 'notNull':
|
|
297
|
+
return { [w.column!]: { $ne: null } }
|
|
298
|
+
|
|
299
|
+
case 'between':
|
|
300
|
+
return { [w.column!]: { $gte: w.range![0], $lte: w.range![1] } }
|
|
301
|
+
|
|
302
|
+
case 'nested':
|
|
303
|
+
return this.translateWheres(w.nested ?? [])
|
|
304
|
+
|
|
305
|
+
case 'raw':
|
|
306
|
+
throw new DriverNotSupportedError('mongodb', 'whereRaw (use standard where methods instead)')
|
|
307
|
+
|
|
308
|
+
case 'column':
|
|
309
|
+
throw new DriverNotSupportedError('mongodb', 'whereColumn (use $expr in native queries instead)')
|
|
310
|
+
|
|
311
|
+
default:
|
|
312
|
+
throw new DriverNotSupportedError('mongodb', `where type "${w.type}"`)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private translateColumns(columns: (string | Expression)[]): Record<string, 1> | undefined {
|
|
317
|
+
if (columns.length === 1 && columns[0] === '*') return undefined
|
|
318
|
+
if (columns.some((c) => c instanceof Expression)) {
|
|
319
|
+
// Allow expressions only if they're simple strings (column names)
|
|
320
|
+
// For actual SQL expressions, throw
|
|
321
|
+
const hasRealExpressions = columns.some((c) => c instanceof Expression && (c.value.includes('(') || c.value.includes(' ')))
|
|
322
|
+
if (hasRealExpressions) {
|
|
323
|
+
throw new DriverNotSupportedError('mongodb', 'selectRaw with SQL expressions')
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const projection: Record<string, 1> = {}
|
|
328
|
+
for (const col of columns) {
|
|
329
|
+
const name = col instanceof Expression ? col.value : col
|
|
330
|
+
projection[name] = 1
|
|
331
|
+
}
|
|
332
|
+
return projection
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private translateOrders(orders: Array<{ column: string | Expression; direction: 'asc' | 'desc' }>): Record<string, 1 | -1> | undefined {
|
|
336
|
+
if (orders.length === 0) return undefined
|
|
337
|
+
const sort: Record<string, 1 | -1> = {}
|
|
338
|
+
for (const o of orders) {
|
|
339
|
+
if (o.column instanceof Expression) {
|
|
340
|
+
throw new DriverNotSupportedError('mongodb', 'orderBy with raw Expression')
|
|
341
|
+
}
|
|
342
|
+
sort[o.column] = o.direction === 'asc' ? 1 : -1
|
|
343
|
+
}
|
|
344
|
+
return sort
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Converts SQL LIKE pattern to regex: % → .*, _ → . */
|
|
348
|
+
private likeToRegex(pattern: string): string {
|
|
349
|
+
return pattern
|
|
350
|
+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
351
|
+
.replace(/%/g, '.*')
|
|
352
|
+
.replace(/_/g, '.')
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ── Guards ────────────────────────────────────────────────────────────────
|
|
356
|
+
|
|
357
|
+
private guardNoJoins(state: QueryState): void {
|
|
358
|
+
if (state.joins.length > 0) {
|
|
359
|
+
throw new DriverNotSupportedError('mongodb', 'joins (use relationships or native $lookup instead)')
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private guardNoHavings(state: QueryState): void {
|
|
364
|
+
if (state.havings.length > 0) {
|
|
365
|
+
throw new DriverNotSupportedError('mongodb', 'having (use native aggregation pipeline instead)')
|
|
366
|
+
}
|
|
367
|
+
}
|
|
196
368
|
}
|
|
197
369
|
|
|
370
|
+
// ── LazyMongoCollection — deferred collection resolution ────────────────────
|
|
371
|
+
|
|
198
372
|
class LazyMongoCollection implements MongoCollectionContract {
|
|
199
373
|
private _col: any = null
|
|
200
374
|
|
|
@@ -232,7 +406,8 @@ class LazyMongoCollection implements MongoCollectionContract {
|
|
|
232
406
|
}
|
|
233
407
|
|
|
234
408
|
async findById(id: any): Promise<Record<string, any> | null> {
|
|
235
|
-
const
|
|
409
|
+
const mongoModule = 'mongodb'
|
|
410
|
+
const { ObjectId } = await import(/* webpackIgnore: true */ mongoModule)
|
|
236
411
|
return (await this.col()).findOne({ _id: typeof id === 'string' ? new ObjectId(id) : id })
|
|
237
412
|
}
|
|
238
413
|
|
|
@@ -17,10 +17,10 @@ export class MongoQueryBuilderImpl implements MongoQueryBuilder {
|
|
|
17
17
|
private readonly collectionName: string,
|
|
18
18
|
private readonly executor: (opts: {
|
|
19
19
|
filter: MongoFilter
|
|
20
|
-
projection?: MongoProjection
|
|
21
|
-
sort?: MongoSortDoc
|
|
22
|
-
limit?: number
|
|
23
|
-
skip?: number
|
|
20
|
+
projection?: MongoProjection | undefined
|
|
21
|
+
sort?: MongoSortDoc | undefined
|
|
22
|
+
limit?: number | undefined
|
|
23
|
+
skip?: number | undefined
|
|
24
24
|
}) => Promise<Record<string, any>[]>,
|
|
25
25
|
private readonly counter: (filter: MongoFilter) => Promise<number>,
|
|
26
26
|
) {}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DatabaseConnection } from '../contracts/Connection.ts'
|
|
2
2
|
import type { SchemaBuilder } from '../schema/SchemaBuilder.ts'
|
|
3
|
+
import { BaseSQLConnection } from './BaseSQLConnection.ts'
|
|
3
4
|
import { QueryBuilder } from '../query/Builder.ts'
|
|
4
5
|
import { MySQLGrammar } from './MySQLGrammar.ts'
|
|
5
6
|
import { SchemaBuilderImpl } from '../schema/SchemaBuilder.ts'
|
|
@@ -7,27 +8,28 @@ import { ConnectionError } from '../errors/ConnectionError.ts'
|
|
|
7
8
|
import { QueryError } from '../errors/QueryError.ts'
|
|
8
9
|
|
|
9
10
|
export interface MySQLConfig {
|
|
10
|
-
host?: string
|
|
11
|
-
port?: number
|
|
11
|
+
host?: string | undefined
|
|
12
|
+
port?: number | undefined
|
|
12
13
|
database: string
|
|
13
|
-
user?: string
|
|
14
|
-
password?: string
|
|
15
|
-
pool?: { min?: number; max?: number }
|
|
14
|
+
user?: string | undefined
|
|
15
|
+
password?: string | undefined
|
|
16
|
+
pool?: { min?: number | undefined; max?: number | undefined } | undefined
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export class MySQLConnection
|
|
19
|
+
export class MySQLConnection extends BaseSQLConnection {
|
|
19
20
|
readonly _grammar = new MySQLGrammar()
|
|
20
21
|
private pool: any = null
|
|
21
22
|
private config: MySQLConfig
|
|
22
23
|
|
|
23
24
|
constructor(config: MySQLConfig) {
|
|
25
|
+
super()
|
|
24
26
|
this.config = config
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
private async getPool(): Promise<any> {
|
|
28
30
|
if (!this.pool) {
|
|
29
31
|
try {
|
|
30
|
-
const mysql = await import('mysql2/promise')
|
|
32
|
+
const mysql: any = await import('mysql2/promise')
|
|
31
33
|
this.pool = await mysql.createPool({
|
|
32
34
|
host: this.config.host ?? 'localhost',
|
|
33
35
|
port: this.config.port ?? 3306,
|
|
@@ -79,18 +81,18 @@ export class MySQLConnection implements DatabaseConnection {
|
|
|
79
81
|
const conn = await pool.getConnection()
|
|
80
82
|
try {
|
|
81
83
|
await conn.beginTransaction()
|
|
82
|
-
const txConn:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
const txConn: any = {
|
|
85
|
+
_grammar: this._grammar,
|
|
86
|
+
select: async (sql: string, b?: any[]) => { const [rows] = await conn.query(sql, b); return rows as Record<string, any>[] },
|
|
87
|
+
statement: async (sql: string, b?: any[]) => { const [r] = await conn.query(sql, b); return (r as any).affectedRows ?? 0 },
|
|
88
|
+
insertGetId: async (sql: string, b?: any[]) => { const [r] = await conn.query(sql, b); return (r as any).insertId ?? 0 },
|
|
89
|
+
transaction: (cb: any) => cb(txConn),
|
|
90
|
+
table: (name: string) => new QueryBuilder(txConn, name),
|
|
88
91
|
schema: () => new SchemaBuilderImpl(txConn),
|
|
89
92
|
getDriverName: () => 'mysql',
|
|
90
93
|
getTablePrefix: () => '',
|
|
91
94
|
}
|
|
92
|
-
|
|
93
|
-
txConn._grammar = this._grammar
|
|
95
|
+
this.applyExecuteMethods(txConn)
|
|
94
96
|
const result = await callback(txConn)
|
|
95
97
|
await conn.commit()
|
|
96
98
|
return result
|
|
@@ -102,10 +104,6 @@ export class MySQLConnection implements DatabaseConnection {
|
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
table(name: string): QueryBuilder {
|
|
106
|
-
return new QueryBuilder(this, name)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
107
|
schema(): SchemaBuilder {
|
|
110
108
|
return new SchemaBuilderImpl(this)
|
|
111
109
|
}
|
|
@@ -113,8 +111,4 @@ export class MySQLConnection implements DatabaseConnection {
|
|
|
113
111
|
getDriverName(): string {
|
|
114
112
|
return 'mysql'
|
|
115
113
|
}
|
|
116
|
-
|
|
117
|
-
getTablePrefix(): string {
|
|
118
|
-
return ''
|
|
119
|
-
}
|
|
120
114
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DatabaseConnection } from '../contracts/Connection.ts'
|
|
2
2
|
import type { SchemaBuilder } from '../schema/SchemaBuilder.ts'
|
|
3
|
+
import { BaseSQLConnection } from './BaseSQLConnection.ts'
|
|
3
4
|
import { QueryBuilder } from '../query/Builder.ts'
|
|
4
5
|
import { PostgresGrammar } from './PostgresGrammar.ts'
|
|
5
6
|
import { SchemaBuilderImpl } from '../schema/SchemaBuilder.ts'
|
|
@@ -7,21 +8,22 @@ import { ConnectionError } from '../errors/ConnectionError.ts'
|
|
|
7
8
|
import { QueryError } from '../errors/QueryError.ts'
|
|
8
9
|
|
|
9
10
|
export interface PostgresConfig {
|
|
10
|
-
host?: string
|
|
11
|
-
port?: number
|
|
11
|
+
host?: string | undefined
|
|
12
|
+
port?: number | undefined
|
|
12
13
|
database: string
|
|
13
|
-
user?: string
|
|
14
|
-
password?: string
|
|
15
|
-
ssl?: boolean
|
|
16
|
-
pool?: { min?: number; max?: number }
|
|
14
|
+
user?: string | undefined
|
|
15
|
+
password?: string | undefined
|
|
16
|
+
ssl?: boolean | undefined
|
|
17
|
+
pool?: { min?: number | undefined; max?: number | undefined } | undefined
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
export class PostgresConnection
|
|
20
|
+
export class PostgresConnection extends BaseSQLConnection {
|
|
20
21
|
readonly _grammar = new PostgresGrammar()
|
|
21
22
|
private client: any = null
|
|
22
23
|
private config: PostgresConfig
|
|
23
24
|
|
|
24
25
|
constructor(config: PostgresConfig) {
|
|
26
|
+
super()
|
|
25
27
|
this.config = config
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -83,19 +85,18 @@ export class PostgresConnection implements DatabaseConnection {
|
|
|
83
85
|
const client = await pool.connect()
|
|
84
86
|
try {
|
|
85
87
|
await client.query('BEGIN')
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
select: async (sql, b) => { const r = await client.query(sql, b); return r.rows },
|
|
89
|
-
statement: async (sql, b) => { const r = await client.query(sql, b); return r.rowCount ?? 0 },
|
|
90
|
-
insertGetId: async (sql, b) => { const r = await client.query(sql, b); return r.rows[0]?.id ?? 0 },
|
|
91
|
-
transaction: (cb) => cb(txConn),
|
|
92
|
-
table: (name) => new QueryBuilder(txConn, name),
|
|
88
|
+
const txConn: any = {
|
|
89
|
+
_grammar: this._grammar,
|
|
90
|
+
select: async (sql: string, b?: any[]) => { const r = await client.query(sql, b); return r.rows },
|
|
91
|
+
statement: async (sql: string, b?: any[]) => { const r = await client.query(sql, b); return r.rowCount ?? 0 },
|
|
92
|
+
insertGetId: async (sql: string, b?: any[]) => { const r = await client.query(sql, b); return r.rows[0]?.id ?? 0 },
|
|
93
|
+
transaction: (cb: any) => cb(txConn),
|
|
94
|
+
table: (name: string) => new QueryBuilder(txConn, name),
|
|
93
95
|
schema: () => new SchemaBuilderImpl(txConn),
|
|
94
96
|
getDriverName: () => 'postgres',
|
|
95
97
|
getTablePrefix: () => '',
|
|
96
98
|
}
|
|
97
|
-
|
|
98
|
-
txConn._grammar = this._grammar
|
|
99
|
+
this.applyExecuteMethods(txConn)
|
|
99
100
|
const result = await callback(txConn)
|
|
100
101
|
await client.query('COMMIT')
|
|
101
102
|
return result
|
|
@@ -107,10 +108,6 @@ export class PostgresConnection implements DatabaseConnection {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
table(name: string): QueryBuilder {
|
|
111
|
-
return new QueryBuilder(this, name)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
111
|
schema(): SchemaBuilder {
|
|
115
112
|
return new SchemaBuilderImpl(this)
|
|
116
113
|
}
|
|
@@ -118,8 +115,4 @@ export class PostgresConnection implements DatabaseConnection {
|
|
|
118
115
|
getDriverName(): string {
|
|
119
116
|
return 'postgres'
|
|
120
117
|
}
|
|
121
|
-
|
|
122
|
-
getTablePrefix(): string {
|
|
123
|
-
return ''
|
|
124
|
-
}
|
|
125
118
|
}
|