@mantiq/database 0.0.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/README.md +19 -0
- package/package.json +77 -0
- package/src/DatabaseManager.ts +115 -0
- package/src/DatabaseServiceProvider.ts +39 -0
- package/src/contracts/Connection.ts +13 -0
- package/src/contracts/Grammar.ts +16 -0
- package/src/contracts/MongoConnection.ts +122 -0
- package/src/contracts/Paginator.ts +10 -0
- package/src/drivers/BaseGrammar.ts +220 -0
- package/src/drivers/MSSQLConnection.ts +154 -0
- package/src/drivers/MSSQLGrammar.ts +106 -0
- package/src/drivers/MongoConnection.ts +298 -0
- package/src/drivers/MongoQueryBuilderImpl.ts +77 -0
- package/src/drivers/MySQLConnection.ts +120 -0
- package/src/drivers/MySQLGrammar.ts +19 -0
- package/src/drivers/PostgresConnection.ts +125 -0
- package/src/drivers/PostgresGrammar.ts +24 -0
- package/src/drivers/SQLiteConnection.ts +125 -0
- package/src/drivers/SQLiteGrammar.ts +19 -0
- package/src/errors/ConnectionError.ts +10 -0
- package/src/errors/ModelNotFoundError.ts +14 -0
- package/src/errors/QueryError.ts +11 -0
- package/src/events/DatabaseEvents.ts +101 -0
- package/src/factories/Factory.ts +170 -0
- package/src/factories/Faker.ts +382 -0
- package/src/helpers/db.ts +37 -0
- package/src/index.ts +100 -0
- package/src/migrations/Migration.ts +12 -0
- package/src/migrations/MigrationRepository.ts +50 -0
- package/src/migrations/Migrator.ts +201 -0
- package/src/orm/Collection.ts +236 -0
- package/src/orm/Document.ts +202 -0
- package/src/orm/Model.ts +775 -0
- package/src/orm/ModelQueryBuilder.ts +415 -0
- package/src/orm/Scope.ts +39 -0
- package/src/orm/eagerLoad.ts +300 -0
- package/src/query/Builder.ts +456 -0
- package/src/query/Expression.ts +18 -0
- package/src/schema/Blueprint.ts +196 -0
- package/src/schema/ColumnDefinition.ts +93 -0
- package/src/schema/SchemaBuilder.ts +376 -0
- package/src/seeders/Seeder.ts +28 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { MongoDatabaseConnection, MongoFilter, MongoUpdateDoc, MongoPipelineStage } from '../contracts/MongoConnection.ts'
|
|
2
|
+
import { ModelNotFoundError } from '../errors/ModelNotFoundError.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base class for MongoDB document models. Similar to Model but for document databases.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* class User extends Document {
|
|
9
|
+
* static collection = 'users'
|
|
10
|
+
* static connection = mongoConn
|
|
11
|
+
*
|
|
12
|
+
* get fullName(): string {
|
|
13
|
+
* return `${this.get('firstName')} ${this.get('lastName')}`
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* const user = await User.findById('65a...')
|
|
18
|
+
* await user!.update({ $set: { name: 'New Name' } })
|
|
19
|
+
*/
|
|
20
|
+
export abstract class Document {
|
|
21
|
+
static connection: MongoDatabaseConnection | null = null
|
|
22
|
+
static collection: string = ''
|
|
23
|
+
static hidden: string[] = []
|
|
24
|
+
|
|
25
|
+
protected _doc: Record<string, any> = {}
|
|
26
|
+
protected _exists = false
|
|
27
|
+
|
|
28
|
+
// ── Static query API ───────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
static col<T extends Document>(this: new () => T): import('../contracts/MongoConnection.ts').MongoCollectionContract {
|
|
31
|
+
const ctor = this as unknown as typeof Document
|
|
32
|
+
if (!ctor.connection) throw new Error(`No connection set on Document ${ctor.collection}`)
|
|
33
|
+
return ctor.connection.collection(ctor.collection)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static async find<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<T[]> {
|
|
37
|
+
const rows = await (this as unknown as typeof Document).col<T>().find(filter).get()
|
|
38
|
+
return rows.map((r) => (this as unknown as typeof Document).hydrate<T>(this, r))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static async findOne<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<T | null> {
|
|
42
|
+
const row = await (this as unknown as typeof Document).col<T>().findOne(filter)
|
|
43
|
+
return row ? (this as unknown as typeof Document).hydrate<T>(this, row) : null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static async findById<T extends Document>(this: new () => T, id: any): Promise<T | null> {
|
|
47
|
+
const row = await (this as unknown as typeof Document).col<T>().findById(id)
|
|
48
|
+
return row ? (this as unknown as typeof Document).hydrate<T>(this, row) : null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static async findByIdOrFail<T extends Document>(this: new () => T, id: any): Promise<T> {
|
|
52
|
+
const doc = await (this as unknown as typeof Document).findById<T>(id)
|
|
53
|
+
if (!doc) throw new ModelNotFoundError((this as unknown as typeof Document).collection)
|
|
54
|
+
return doc
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static async create<T extends Document>(this: new () => T, data: Record<string, any>): Promise<T> {
|
|
58
|
+
const col = (this as unknown as typeof Document).col<T>()
|
|
59
|
+
const now = new Date()
|
|
60
|
+
const doc = { ...data, createdAt: now, updatedAt: now }
|
|
61
|
+
const result = await col.insertOne(doc)
|
|
62
|
+
return (this as unknown as typeof Document).hydrate<T>(this, { ...doc, _id: result.insertedId })
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static async insertMany<T extends Document>(
|
|
66
|
+
this: new () => T,
|
|
67
|
+
docs: Record<string, any>[],
|
|
68
|
+
): Promise<T[]> {
|
|
69
|
+
const col = (this as unknown as typeof Document).col<T>()
|
|
70
|
+
const now = new Date()
|
|
71
|
+
const withTimestamps = docs.map((d) => ({ ...d, createdAt: now, updatedAt: now }))
|
|
72
|
+
const result = await col.insertMany(withTimestamps)
|
|
73
|
+
return withTimestamps.map((d, i) =>
|
|
74
|
+
(this as unknown as typeof Document).hydrate<T>(this, { ...d, _id: result.insertedIds[i] }),
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static async updateOne<T extends Document>(
|
|
79
|
+
this: new () => T,
|
|
80
|
+
filter: MongoFilter,
|
|
81
|
+
update: MongoUpdateDoc,
|
|
82
|
+
) {
|
|
83
|
+
const merged: MongoUpdateDoc = {
|
|
84
|
+
...update,
|
|
85
|
+
$set: { ...update.$set, updatedAt: new Date() },
|
|
86
|
+
}
|
|
87
|
+
return (this as unknown as typeof Document).col<T>().updateOne(filter, merged)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static async updateMany<T extends Document>(
|
|
91
|
+
this: new () => T,
|
|
92
|
+
filter: MongoFilter,
|
|
93
|
+
update: MongoUpdateDoc,
|
|
94
|
+
) {
|
|
95
|
+
const merged: MongoUpdateDoc = {
|
|
96
|
+
...update,
|
|
97
|
+
$set: { ...update.$set, updatedAt: new Date() },
|
|
98
|
+
}
|
|
99
|
+
return (this as unknown as typeof Document).col<T>().updateMany(filter, merged)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static async deleteOne<T extends Document>(this: new () => T, filter: MongoFilter) {
|
|
103
|
+
return (this as unknown as typeof Document).col<T>().deleteOne(filter)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static async deleteMany<T extends Document>(this: new () => T, filter: MongoFilter) {
|
|
107
|
+
return (this as unknown as typeof Document).col<T>().deleteMany(filter)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static async aggregate<T extends Document>(
|
|
111
|
+
this: new () => T,
|
|
112
|
+
pipeline: MongoPipelineStage[],
|
|
113
|
+
): Promise<Record<string, any>[]> {
|
|
114
|
+
return (this as unknown as typeof Document).col<T>().aggregate(pipeline)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static async count<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<number> {
|
|
118
|
+
return (this as unknown as typeof Document).col<T>().count(filter)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Set the MongoDB connection for this Document class */
|
|
122
|
+
static setConnection(connection: MongoDatabaseConnection): void {
|
|
123
|
+
this.connection = connection
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Instance methods ──────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
get(key: string): any {
|
|
129
|
+
return this._doc[key]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
set(key: string, value: any): this {
|
|
133
|
+
this._doc[key] = value
|
|
134
|
+
return this
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getId(): any {
|
|
138
|
+
return this._doc['_id']
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
toObject(): Record<string, any> {
|
|
142
|
+
const ctor = this.constructor as typeof Document
|
|
143
|
+
const obj: Record<string, any> = {}
|
|
144
|
+
for (const [k, v] of Object.entries(this._doc)) {
|
|
145
|
+
if (!ctor.hidden.includes(k)) obj[k] = v
|
|
146
|
+
}
|
|
147
|
+
return obj
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
toJSON(): Record<string, any> {
|
|
151
|
+
return this.toObject()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async update(update: MongoUpdateDoc): Promise<this> {
|
|
155
|
+
const ctor = this.constructor as typeof Document
|
|
156
|
+
if (!ctor.connection || !this._exists) return this
|
|
157
|
+
|
|
158
|
+
const merged: MongoUpdateDoc = {
|
|
159
|
+
...update,
|
|
160
|
+
$set: { ...update.$set, updatedAt: new Date() },
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await ctor.connection.collection(ctor.collection).updateOne({ _id: this.getId() }, merged)
|
|
164
|
+
|
|
165
|
+
// Apply $set changes locally
|
|
166
|
+
if (merged.$set) {
|
|
167
|
+
for (const [k, v] of Object.entries(merged.$set)) {
|
|
168
|
+
this._doc[k] = v
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return this
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async delete(): Promise<boolean> {
|
|
176
|
+
const ctor = this.constructor as typeof Document
|
|
177
|
+
if (!ctor.connection || !this._exists) return false
|
|
178
|
+
await ctor.connection.collection(ctor.collection).deleteOne({ _id: this.getId() })
|
|
179
|
+
this._exists = false
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async refresh(): Promise<this> {
|
|
184
|
+
const ctor = this.constructor as typeof Document
|
|
185
|
+
if (!ctor.connection) return this
|
|
186
|
+
const row = await ctor.connection.collection(ctor.collection).findById(this.getId())
|
|
187
|
+
if (row) this._doc = row
|
|
188
|
+
return this
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ── Private ────────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
private static hydrate<T extends Document>(
|
|
194
|
+
ctor: new () => T,
|
|
195
|
+
doc: Record<string, any>,
|
|
196
|
+
): T {
|
|
197
|
+
const instance = new ctor()
|
|
198
|
+
instance._doc = doc
|
|
199
|
+
instance._exists = true
|
|
200
|
+
return instance
|
|
201
|
+
}
|
|
202
|
+
}
|