tspace-mysql 1.9.0-beta.6 → 1.9.1-beta.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 +321 -28
- package/dist/lib/core/Abstracts/AbstractBuilder.d.ts +16 -16
- package/dist/lib/core/Abstracts/AbstractBuilder.js.map +1 -1
- package/dist/lib/core/Abstracts/AbstractDB.d.ts +1 -1
- package/dist/lib/core/Abstracts/AbstractModel.d.ts +7 -2
- package/dist/lib/core/Abstracts/AbstractModel.js.map +1 -1
- package/dist/lib/core/Blueprint.d.ts +10 -10
- package/dist/lib/core/Blueprint.js +0 -1
- package/dist/lib/core/Blueprint.js.map +1 -1
- package/dist/lib/core/Builder.d.ts +54 -36
- package/dist/lib/core/Builder.js +79 -47
- package/dist/lib/core/Builder.js.map +1 -1
- package/dist/lib/core/Cache/index.js +32 -13
- package/dist/lib/core/Cache/index.js.map +1 -1
- package/dist/lib/core/Contracts/Audit.d.ts +1 -1
- package/dist/lib/core/Contracts/Audit.js.map +1 -1
- package/dist/lib/core/Contracts/Logger.d.ts +1 -1
- package/dist/lib/core/Contracts/Logger.js.map +1 -1
- package/dist/lib/core/DB.d.ts +2 -2
- package/dist/lib/core/DB.js +6 -19
- package/dist/lib/core/DB.js.map +1 -1
- package/dist/lib/core/Driver/index.d.ts +8 -5
- package/dist/lib/core/Driver/index.js +4 -2
- package/dist/lib/core/Driver/index.js.map +1 -1
- package/dist/lib/core/Driver/mariadb/MariadbDriver.js +36 -9
- package/dist/lib/core/Driver/mariadb/MariadbDriver.js.map +1 -1
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.d.ts +4 -3
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js +28 -7
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/mongodb/MongodbDriver.js +18 -12
- package/dist/lib/core/Driver/mongodb/MongodbDriver.js.map +1 -1
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.d.ts +4 -3
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js +13 -1
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/mysql/MysqlDriver.js +36 -29
- package/dist/lib/core/Driver/mysql/MysqlDriver.js.map +1 -1
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.d.ts +4 -3
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js +28 -7
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/postgres/PostgresDriver.js +31 -8
- package/dist/lib/core/Driver/postgres/PostgresDriver.js.map +1 -1
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.d.ts +4 -3
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js +26 -7
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/sqlite/SqliteDriver.d.ts +1 -0
- package/dist/lib/core/Driver/sqlite/SqliteDriver.js +25 -4
- package/dist/lib/core/Driver/sqlite/SqliteDriver.js.map +1 -1
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.d.ts +3 -0
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js +29 -9
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js.map +1 -1
- package/dist/lib/core/Join.d.ts +63 -5
- package/dist/lib/core/Join.js +92 -15
- package/dist/lib/core/Join.js.map +1 -1
- package/dist/lib/core/JoinModel.d.ts +65 -9
- package/dist/lib/core/JoinModel.js +79 -50
- package/dist/lib/core/JoinModel.js.map +1 -1
- package/dist/lib/core/Meta.d.ts +7 -7
- package/dist/lib/core/Meta.js +10 -9
- package/dist/lib/core/Meta.js.map +1 -1
- package/dist/lib/core/Model.d.ts +538 -49
- package/dist/lib/core/Model.js +742 -86
- package/dist/lib/core/Model.js.map +1 -1
- package/dist/lib/core/Queue.d.ts +18 -9
- package/dist/lib/core/Queue.js +79 -39
- package/dist/lib/core/Queue.js.map +1 -1
- package/dist/lib/core/RelationManager.d.ts +4 -4
- package/dist/lib/core/RelationManager.js +141 -16
- package/dist/lib/core/RelationManager.js.map +1 -1
- package/dist/lib/core/Repository.d.ts +57 -189
- package/dist/lib/core/Repository.js +62 -224
- package/dist/lib/core/Repository.js.map +1 -1
- package/dist/lib/core/Schema.d.ts +3 -11
- package/dist/lib/core/StateManager.d.ts +6 -0
- package/dist/lib/core/StateManager.js +1 -0
- package/dist/lib/core/StateManager.js.map +1 -1
- package/dist/lib/core/UtilityTypes.d.ts +52 -70
- package/dist/lib/core/UtilityTypes.js.map +1 -1
- package/dist/lib/types/index.d.ts +51 -9
- package/dist/lib/types/repository/index.d.ts +11 -10
- package/dist/lib/utils/index.d.ts +2 -1
- package/dist/lib/utils/index.js +26 -3
- package/dist/lib/utils/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -66,6 +66,8 @@ npm install -D typescript@5.9.3
|
|
|
66
66
|
- [MySQL Database](#mysql-database)
|
|
67
67
|
- [Mariadb Database](#mariadb-database)
|
|
68
68
|
- [Postgres Database](#postgres-database)
|
|
69
|
+
- [SQLite Database](#sqlite-database)
|
|
70
|
+
- [Mongodb Database](#mongodb-database)
|
|
69
71
|
- [Cluster Database](#cluster-database)
|
|
70
72
|
- [SQL Like](#sql-Like)
|
|
71
73
|
- [Query Builder](#query-builder)
|
|
@@ -107,6 +109,7 @@ npm install -D typescript@5.9.3
|
|
|
107
109
|
- [More Methods](#more-methods)
|
|
108
110
|
- [Database Transactions](#database-transactions)
|
|
109
111
|
- [Race Condition](#race-condition)
|
|
112
|
+
- [Lock Table](#lock-table)
|
|
110
113
|
- [Connection](#connection)
|
|
111
114
|
- [Backup](#backup)
|
|
112
115
|
- [Injection](#injection)
|
|
@@ -247,6 +250,50 @@ DB_PASSWORD = password
|
|
|
247
250
|
DB_DATABASE = database
|
|
248
251
|
```
|
|
249
252
|
|
|
253
|
+
### SQLite Database
|
|
254
|
+
|
|
255
|
+
To connect the application to a SQLite database, using the following:
|
|
256
|
+
|
|
257
|
+
```sh
|
|
258
|
+
npm install better-sqlite3 --save
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
DB_DRIVER = sqlite
|
|
263
|
+
DB_DATABASE = app.db
|
|
264
|
+
```
|
|
265
|
+
⚠️ Requirements for better-sqlite3
|
|
266
|
+
Node.js 22 or higher is required
|
|
267
|
+
|
|
268
|
+
### Mongodb Database
|
|
269
|
+
|
|
270
|
+
To connect the application to a Mongodb database, using the following:
|
|
271
|
+
```sh
|
|
272
|
+
npm install mongodb --save
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
DB_DRIVER = mongodb
|
|
277
|
+
DB_HOST = localhost
|
|
278
|
+
DB_PORT = 27017
|
|
279
|
+
DB_USERNAME = root
|
|
280
|
+
DB_PASSWORD = password
|
|
281
|
+
DB_DATABASE = database
|
|
282
|
+
```
|
|
283
|
+
✅ Supported Features
|
|
284
|
+
|
|
285
|
+
CRUD operations (create, read, update, delete)
|
|
286
|
+
Basic joins & relations (via abstraction layer)
|
|
287
|
+
|
|
288
|
+
⚠️ Limitations
|
|
289
|
+
|
|
290
|
+
MongoDB support is partially implemented and may not fully match SQL-based drivers like MySQL or PostgreSQL
|
|
291
|
+
|
|
292
|
+
* Advanced ORM features may be limited
|
|
293
|
+
* Complex joins rely on abstraction (not native MongoDB behavior)
|
|
294
|
+
* Some features may behave differently compared to relational databases
|
|
295
|
+
* Transactions / advanced optimizations may be limited
|
|
296
|
+
|
|
250
297
|
### Cluster Database
|
|
251
298
|
If you need strict race condition control, it is required to use multiple nodes for write and read. <br>
|
|
252
299
|
Avoid using a node load balancer in this case, as it may bypass proper write/read distribution and compromise consistency.<br>
|
|
@@ -1251,7 +1298,7 @@ whereNotIn(column , [])
|
|
|
1251
1298
|
whereNull(column)
|
|
1252
1299
|
whereNotNull(column)
|
|
1253
1300
|
whereBetween (column , [value1 , value2])
|
|
1254
|
-
whereQuery(
|
|
1301
|
+
whereQuery(query)
|
|
1255
1302
|
whereJson(column, { targetKey, value , OP })
|
|
1256
1303
|
whereRaw(sql)
|
|
1257
1304
|
whereExists(sql)
|
|
@@ -1261,7 +1308,7 @@ orWhere(column , OP , value)
|
|
|
1261
1308
|
orWhereRaw(sql)
|
|
1262
1309
|
orWhereIn(column , [])
|
|
1263
1310
|
orWhereSubQuery(colmn , rawSQL)
|
|
1264
|
-
when(contition ,
|
|
1311
|
+
when(contition , query)
|
|
1265
1312
|
select(column1 ,column2 ,...N)
|
|
1266
1313
|
distinct()
|
|
1267
1314
|
selectRaw(column1 ,column2 ,...N)
|
|
@@ -1294,7 +1341,7 @@ onlyTrashed()
|
|
|
1294
1341
|
connection(options)
|
|
1295
1342
|
backup({ database , connection })
|
|
1296
1343
|
backupToFile({ filePath, database , connection })
|
|
1297
|
-
hook((result) => ...) //
|
|
1344
|
+
hook((result) => ...) // query result to function
|
|
1298
1345
|
sleep(seconds)
|
|
1299
1346
|
|
|
1300
1347
|
/**
|
|
@@ -1322,9 +1369,9 @@ relationsExists(name1 , name2,...nameN) // withExists(name1, name2,...nameN)
|
|
|
1322
1369
|
*/
|
|
1323
1370
|
relationsTrashed(name1 , name2,...nameN) // withTrashed(name1, name2,...nameN)
|
|
1324
1371
|
/**
|
|
1325
|
-
* @relation call a name of relation in registry,
|
|
1372
|
+
* @relation call a name of relation in registry, query query of data
|
|
1326
1373
|
*/
|
|
1327
|
-
relationQuery(name, (
|
|
1374
|
+
relationQuery(name, (query) ) // withQuery(name1, (query))
|
|
1328
1375
|
|
|
1329
1376
|
|
|
1330
1377
|
/**
|
|
@@ -1360,6 +1407,10 @@ makeCreateTableStatement()
|
|
|
1360
1407
|
Within a database transaction, you can utilize the following:
|
|
1361
1408
|
|
|
1362
1409
|
```js
|
|
1410
|
+
import { DB } from 'tspace-mysql';
|
|
1411
|
+
import { User } from '../Models/User';
|
|
1412
|
+
import { Post } from '../Models/Post';
|
|
1413
|
+
|
|
1363
1414
|
const trx = await new DB().beginTransaction();
|
|
1364
1415
|
|
|
1365
1416
|
try {
|
|
@@ -1441,6 +1492,52 @@ try {
|
|
|
1441
1492
|
*/
|
|
1442
1493
|
await trx.rollback();
|
|
1443
1494
|
}
|
|
1495
|
+
```
|
|
1496
|
+
Or you can use transaction method for auto commit and rollback, you can utilize the following:
|
|
1497
|
+
|
|
1498
|
+
```js
|
|
1499
|
+
import { DB } from 'tspace-mysql';
|
|
1500
|
+
import { User } from '../Models/User';
|
|
1501
|
+
import { Post } from '../Models/Post';
|
|
1502
|
+
|
|
1503
|
+
try {
|
|
1504
|
+
|
|
1505
|
+
const { user, posts } = await DB.transaction(async (trx) => {
|
|
1506
|
+
|
|
1507
|
+
const user = await new User()
|
|
1508
|
+
.create({
|
|
1509
|
+
name: `tspace`,
|
|
1510
|
+
email: "tspace@example.com",
|
|
1511
|
+
})
|
|
1512
|
+
.bind(trx) // don't forget this
|
|
1513
|
+
.save();
|
|
1514
|
+
|
|
1515
|
+
const posts = await new Post()
|
|
1516
|
+
.createMultiple([
|
|
1517
|
+
{
|
|
1518
|
+
user_id: user.id,
|
|
1519
|
+
title: `tspace-post1`,
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
user_id: user.id,
|
|
1523
|
+
title: `tspace-post2`,
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
user_id: user.id,
|
|
1527
|
+
title: `tspace-post3`,
|
|
1528
|
+
},
|
|
1529
|
+
])
|
|
1530
|
+
.bind(trx) // don't forget this
|
|
1531
|
+
.save();
|
|
1532
|
+
|
|
1533
|
+
return { user, posts };
|
|
1534
|
+
|
|
1535
|
+
});
|
|
1536
|
+
} catch (err) {
|
|
1537
|
+
// error here is from transaction method, you don't need to worry about rollback, just catch error and handle it.
|
|
1538
|
+
console.error(err);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1444
1541
|
```
|
|
1445
1542
|
|
|
1446
1543
|
## Race Condition
|
|
@@ -1553,6 +1650,148 @@ async function simulateRaceConnection(): Promise<void> {
|
|
|
1553
1650
|
|
|
1554
1651
|
simulateRaceConnection();
|
|
1555
1652
|
|
|
1653
|
+
```
|
|
1654
|
+
## Lock Table
|
|
1655
|
+
|
|
1656
|
+
Within a lock table, you can utilize the following:
|
|
1657
|
+
|
|
1658
|
+
This approach is not recommended in clustered or load-balance environments,
|
|
1659
|
+
because locks are session-bound and requests may be routed to different database connections.
|
|
1660
|
+
|
|
1661
|
+
READ:
|
|
1662
|
+
- Other sessions can read.
|
|
1663
|
+
- Other sessions must wait before writing.
|
|
1664
|
+
- SELECT: allowed
|
|
1665
|
+
- INSERT: waits
|
|
1666
|
+
- UPDATE: waits
|
|
1667
|
+
- DELETE: waits
|
|
1668
|
+
|
|
1669
|
+
WRITE:
|
|
1670
|
+
- Other sessions must wait before reading or writing.
|
|
1671
|
+
- SELECT: waits
|
|
1672
|
+
- INSERT: waits
|
|
1673
|
+
- UPDATE: waits
|
|
1674
|
+
- DELETE: waits
|
|
1675
|
+
|
|
1676
|
+
The lock is released automatically after the query completes.
|
|
1677
|
+
|
|
1678
|
+
```js
|
|
1679
|
+
import { Model, Blueprint } from 'tspace-mysql'
|
|
1680
|
+
|
|
1681
|
+
class User extends Model {
|
|
1682
|
+
protected boot() {
|
|
1683
|
+
this.useSchema({
|
|
1684
|
+
id : Blueprint.int().notNull().primary().autoIncrement(),
|
|
1685
|
+
email : Blueprint.varchar(20).notNull()
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
async function requestA() {
|
|
1691
|
+
|
|
1692
|
+
try {
|
|
1693
|
+
|
|
1694
|
+
console.log("A: locking table...");
|
|
1695
|
+
|
|
1696
|
+
// READ | WRITE
|
|
1697
|
+
await User.lockTable('WRITE', async (query) => {
|
|
1698
|
+
console.log("A: table LOCKED");
|
|
1699
|
+
|
|
1700
|
+
// Simulate a long-running write transaction.
|
|
1701
|
+
// WRITE operations from other sessions will wait
|
|
1702
|
+
// until the table lock is released.
|
|
1703
|
+
//
|
|
1704
|
+
// READ behavior depends on the selected lock mode:
|
|
1705
|
+
// - READ lock: other readers are allowed
|
|
1706
|
+
// - WRITE lock: readers/writers may be blocked
|
|
1707
|
+
await new Promise((r) => setTimeout(r, 1000 * 10));
|
|
1708
|
+
|
|
1709
|
+
// IMPORTANT:
|
|
1710
|
+
// While the table is locked, all database operations
|
|
1711
|
+
// must use the provided `query` instance.
|
|
1712
|
+
//
|
|
1713
|
+
// This guarantees the operation runs on the same
|
|
1714
|
+
// database session/connection that owns the lock.
|
|
1715
|
+
//
|
|
1716
|
+
// ❌ May fail because it can use a different connection:
|
|
1717
|
+
// await new User().insert({...}).save();
|
|
1718
|
+
//
|
|
1719
|
+
// ✅ Uses the lock owner session:
|
|
1720
|
+
await query.insert({...}).save();
|
|
1721
|
+
|
|
1722
|
+
// In READ mode this write operation will throw,
|
|
1723
|
+
// because writes are not permitted under a READ lock.
|
|
1724
|
+
// In WRITE mode it executes successfully.
|
|
1725
|
+
|
|
1726
|
+
console.log("A: unlocking table...");
|
|
1727
|
+
});
|
|
1728
|
+
|
|
1729
|
+
console.log("A: done");
|
|
1730
|
+
|
|
1731
|
+
} catch (err:any) {
|
|
1732
|
+
|
|
1733
|
+
console.error('❌ SHOULD NOT FAIL:', err.message);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
async function requestB(n: number = 1) {
|
|
1738
|
+
console.log(`B(${n}): waiting 1s before query...`);
|
|
1739
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1740
|
+
|
|
1741
|
+
console.log(`B(${n}): trying SELECT...`);
|
|
1742
|
+
|
|
1743
|
+
const start = Date.now();
|
|
1744
|
+
|
|
1745
|
+
try {
|
|
1746
|
+
|
|
1747
|
+
const users = await new User().select('id').get();
|
|
1748
|
+
console.log(`B(${n}): result`, users.length);
|
|
1749
|
+
|
|
1750
|
+
console.log(`B(${n}): finished in`, Date.now() - start, "ms");
|
|
1751
|
+
} catch (err:any) {
|
|
1752
|
+
console.error(`B(${n}): ERROR`, err.message);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
async function requestC(n: number = 1) {
|
|
1757
|
+
console.log(`C(${n}): waiting 1s before query...`);
|
|
1758
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1759
|
+
|
|
1760
|
+
console.log(`C(${n}): trying Insert...Select`);
|
|
1761
|
+
|
|
1762
|
+
const start = Date.now();
|
|
1763
|
+
|
|
1764
|
+
try {
|
|
1765
|
+
|
|
1766
|
+
await new User().faker(1);
|
|
1767
|
+
|
|
1768
|
+
const users = await new User().select('id').get();
|
|
1769
|
+
console.log(`C(${n}): result`, users.length);
|
|
1770
|
+
|
|
1771
|
+
console.log(`C(${n}): finished in`, Date.now() - start, "ms");
|
|
1772
|
+
} catch (err:any) {
|
|
1773
|
+
console.error(`C(${n}): ERROR`, err.message);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
(async () => {
|
|
1778
|
+
// Test scenario:
|
|
1779
|
+
//
|
|
1780
|
+
// 1. Request A acquires a table lock.
|
|
1781
|
+
// 2. Requests B perform concurrent reads.
|
|
1782
|
+
// 3. Request C performs a concurrent write.
|
|
1783
|
+
// 4. Observe whether operations are blocked, queued, or rejected
|
|
1784
|
+
// according to the selected lock mode.
|
|
1785
|
+
await Promise.all([
|
|
1786
|
+
requestA(),
|
|
1787
|
+
requestB(1),
|
|
1788
|
+
requestB(2),
|
|
1789
|
+
requestC(3)
|
|
1790
|
+
])
|
|
1791
|
+
.then(r => process.exit(1))
|
|
1792
|
+
|
|
1793
|
+
})()
|
|
1794
|
+
|
|
1556
1795
|
```
|
|
1557
1796
|
|
|
1558
1797
|
## Connection
|
|
@@ -1707,6 +1946,26 @@ class User extends Model {
|
|
|
1707
1946
|
* updated_at : Blueprint.timestamp().null(),
|
|
1708
1947
|
* deleted_at : Blueprint.timestamp().null()
|
|
1709
1948
|
* }) // auto-generated table when table is not exists and auto-create column when column not exists
|
|
1949
|
+
*
|
|
1950
|
+
* this.useLifecycle('beforeInsert', () => console.log('beforeInsert'))
|
|
1951
|
+
* this.useLifecycle('afterInsert', () => console.log('afterInsert'))
|
|
1952
|
+
* this.useLifecycle('beforeUpdate', () => console.log('beforeUpdate'))
|
|
1953
|
+
* this.useLifecycle('afterUpdate', () => console.log('afterUpdate'))
|
|
1954
|
+
* this.useLifecycle('beforeRemove', () => console.log('beforeRemove'))
|
|
1955
|
+
* this.useLifecycle('afterRemove', () => console.log('afterRemove'))
|
|
1956
|
+
*
|
|
1957
|
+
* this.useTransform({
|
|
1958
|
+
* name : {
|
|
1959
|
+
* to : async (name) => `${name}-> transform@before`,
|
|
1960
|
+
* from : async (name) => `${name}-> transform@after`,
|
|
1961
|
+
* }
|
|
1962
|
+
* })
|
|
1963
|
+
*
|
|
1964
|
+
* this.useHooks([
|
|
1965
|
+
* (r:any) => console.log(r,'hook1'),
|
|
1966
|
+
* (r:any) => console.log(r,'hook2'),
|
|
1967
|
+
* (r:any) => console.log(r,'hook3')
|
|
1968
|
+
* ])
|
|
1710
1969
|
*
|
|
1711
1970
|
* // validate input when create or update reference to the schema in 'this.useSchema'
|
|
1712
1971
|
* this.useValidateSchema({
|
|
@@ -2003,7 +2262,8 @@ Let's example a basic relationship:
|
|
|
2003
2262
|
A one-to-one relationship is used to define relationships where a single model is the parent to one child models
|
|
2004
2263
|
|
|
2005
2264
|
```js
|
|
2006
|
-
import { Model } from 'tspace-mysql'
|
|
2265
|
+
import { Model , type T } from 'tspace-mysql';
|
|
2266
|
+
|
|
2007
2267
|
import Phone from '../Phone'
|
|
2008
2268
|
class User extends Model {
|
|
2009
2269
|
constructor(){
|
|
@@ -2018,12 +2278,15 @@ class User extends Model {
|
|
|
2018
2278
|
}
|
|
2019
2279
|
/**
|
|
2020
2280
|
* Mark a method for relationship
|
|
2021
|
-
* @hasOne Get the phone associated with the user. using function
|
|
2281
|
+
* @hasOne Get the phone associated with the user. using function query
|
|
2022
2282
|
* @function
|
|
2023
2283
|
*/
|
|
2024
|
-
phone (
|
|
2025
|
-
return this.hasOneBuilder({
|
|
2284
|
+
public phone (query ?: T.QueryModifier<Phone>) {
|
|
2285
|
+
return this.hasOneBuilder({ model : Phone } , query)
|
|
2026
2286
|
}
|
|
2287
|
+
|
|
2288
|
+
// or
|
|
2289
|
+
public phone = this.createRelation(Phone,{ type : 'hasOne'})
|
|
2027
2290
|
}
|
|
2028
2291
|
export default User
|
|
2029
2292
|
|
|
@@ -2041,8 +2304,8 @@ const userUsingFunction = await new User().phone().findOne()
|
|
|
2041
2304
|
A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models.
|
|
2042
2305
|
|
|
2043
2306
|
```js
|
|
2044
|
-
import { Model } from 'tspace-mysql'
|
|
2045
|
-
import Comment from '../Comment'
|
|
2307
|
+
import { Model, type T } from 'tspace-mysql';
|
|
2308
|
+
import Comment from '../Comment';
|
|
2046
2309
|
class Post extends Model {
|
|
2047
2310
|
constructor(){
|
|
2048
2311
|
super()
|
|
@@ -2056,12 +2319,15 @@ class Post extends Model {
|
|
|
2056
2319
|
}
|
|
2057
2320
|
/**
|
|
2058
2321
|
*
|
|
2059
|
-
* @hasManyQuery Get the comments for the post. using function
|
|
2322
|
+
* @hasManyQuery Get the comments for the post. using function query
|
|
2060
2323
|
* @function
|
|
2061
2324
|
*/
|
|
2062
|
-
comments (
|
|
2063
|
-
return this.hasManyBuilder({
|
|
2325
|
+
public comments (query?: T.QueryModifier<Comment>) {
|
|
2326
|
+
return this.hasManyBuilder({ model : Comment } , query)
|
|
2064
2327
|
}
|
|
2328
|
+
|
|
2329
|
+
// or
|
|
2330
|
+
public comments = this.createRelation(Comment,{ type : 'hasMany'})
|
|
2065
2331
|
}
|
|
2066
2332
|
export default Post
|
|
2067
2333
|
|
|
@@ -2079,7 +2345,7 @@ const postsUsingFunction = await new Post().comments().findOne()
|
|
|
2079
2345
|
A belongsto relationship is used to define relationships where a single model is the child to parent models.
|
|
2080
2346
|
|
|
2081
2347
|
```js
|
|
2082
|
-
import { Model } from 'tspace-mysql'
|
|
2348
|
+
import { Model, type T } from 'tspace-mysql'
|
|
2083
2349
|
import User from '../User'
|
|
2084
2350
|
class Phone extends Model {
|
|
2085
2351
|
constructor(){
|
|
@@ -2094,12 +2360,15 @@ class Phone extends Model {
|
|
|
2094
2360
|
}
|
|
2095
2361
|
/**
|
|
2096
2362
|
*
|
|
2097
|
-
* @belongsToBuilder Get the user that owns the phone.. using function
|
|
2363
|
+
* @belongsToBuilder Get the user that owns the phone.. using function query
|
|
2098
2364
|
* @function
|
|
2099
2365
|
*/
|
|
2100
|
-
user (
|
|
2101
|
-
|
|
2366
|
+
public user (query?: T.QueryModifier<user>) {
|
|
2367
|
+
return this.belongsToBuilder({ model : User }, query)
|
|
2102
2368
|
}
|
|
2369
|
+
|
|
2370
|
+
// or
|
|
2371
|
+
public user = this.createRelation(User,{ type : 'belognsTo'})
|
|
2103
2372
|
}
|
|
2104
2373
|
export default Phone
|
|
2105
2374
|
|
|
@@ -2117,7 +2386,7 @@ const phoneUsingFunction = await new Phone().user().findOne()
|
|
|
2117
2386
|
Many-to-many relations are slightly more complicated than hasOne and hasMany relationships.
|
|
2118
2387
|
|
|
2119
2388
|
```js
|
|
2120
|
-
import { Model } from 'tspace-mysql'
|
|
2389
|
+
import { Model, type T } from 'tspace-mysql'
|
|
2121
2390
|
import Role from '../Role'
|
|
2122
2391
|
class User extends Model {
|
|
2123
2392
|
constructor(){
|
|
@@ -2131,12 +2400,15 @@ class User extends Model {
|
|
|
2131
2400
|
this.belognsToMany({ name : 'roles' , model : Role })
|
|
2132
2401
|
}
|
|
2133
2402
|
/**
|
|
2134
|
-
* @belongsToBuilder Get the user that owns the phone.. using function
|
|
2403
|
+
* @belongsToBuilder Get the user that owns the phone.. using function query
|
|
2135
2404
|
* @function
|
|
2136
2405
|
*/
|
|
2137
|
-
roles (
|
|
2138
|
-
|
|
2406
|
+
public roles (query?: T.QueryModifier<Role>) {
|
|
2407
|
+
return this.belognsToManyBuilder({ model : Role } , query)
|
|
2139
2408
|
}
|
|
2409
|
+
|
|
2410
|
+
// or
|
|
2411
|
+
public roles = this.createRelation(Role, { type : 'belognsToMany'})
|
|
2140
2412
|
}
|
|
2141
2413
|
export default User
|
|
2142
2414
|
|
|
@@ -2650,7 +2922,7 @@ Cache can be used in a Model.
|
|
|
2650
2922
|
Let's illustrate this with an example of a cache:
|
|
2651
2923
|
|
|
2652
2924
|
```js
|
|
2653
|
-
// support memory db
|
|
2925
|
+
// support memory , db , redis
|
|
2654
2926
|
// set cache in file config .env , .env.development ... etc
|
|
2655
2927
|
DB_CACHE = memory // by default
|
|
2656
2928
|
|
|
@@ -2658,16 +2930,21 @@ DB_CACHE = memory // by default
|
|
|
2658
2930
|
DB_CACHE = db
|
|
2659
2931
|
|
|
2660
2932
|
// for redis
|
|
2933
|
+
// npm install redis@5.6.0
|
|
2661
2934
|
DB_CACHE = redis://username:password@server:6379
|
|
2662
2935
|
|
|
2663
2936
|
const users = await new User()
|
|
2664
2937
|
.cache({
|
|
2665
|
-
key : '
|
|
2666
|
-
expires : 1000 * 60 // cache expires in 60 seconds
|
|
2938
|
+
key : 'users1', // key of the cache
|
|
2939
|
+
expires : 1000 * 60, // cache expires in 60 seconds
|
|
2940
|
+
namespace : true // add namespace to the key of the cache like that 'db:users:users1' to avoid conflicts with other keys in cache
|
|
2667
2941
|
})
|
|
2668
2942
|
.sleep(5) // assume the query takes longer than 5 seconds...
|
|
2669
2943
|
.findMany()
|
|
2670
2944
|
|
|
2945
|
+
// delete cache by key
|
|
2946
|
+
User.cache.delete('users1', { namespace : true })
|
|
2947
|
+
|
|
2671
2948
|
```
|
|
2672
2949
|
|
|
2673
2950
|
### Decorator
|
|
@@ -3525,6 +3802,10 @@ import { User } from '../Models/User'
|
|
|
3525
3802
|
|
|
3526
3803
|
// Create repository instance for User entity
|
|
3527
3804
|
const userRepository = Repository(User)
|
|
3805
|
+
|
|
3806
|
+
// Or Use static methods for flexible querying
|
|
3807
|
+
// const user = await User.findOne({ ... }) // traditional way without repository
|
|
3808
|
+
|
|
3528
3809
|
// Fetch a single user with flexible query options
|
|
3529
3810
|
const user = await userRepository.findOne({
|
|
3530
3811
|
/**
|
|
@@ -3633,6 +3914,8 @@ const user = await userRepository.findOne({
|
|
|
3633
3914
|
})
|
|
3634
3915
|
|
|
3635
3916
|
|
|
3917
|
+
// Use static methods for flexible querying
|
|
3918
|
+
// const users = await User.findMany({ limit: 3 }) // traditional way without repository
|
|
3636
3919
|
const users = await userRepository.findMany({
|
|
3637
3920
|
select : {
|
|
3638
3921
|
id : true,
|
|
@@ -3652,6 +3935,8 @@ const users = await userRepository.findMany({
|
|
|
3652
3935
|
}
|
|
3653
3936
|
})
|
|
3654
3937
|
|
|
3938
|
+
// Use static methods for flexible querying
|
|
3939
|
+
// const userPaginate = await User.pagination() // traditional way without repository
|
|
3655
3940
|
const userPaginate = await userRepository.pagination({
|
|
3656
3941
|
select : {
|
|
3657
3942
|
id : true,
|
|
@@ -3677,6 +3962,8 @@ const findFullName = await userRepository.findOne({
|
|
|
3677
3962
|
```
|
|
3678
3963
|
### Repository Insert Statements
|
|
3679
3964
|
```js
|
|
3965
|
+
// Use static methods for flexible querying
|
|
3966
|
+
// const created = await User.create({...}) // traditional way without repository
|
|
3680
3967
|
|
|
3681
3968
|
const userRepository = Repository(User)
|
|
3682
3969
|
|
|
@@ -3730,8 +4017,10 @@ const createdOrSelected = await userRepository.createOrSelect({
|
|
|
3730
4017
|
### Repository Update Statements
|
|
3731
4018
|
```js
|
|
3732
4019
|
|
|
3733
|
-
|
|
4020
|
+
// Use static methods for flexible querying
|
|
4021
|
+
// const updated = await User.update({...}) // traditional way without repository
|
|
3734
4022
|
|
|
4023
|
+
const userRepository = Repository(User)
|
|
3735
4024
|
const updated = await userRepository.update({
|
|
3736
4025
|
data : {
|
|
3737
4026
|
name : "repository-name",
|
|
@@ -3746,8 +4035,10 @@ const updated = await userRepository.update({
|
|
|
3746
4035
|
### Repository Delete Statements
|
|
3747
4036
|
```js
|
|
3748
4037
|
|
|
3749
|
-
|
|
4038
|
+
// Use static methods for flexible querying
|
|
4039
|
+
// const deleted = await User.delete({...}) // traditional way without repository
|
|
3750
4040
|
|
|
4041
|
+
const userRepository = Repository(User)
|
|
3751
4042
|
const deleted = await userRepository.delete({
|
|
3752
4043
|
where : {
|
|
3753
4044
|
id : 1
|
|
@@ -3806,8 +4097,10 @@ import { Repository , OP } from 'tspace-mysql'
|
|
|
3806
4097
|
import { User } from '../Models/User'
|
|
3807
4098
|
import { Phone } from '../Models/Phone'
|
|
3808
4099
|
|
|
3809
|
-
|
|
4100
|
+
// Use static methods for flexible querying
|
|
4101
|
+
// const userHasPhones = await User.findOne({...}) // traditional way without repository
|
|
3810
4102
|
|
|
4103
|
+
const userRepository = Repository(User)
|
|
3811
4104
|
const userHasPhones = await userRepository.findOne({
|
|
3812
4105
|
select : {
|
|
3813
4106
|
id : true,
|
|
@@ -2,6 +2,7 @@ import { StateManager } from "../StateManager";
|
|
|
2
2
|
import { TUtils } from "../../utils";
|
|
3
3
|
import { Join } from "../Join";
|
|
4
4
|
import type { TPagination, TConstant, TDriver } from "../../types";
|
|
5
|
+
import { Builder } from "../Builder";
|
|
5
6
|
declare abstract class AbstractBuilder {
|
|
6
7
|
protected $utils: TUtils;
|
|
7
8
|
protected $database: string;
|
|
@@ -49,7 +50,6 @@ declare abstract class AbstractBuilder {
|
|
|
49
50
|
abstract whereIn(column: string, arrayValues: any[]): this;
|
|
50
51
|
abstract orWhereIn(column: string, arrayValues: any[]): this;
|
|
51
52
|
abstract whereNotIn(column: string, arrayValues: any[]): this;
|
|
52
|
-
abstract whereSubQuery(column: string, subQuery: string): this;
|
|
53
53
|
abstract whereNotSubQuery(column: string, subQuery: string): this;
|
|
54
54
|
abstract orWhereSubQuery(column: string, subQuery: string): this;
|
|
55
55
|
abstract whereBetween(column: string, arrayValue: [any, any]): this;
|
|
@@ -77,30 +77,31 @@ declare abstract class AbstractBuilder {
|
|
|
77
77
|
abstract limit(number: number): this;
|
|
78
78
|
abstract insert(data: Record<string, any>, { database }: {
|
|
79
79
|
database?: string;
|
|
80
|
-
}):
|
|
80
|
+
}): Builder<"create">;
|
|
81
81
|
abstract create(data: Record<string, any>, { database }: {
|
|
82
82
|
database?: string;
|
|
83
|
-
}):
|
|
84
|
-
abstract update(data: Record<string, any>, updateNotExists?: string[]):
|
|
85
|
-
abstract updateMany(data: Record<string, any>, updateNotExists?: string[]):
|
|
86
|
-
abstract insertNotExists(data: Record<string, any>):
|
|
87
|
-
abstract createNotExists(data: Record<string, any>):
|
|
88
|
-
abstract insertOrUpdate(data: Record<string, any>):
|
|
89
|
-
abstract createOrUpdate(data: Record<string, any>):
|
|
90
|
-
abstract updateOrInsert(data: Record<string, any>):
|
|
91
|
-
abstract updateOrCreate(data: Record<string, any>):
|
|
83
|
+
}): Builder<"create">;
|
|
84
|
+
abstract update(data: Record<string, any>, updateNotExists?: string[]): Builder<"update">;
|
|
85
|
+
abstract updateMany(data: Record<string, any>, updateNotExists?: string[]): Builder<"updateMany">;
|
|
86
|
+
abstract insertNotExists(data: Record<string, any>): Builder<"create"> | null;
|
|
87
|
+
abstract createNotExists(data: Record<string, any>): Builder<"create"> | null;
|
|
88
|
+
abstract insertOrUpdate(data: Record<string, any>): Builder<"createOrUpdate">;
|
|
89
|
+
abstract createOrUpdate(data: Record<string, any>): Builder<"createOrUpdate">;
|
|
90
|
+
abstract updateOrInsert(data: Record<string, any>): Builder<"createOrUpdate">;
|
|
91
|
+
abstract updateOrCreate(data: Record<string, any>): Builder<"createOrUpdate">;
|
|
92
92
|
abstract createMultiple(data: Record<string, any>[], { database }: {
|
|
93
93
|
database?: string;
|
|
94
|
-
}):
|
|
94
|
+
}): Builder<"createMany">;
|
|
95
95
|
abstract insertMultiple(data: Record<string, any>[], { database }: {
|
|
96
96
|
database?: string;
|
|
97
|
-
}):
|
|
97
|
+
}): Builder<"createMany">;
|
|
98
98
|
abstract createMany(data: Record<string, any>[], { database }: {
|
|
99
99
|
database?: string;
|
|
100
|
-
}):
|
|
100
|
+
}): Builder<"createMany">;
|
|
101
101
|
abstract insertMany(data: Record<string, any>[], { database }: {
|
|
102
102
|
database?: string;
|
|
103
|
-
}):
|
|
103
|
+
}): Builder<"createMany">;
|
|
104
|
+
abstract save(): Promise<any>;
|
|
104
105
|
abstract except(...columns: string[]): this;
|
|
105
106
|
abstract drop(): Promise<any>;
|
|
106
107
|
abstract truncate({ force }: {
|
|
@@ -137,7 +138,6 @@ declare abstract class AbstractBuilder {
|
|
|
137
138
|
abstract delete(): Promise<boolean>;
|
|
138
139
|
abstract deleteMany(): Promise<boolean>;
|
|
139
140
|
abstract exists(): Promise<boolean>;
|
|
140
|
-
abstract save(): Promise<Record<string, any> | any[] | null | undefined>;
|
|
141
141
|
abstract increment(column: string, value: number): Promise<number>;
|
|
142
142
|
abstract decrement(column: string, value: number): Promise<number>;
|
|
143
143
|
abstract faker(round: number, cb?: Function): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractBuilder.js","sourceRoot":"","sources":["../../../../src/lib/core/Abstracts/AbstractBuilder.ts"],"names":[],"mappings":";;;AAGA,yCAA4C;
|
|
1
|
+
{"version":3,"file":"AbstractBuilder.js","sourceRoot":"","sources":["../../../../src/lib/core/Abstracts/AbstractBuilder.ts"],"names":[],"mappings":";;;AAGA,yCAA4C;AAO5C,MAAe,eAAe;IAEhB,MAAM,CAAU;IAEhB,SAAS,GAAY,MAAM,CAAC,eAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAEnD,YAAY,GAAG;QACrB,IAAI,EAAO,eAAM,CAAC,IAAI;QACtB,IAAI,EAAO,MAAM,CAAC,eAAM,CAAC,IAAI,CAAC;QAC9B,QAAQ,EAAG,MAAM,CAAC,eAAM,CAAC,QAAQ,CAAC;QAClC,QAAQ,EAAG,MAAM,CAAC,eAAM,CAAC,QAAQ,CAAC;QAClC,QAAQ,EAAG,eAAM,CAAC,QAAQ;KAC7B,CAAA;IAES,OAAO,GAAI,MAAM,CAAC,eAAM,CAAC,MAAM,IAAI,OAAO,CAAY,CAAC;IAEvD,QAAQ,GAAa,OAAO,CAAC,eAAM,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;IAEtD,UAAU,CAGlB;IAEQ,MAAM,CAAgB;IAEtB,KAAK,GAMX;QACA,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,GAAE,CAAC;QAC1B,GAAG,EAAE,CAAC,IAAU,EAAE,EAAE,GAAE,CAAC;QACvB,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC;QACb,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;QACtB,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC;KACxB,CAAA;CAyFJ;AAES,0CAAe;AACzB,kBAAe,eAAe,CAAA"}
|
|
@@ -18,7 +18,7 @@ declare abstract class AbstractDB extends Builder {
|
|
|
18
18
|
when: string;
|
|
19
19
|
then: string;
|
|
20
20
|
}[], final?: string): string | [];
|
|
21
|
-
abstract getConnection(options: TConnectionOptions):
|
|
21
|
+
abstract getConnection(options: TConnectionOptions): TPoolConnected;
|
|
22
22
|
}
|
|
23
23
|
export { AbstractDB };
|
|
24
24
|
export default AbstractDB;
|
|
@@ -3,8 +3,9 @@ import { Builder } from '../Builder';
|
|
|
3
3
|
import { RelationManager } from '../RelationManager';
|
|
4
4
|
import { Model } from '../Model';
|
|
5
5
|
import type { T } from '../UtilityTypes';
|
|
6
|
-
import type { TPattern, TValidateSchema, TCache, TRelationOptions, TRelationQueryOptions } from '../../types';
|
|
7
|
-
|
|
6
|
+
import type { TPattern, TValidateSchema, TCache, TRelationOptions, TRelationQueryOptions, TModelOrObject } from '../../types';
|
|
7
|
+
import { JoinModel } from '../JoinModel';
|
|
8
|
+
declare abstract class AbstractModel extends Builder<any> {
|
|
8
9
|
protected $cache: TCache;
|
|
9
10
|
protected $relation: RelationManager;
|
|
10
11
|
protected $schema: Record<string, Blueprint>;
|
|
@@ -76,6 +77,10 @@ declare abstract class AbstractModel extends Builder {
|
|
|
76
77
|
abstract relationsAll<K extends T.RelationKeys<this>>(...nameRelations: K[]): this;
|
|
77
78
|
abstract relationsCount<K extends T.RelationKeys<this>>(...nameRelations: K[]): this;
|
|
78
79
|
abstract relationsTrashed<K extends T.RelationKeys<this>>(...nameRelations: K[]): this;
|
|
80
|
+
abstract joinModel(m1: TModelOrObject | ((join: JoinModel) => JoinModel), m2?: TModelOrObject): this;
|
|
81
|
+
abstract rightJoinModel(m1: TModelOrObject | ((join: JoinModel) => JoinModel), m2?: TModelOrObject): this;
|
|
82
|
+
abstract leftJoinModel(m1: TModelOrObject | ((join: JoinModel) => JoinModel), m2?: TModelOrObject): this;
|
|
83
|
+
abstract crossJoinModel(m1: TModelOrObject | ((join: JoinModel) => JoinModel), m2?: TModelOrObject): this;
|
|
79
84
|
}
|
|
80
85
|
export { AbstractModel };
|
|
81
86
|
export default AbstractModel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractModel.js","sourceRoot":"","sources":["../../../../src/lib/core/Abstracts/AbstractModel.ts"],"names":[],"mappings":";;;AACA,wCAA8C;
|
|
1
|
+
{"version":3,"file":"AbstractModel.js","sourceRoot":"","sources":["../../../../src/lib/core/Abstracts/AbstractModel.ts"],"names":[],"mappings":";;;AACA,wCAA8C;AAa9C,MAAe,aAAc,SAAQ,iBAAY;IAEnC,MAAM,CAAsB;IAC5B,SAAS,CAA4B;IACrC,OAAO,CAAyC;IAChD,eAAe,CAAsB;IACrC,MAAM,CAAsB;IAC5B,QAAQ,CAAsB;IAC9B,QAAQ,CAAqC;IAC7C,OAAO,CAAsC;IAC7C,UAAU,CAAmC;IAC7C,cAAc,CAA+B;IAC7C,UAAU,CAAmB;IAC7B,WAAW,CAAkB;IAC7B,KAAK,CAAwB;IAC7B,WAAW,CAAiB;IAC5B,iBAAiB,CAAgD;IACjE,iBAAiB,CAAW;IAC5B,SAAS,CAKjB;IACQ,WAAW,CAGZ;CAuJZ;AAEQ,sCAAa;AACtB,kBAAe,aAAa,CAAA"}
|