tspace-mysql 1.8.9 → 1.9.0-beta.2
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 +4287 -125
- package/dist/cli/generate/make.js +30 -0
- package/dist/cli/generate/make.js.map +1 -0
- package/{build → dist}/cli/index.js +1 -0
- package/{build → dist}/cli/index.js.map +1 -1
- package/{build → dist}/cli/migrations/make-model.js +17 -7
- package/dist/cli/migrations/make-model.js.map +1 -0
- package/{build → dist}/lib/config/index.d.ts +9 -15
- package/dist/lib/config/index.js +87 -0
- package/dist/lib/config/index.js.map +1 -0
- package/{build → dist}/lib/constants/index.d.ts +14 -2
- package/{build → dist}/lib/constants/index.js +15 -3
- package/dist/lib/constants/index.js.map +1 -0
- package/{build → dist}/lib/core/Abstracts/AbstractBuilder.d.ts +12 -19
- package/dist/lib/core/Abstracts/AbstractBuilder.js +29 -0
- package/dist/lib/core/Abstracts/AbstractBuilder.js.map +1 -0
- package/dist/lib/core/Abstracts/AbstractModel.d.ts +81 -0
- package/{build → dist}/lib/core/Abstracts/AbstractModel.js +1 -0
- package/dist/lib/core/Abstracts/AbstractModel.js.map +1 -0
- package/{build → dist}/lib/core/Abstracts/AbstractView.js.map +1 -1
- package/{build → dist}/lib/core/Blueprint.d.ts +49 -18
- package/{build → dist}/lib/core/Blueprint.js +77 -16
- package/dist/lib/core/Blueprint.js.map +1 -0
- package/{build → dist}/lib/core/Builder.d.ts +460 -71
- package/{build → dist}/lib/core/Builder.js +1212 -676
- package/dist/lib/core/Builder.js.map +1 -0
- package/{build → dist}/lib/core/Cache/DBCache.js +23 -24
- package/dist/lib/core/Cache/DBCache.js.map +1 -0
- package/{build → dist}/lib/core/Cache/MemoryCache.js +3 -3
- package/{build → dist}/lib/core/Cache/MemoryCache.js.map +1 -1
- package/dist/lib/core/Cache/RedisCache.js +76 -0
- package/dist/lib/core/Cache/RedisCache.js.map +1 -0
- package/{build → dist}/lib/core/Cache/index.js +5 -4
- package/dist/lib/core/Cache/index.js.map +1 -0
- package/dist/lib/core/Contracts/AlterTable.d.ts +152 -0
- package/dist/lib/core/Contracts/AlterTable.js +243 -0
- package/dist/lib/core/Contracts/AlterTable.js.map +1 -0
- package/dist/lib/core/Contracts/Audit.d.ts +27 -0
- package/{build → dist}/lib/core/Contracts/Audit.js +2 -5
- package/dist/lib/core/Contracts/Audit.js.map +1 -0
- package/dist/lib/core/Contracts/Logger.d.ts +24 -0
- package/{build → dist}/lib/core/DB.d.ts +138 -10
- package/{build → dist}/lib/core/DB.js +341 -72
- package/dist/lib/core/DB.js.map +1 -0
- package/dist/lib/core/Decorator.d.ts +550 -0
- package/dist/lib/core/Decorator.js +773 -0
- package/dist/lib/core/Decorator.js.map +1 -0
- package/dist/lib/core/Driver/index.d.ts +163 -0
- package/{build → dist}/lib/core/Driver/index.js +38 -37
- package/dist/lib/core/Driver/index.js.map +1 -0
- package/{build → dist}/lib/core/Driver/mariadb/MariadbDriver.d.ts +4 -1
- package/{build → dist}/lib/core/Driver/mariadb/MariadbDriver.js +73 -32
- package/dist/lib/core/Driver/mariadb/MariadbDriver.js.map +1 -0
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.d.ts +142 -0
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js +639 -0
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js.map +1 -0
- package/dist/lib/core/Driver/mongodb/MongodbDriver.d.ts +24 -0
- package/dist/lib/core/Driver/mongodb/MongodbDriver.js +255 -0
- package/dist/lib/core/Driver/mongodb/MongodbDriver.js.map +1 -0
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.d.ts +140 -0
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js +553 -0
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js.map +1 -0
- package/{build → dist}/lib/core/Driver/mysql/MysqlDriver.d.ts +4 -1
- package/dist/lib/core/Driver/mysql/MysqlDriver.js +215 -0
- package/dist/lib/core/Driver/mysql/MysqlDriver.js.map +1 -0
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.d.ts +142 -0
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js +654 -0
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js.map +1 -0
- package/{build → dist}/lib/core/Driver/postgres/PostgresDriver.d.ts +4 -1
- package/dist/lib/core/Driver/postgres/PostgresDriver.js +197 -0
- package/dist/lib/core/Driver/postgres/PostgresDriver.js.map +1 -0
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.d.ts +148 -0
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js +908 -0
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js.map +1 -0
- package/dist/lib/core/Driver/sqlite/SqliteDriver.d.ts +20 -0
- package/dist/lib/core/Driver/sqlite/SqliteDriver.js +192 -0
- package/dist/lib/core/Driver/sqlite/SqliteDriver.js.map +1 -0
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.d.ts +143 -0
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js +685 -0
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js.map +1 -0
- package/{build → dist}/lib/core/JoinModel.d.ts +4 -4
- package/{build → dist}/lib/core/JoinModel.js +2 -2
- package/dist/lib/core/JoinModel.js.map +1 -0
- package/dist/lib/core/Meta.d.ts +159 -0
- package/{build → dist}/lib/core/Meta.js +76 -53
- package/dist/lib/core/Meta.js.map +1 -0
- package/{build → dist}/lib/core/Model.d.ts +465 -343
- package/{build → dist}/lib/core/Model.js +1889 -1289
- package/dist/lib/core/Model.js.map +1 -0
- package/dist/lib/core/Nest/index.d.ts +75 -0
- package/dist/lib/core/Nest/index.js +95 -0
- package/dist/lib/core/Nest/index.js.map +1 -0
- package/dist/lib/core/Operator.d.ts +60 -0
- package/{build → dist}/lib/core/Operator.js +13 -11
- package/dist/lib/core/Operator.js.map +1 -0
- package/{build/lib/tools/index.d.ts → dist/lib/core/Package.d.ts} +11 -3
- package/{build/lib/tools/index.js → dist/lib/core/Package.js} +20 -7
- package/dist/lib/core/Package.js.map +1 -0
- package/{build → dist}/lib/core/Pool.d.ts +7 -8
- package/{build → dist}/lib/core/Pool.js +75 -64
- package/dist/lib/core/Pool.js.map +1 -0
- package/dist/lib/core/Queue.d.ts +239 -0
- package/dist/lib/core/Queue.js +665 -0
- package/dist/lib/core/Queue.js.map +1 -0
- package/{build/lib/core/Handlers/Relation.d.ts → dist/lib/core/RelationManager.d.ts} +6 -8
- package/{build/lib/core/Handlers/Relation.js → dist/lib/core/RelationManager.js} +76 -97
- package/dist/lib/core/RelationManager.js.map +1 -0
- package/{build → dist}/lib/core/Repository.d.ts +79 -78
- package/{build → dist}/lib/core/Repository.js +252 -191
- package/dist/lib/core/Repository.js.map +1 -0
- package/dist/lib/core/Schema.d.ts +379 -0
- package/dist/lib/core/Schema.js +879 -0
- package/dist/lib/core/Schema.js.map +1 -0
- package/dist/lib/core/StateManager.d.ts +194 -0
- package/{build/lib/core/Handlers/State.js → dist/lib/core/StateManager.js} +41 -26
- package/dist/lib/core/StateManager.js.map +1 -0
- package/dist/lib/core/UtilityTypes.d.ts +241 -0
- package/dist/lib/core/UtilityTypes.js +4 -0
- package/{build → dist}/lib/core/UtilityTypes.js.map +1 -1
- package/{build → dist}/lib/core/index.d.ts +4 -0
- package/{build → dist}/lib/core/index.js +6 -2
- package/dist/lib/core/index.js.map +1 -0
- package/{build → dist}/lib/index.js +17 -7
- package/{build → dist}/lib/index.js.map +1 -1
- package/dist/lib/types/decorator/index.d.ts +30 -0
- package/dist/lib/types/decorator/index.js.map +1 -0
- package/{build → dist}/lib/types/index.d.ts +99 -123
- package/{build/lib/core/UtilityTypes.js → dist/lib/types/index.js} +1 -1
- package/dist/lib/types/repository/index.d.ts +166 -0
- package/dist/lib/types/repository/index.js +3 -0
- package/dist/lib/types/repository/index.js.map +1 -0
- package/dist/lib/utils/index.d.ts +65 -0
- package/{build → dist}/lib/utils/index.js +190 -31
- package/dist/lib/utils/index.js.map +1 -0
- package/package.json +30 -18
- package/build/cli/generate/make.js +0 -146
- package/build/cli/generate/make.js.map +0 -1
- package/build/cli/generate/model.d.ts +0 -2
- package/build/cli/generate/model.js +0 -31
- package/build/cli/generate/model.js.map +0 -1
- package/build/cli/generate/modelDecorator.d.ts +0 -2
- package/build/cli/generate/modelDecorator.js +0 -15
- package/build/cli/generate/modelDecorator.js.map +0 -1
- package/build/cli/migrations/make-model.js.map +0 -1
- package/build/lib/config/index.js +0 -92
- package/build/lib/config/index.js.map +0 -1
- package/build/lib/constants/index.js.map +0 -1
- package/build/lib/core/Abstracts/AbstractBuilder.js +0 -47
- package/build/lib/core/Abstracts/AbstractBuilder.js.map +0 -1
- package/build/lib/core/Abstracts/AbstractModel.d.ts +0 -78
- package/build/lib/core/Abstracts/AbstractModel.js.map +0 -1
- package/build/lib/core/Blueprint.js.map +0 -1
- package/build/lib/core/Builder.js.map +0 -1
- package/build/lib/core/Cache/DBCache.js.map +0 -1
- package/build/lib/core/Cache/RedisCache.js +0 -66
- package/build/lib/core/Cache/RedisCache.js.map +0 -1
- package/build/lib/core/Cache/index.js.map +0 -1
- package/build/lib/core/Contracts/Audit.d.ts +0 -27
- package/build/lib/core/Contracts/Audit.js.map +0 -1
- package/build/lib/core/Contracts/Logger.d.ts +0 -24
- package/build/lib/core/DB.js.map +0 -1
- package/build/lib/core/Decorator.d.ts +0 -98
- package/build/lib/core/Decorator.js +0 -253
- package/build/lib/core/Decorator.js.map +0 -1
- package/build/lib/core/Driver/index.d.ts +0 -113
- package/build/lib/core/Driver/index.js.map +0 -1
- package/build/lib/core/Driver/mariadb/MariadbDriver.js.map +0 -1
- package/build/lib/core/Driver/mariadb/MariadbQueryBuilder.d.ts +0 -90
- package/build/lib/core/Driver/mariadb/MariadbQueryBuilder.js +0 -345
- package/build/lib/core/Driver/mariadb/MariadbQueryBuilder.js.map +0 -1
- package/build/lib/core/Driver/mysql/MysqlDriver.js +0 -164
- package/build/lib/core/Driver/mysql/MysqlDriver.js.map +0 -1
- package/build/lib/core/Driver/mysql/MysqlQueryBuilder.d.ts +0 -94
- package/build/lib/core/Driver/mysql/MysqlQueryBuilder.js +0 -362
- package/build/lib/core/Driver/mysql/MysqlQueryBuilder.js.map +0 -1
- package/build/lib/core/Driver/postgres/PostgresDriver.js +0 -167
- package/build/lib/core/Driver/postgres/PostgresDriver.js.map +0 -1
- package/build/lib/core/Driver/postgres/PostgresQueryBuilder.d.ts +0 -91
- package/build/lib/core/Driver/postgres/PostgresQueryBuilder.js +0 -455
- package/build/lib/core/Driver/postgres/PostgresQueryBuilder.js.map +0 -1
- package/build/lib/core/Handlers/Logger.d.ts +0 -8
- package/build/lib/core/Handlers/Logger.js +0 -50
- package/build/lib/core/Handlers/Logger.js.map +0 -1
- package/build/lib/core/Handlers/Proxy.d.ts +0 -14
- package/build/lib/core/Handlers/Proxy.js +0 -33
- package/build/lib/core/Handlers/Proxy.js.map +0 -1
- package/build/lib/core/Handlers/Relation.js.map +0 -1
- package/build/lib/core/Handlers/State.d.ts +0 -166
- package/build/lib/core/Handlers/State.js.map +0 -1
- package/build/lib/core/JoinModel.js.map +0 -1
- package/build/lib/core/Meta.d.ts +0 -128
- package/build/lib/core/Meta.js.map +0 -1
- package/build/lib/core/Model.js.map +0 -1
- package/build/lib/core/Nest/index.d.ts +0 -9
- package/build/lib/core/Nest/index.js +0 -23
- package/build/lib/core/Nest/index.js.map +0 -1
- package/build/lib/core/Operator.d.ts +0 -58
- package/build/lib/core/Operator.js.map +0 -1
- package/build/lib/core/Pool.js.map +0 -1
- package/build/lib/core/Repository.js.map +0 -1
- package/build/lib/core/Schema.d.ts +0 -157
- package/build/lib/core/Schema.js +0 -527
- package/build/lib/core/Schema.js.map +0 -1
- package/build/lib/core/UtilityTypes.d.ts +0 -278
- package/build/lib/core/index.js.map +0 -1
- package/build/lib/options/index.d.ts +0 -27
- package/build/lib/options/index.js +0 -88
- package/build/lib/options/index.js.map +0 -1
- package/build/lib/tools/index.js.map +0 -1
- package/build/lib/utils/index.d.ts +0 -33
- package/build/lib/utils/index.js.map +0 -1
- package/build/tests/00-Driver.test.d.ts +0 -1
- package/build/tests/00-Driver.test.js +0 -62
- package/build/tests/00-Driver.test.js.map +0 -1
- package/build/tests/01-Pool.test.d.ts +0 -1
- package/build/tests/01-Pool.test.js +0 -28
- package/build/tests/01-Pool.test.js.map +0 -1
- package/build/tests/02-DB.test.d.ts +0 -1
- package/build/tests/02-DB.test.js +0 -91
- package/build/tests/02-DB.test.js.map +0 -1
- package/build/tests/03-Model-default.test.d.ts +0 -1
- package/build/tests/03-Model-default.test.js +0 -322
- package/build/tests/03-Model-default.test.js.map +0 -1
- package/build/tests/03-Transaction.test.d.ts +0 -1
- package/build/tests/03-Transaction.test.js +0 -167
- package/build/tests/03-Transaction.test.js.map +0 -1
- package/build/tests/04-Model-default.test.d.ts +0 -1
- package/build/tests/04-Model-default.test.js +0 -392
- package/build/tests/04-Model-default.test.js.map +0 -1
- package/build/tests/04-Model-pattern.test.d.ts +0 -1
- package/build/tests/04-Model-pattern.test.js +0 -323
- package/build/tests/04-Model-pattern.test.js.map +0 -1
- package/build/tests/04.1-Model-camelCase.test.d.ts +0 -1
- package/build/tests/04.1-Model-camelCase.test.js +0 -392
- package/build/tests/04.1-Model-camelCase.test.js.map +0 -1
- package/build/tests/04.2-Model-snake-case.test.d.ts +0 -1
- package/build/tests/04.2-Model-snake-case.test.js +0 -392
- package/build/tests/04.2-Model-snake-case.test.js.map +0 -1
- package/build/tests/05-Repository.test.d.ts +0 -0
- package/build/tests/05-Repository.test.js +0 -2
- package/build/tests/05-Repository.test.js.map +0 -1
- package/build/tests/05-View.test.d.ts +0 -1
- package/build/tests/05-View.test.js +0 -72
- package/build/tests/05-View.test.js.map +0 -1
- package/build/tests/06-Meta.test.d.ts +0 -1
- package/build/tests/06-Meta.test.js +0 -95
- package/build/tests/06-Meta.test.js.map +0 -1
- package/build/tests/07-View.test.d.ts +0 -1
- package/build/tests/07-View.test.js +0 -72
- package/build/tests/07-View.test.js.map +0 -1
- package/build/tests/07-Virtual-column.test.d.ts +0 -1
- package/build/tests/07-Virtual-column.test.js +0 -143
- package/build/tests/07-Virtual-column.test.js.map +0 -1
- package/build/tests/08-Virtual-column.test.d.ts +0 -1
- package/build/tests/08-Virtual-column.test.js +0 -131
- package/build/tests/08-Virtual-column.test.js.map +0 -1
- package/build/tests/benchmark.test.d.ts +0 -1
- package/build/tests/benchmark.test.js +0 -50
- package/build/tests/benchmark.test.js.map +0 -1
- package/build/tests/default-spec.d.ts +0 -254
- package/build/tests/default-spec.js +0 -111
- package/build/tests/default-spec.js.map +0 -1
- package/build/tests/mock-data-spec.d.ts +0 -42
- package/build/tests/mock-data-spec.js +0 -64
- package/build/tests/mock-data-spec.js.map +0 -1
- package/build/tests/schema-spec.d.ts +0 -245
- package/build/tests/schema-spec.js +0 -123
- package/build/tests/schema-spec.js.map +0 -1
- /package/{build → dist}/cli/dump/db.d.ts +0 -0
- /package/{build → dist}/cli/dump/db.js +0 -0
- /package/{build → dist}/cli/dump/db.js.map +0 -0
- /package/{build → dist}/cli/dump/table.d.ts +0 -0
- /package/{build → dist}/cli/dump/table.js +0 -0
- /package/{build → dist}/cli/dump/table.js.map +0 -0
- /package/{build → dist}/cli/generate/make.d.ts +0 -0
- /package/{build → dist}/cli/index.d.ts +0 -0
- /package/{build → dist}/cli/migrate/make.d.ts +0 -0
- /package/{build → dist}/cli/migrate/make.js +0 -0
- /package/{build → dist}/cli/migrate/make.js.map +0 -0
- /package/{build → dist}/cli/migrations/make-db.d.ts +0 -0
- /package/{build → dist}/cli/migrations/make-db.js +0 -0
- /package/{build → dist}/cli/migrations/make-db.js.map +0 -0
- /package/{build → dist}/cli/migrations/make-model.d.ts +0 -0
- /package/{build → dist}/cli/models/make.d.ts +0 -0
- /package/{build → dist}/cli/models/make.js +0 -0
- /package/{build → dist}/cli/models/make.js.map +0 -0
- /package/{build → dist}/cli/models/model.d.ts +0 -0
- /package/{build → dist}/cli/models/model.js +0 -0
- /package/{build → dist}/cli/models/model.js.map +0 -0
- /package/{build → dist}/cli/query/index.d.ts +0 -0
- /package/{build → dist}/cli/query/index.js +0 -0
- /package/{build → dist}/cli/query/index.js.map +0 -0
- /package/{build → dist}/cli/tables/make.d.ts +0 -0
- /package/{build → dist}/cli/tables/make.js +0 -0
- /package/{build → dist}/cli/tables/make.js.map +0 -0
- /package/{build → dist}/cli/tables/table.d.ts +0 -0
- /package/{build → dist}/cli/tables/table.js +0 -0
- /package/{build → dist}/cli/tables/table.js.map +0 -0
- /package/{build → dist}/lib/core/Abstracts/AbstractDB.d.ts +0 -0
- /package/{build → dist}/lib/core/Abstracts/AbstractDB.js +0 -0
- /package/{build → dist}/lib/core/Abstracts/AbstractDB.js.map +0 -0
- /package/{build → dist}/lib/core/Abstracts/AbstractView.d.ts +0 -0
- /package/{build → dist}/lib/core/Abstracts/AbstractView.js +0 -0
- /package/{build → dist}/lib/core/Cache/DBCache.d.ts +0 -0
- /package/{build → dist}/lib/core/Cache/MemoryCache.d.ts +0 -0
- /package/{build → dist}/lib/core/Cache/RedisCache.d.ts +0 -0
- /package/{build → dist}/lib/core/Cache/index.d.ts +0 -0
- /package/{build → dist}/lib/core/Contracts/Logger.js +0 -0
- /package/{build → dist}/lib/core/Contracts/Logger.js.map +0 -0
- /package/{build → dist}/lib/core/Join.d.ts +0 -0
- /package/{build → dist}/lib/core/Join.js +0 -0
- /package/{build → dist}/lib/core/Join.js.map +0 -0
- /package/{build → dist}/lib/core/SqlLike.d.ts +0 -0
- /package/{build → dist}/lib/core/SqlLike.js +0 -0
- /package/{build → dist}/lib/core/SqlLike.js.map +0 -0
- /package/{build → dist}/lib/core/StoredProcedure.d.ts +0 -0
- /package/{build → dist}/lib/core/StoredProcedure.js +0 -0
- /package/{build → dist}/lib/core/StoredProcedure.js.map +0 -0
- /package/{build → dist}/lib/core/View.d.ts +0 -0
- /package/{build → dist}/lib/core/View.js +0 -0
- /package/{build → dist}/lib/core/View.js.map +0 -0
- /package/{build → dist}/lib/index.d.ts +0 -0
- /package/{build/lib/types → dist/lib/types/decorator}/index.js +0 -0
- /package/{build → dist}/lib/types/index.js.map +0 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ tspace-mysql is an Object-Relational Mapping (ORM) tool designed to run seamless
|
|
|
10
10
|
|
|
11
11
|
| **Feature** | **Description** |
|
|
12
12
|
|--------------------------------|---------------------------------------------------------------------------------------------------------|
|
|
13
|
-
| **Supports Driver** | MySQL ✅ / MariaDB ✅ / Postgres / ✅
|
|
13
|
+
| **Supports Driver** | MySQL ✅ / MariaDB ✅ / Postgres ✅ / SQLite ✅ / Mongodb ✅ (yes, even MongoDB 😏) / MSSQL ⏳ |
|
|
14
14
|
| **Query Builder** | Create flexible queries like `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. You can also use raw SQL. |
|
|
15
15
|
| **Join Clauses** | Use `INNER JOIN`, `LEFT JOIN`, `RIGHT JOIN`, and `CROSS JOIN` to combine data from multiple tables. |
|
|
16
16
|
| **Model** | Provides a way to interact with database records as objects in code. You can perform create, read, update, and delete (CRUD) operations. Models also support soft deletes and relationship methods. |
|
|
@@ -24,6 +24,7 @@ tspace-mysql is an Object-Relational Mapping (ORM) tool designed to run seamless
|
|
|
24
24
|
| **Repository** | Follows a pattern for managing database operations like `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. It helps keep the code organized. |
|
|
25
25
|
| **Decorators** | Use decorators to add extra functionality or information to model classes and methods, making the code easier to read. |
|
|
26
26
|
| **Caching** | Improves performance by storing frequently requested data. Supports in-memory caching (like memory DB) and Redis for distributed caching. |
|
|
27
|
+
| **Queue** | Job queue for background and async processing. Runs on top of databases for distributed workers (similar to pg-boss). |
|
|
27
28
|
| **Migrations** | Use CLI commands to create models, make migrations, and apply changes to the database structure. |
|
|
28
29
|
| **Blueprints** | Create a clear layout of the database structure and how models and tables relate to each other. |
|
|
29
30
|
| **CLI** | A Command Line Interface for managing models, running migrations, executing queries, and performing other tasks using commands (like `make:model`, `migrate`, and `query`). |
|
|
@@ -33,140 +34,4301 @@ tspace-mysql is an Object-Relational Mapping (ORM) tool designed to run seamless
|
|
|
33
34
|
Install with [npm](https://www.npmjs.com/):
|
|
34
35
|
|
|
35
36
|
```sh
|
|
36
|
-
|
|
37
|
+
## Install tspace-mysql locally for your project
|
|
37
38
|
npm install tspace-mysql --save
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
## Install tspace-mysql globally (optional)
|
|
40
41
|
npm install -g tspace-mysql
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
See the [`docs`](https://thanathip41.github.io/tspace-mysql) directory for full documentation.
|
|
47
|
+
|
|
48
|
+
## TypeScript
|
|
49
|
+
|
|
50
|
+
The TypeScript version is specified only for **development and build-time** purposes and does **not** restrict how consumers use the library.
|
|
51
|
+
|
|
52
|
+
This library is built using **TypeScript 5.9.3**.
|
|
53
|
+
The minimum supported **TypeScript version is >= 5.6.2**.
|
|
54
|
+
|
|
55
|
+
If you are contributing to this library or building it locally, install the pinned TypeScript version:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
|
|
59
|
+
npm install -D typescript@5.9.3
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Basic Usage
|
|
41
64
|
|
|
42
|
-
#
|
|
43
|
-
|
|
65
|
+
- [Configuration](#configuration)
|
|
66
|
+
- [MySQL Database](#mysql-database)
|
|
67
|
+
- [Mariadb Database](#mariadb-database)
|
|
68
|
+
- [Postgres Database](#postgres-database)
|
|
69
|
+
- [Cluster Database](#cluster-database)
|
|
70
|
+
- [SQL Like](#sql-Like)
|
|
71
|
+
- [Query Builder](#query-builder)
|
|
72
|
+
- [Table Name & Alias Name](#table-name--alias-name)
|
|
73
|
+
- [Returning Results](#returning-results)
|
|
74
|
+
- [Query Statement](#query-statements)
|
|
75
|
+
- [Select Statements](#select-statements)
|
|
76
|
+
- [Raw Expressions](#raw-expressions)
|
|
77
|
+
- [Ordering, Grouping, Limit and Offset](#ordering-grouping-limit-and-offset)
|
|
78
|
+
- [Ordering](#ordering)
|
|
79
|
+
- [Grouping](#grouping)
|
|
80
|
+
- [Limit and Offset](#limit-and-offset)
|
|
81
|
+
- [Joins](#joins)
|
|
82
|
+
- [Inner Join Clause](#inner-join-clause)
|
|
83
|
+
- [Left Join, Right Join Clause](#left-join-right-join-clause)
|
|
84
|
+
- [Cross Join Clause](#cross-join-clause)
|
|
85
|
+
- [Basic Where Clauses](#basic-where-clauses)
|
|
86
|
+
- [Where Clauses](#where-clauses)
|
|
87
|
+
- [Or Where Clauses](#or-where-clauses)
|
|
88
|
+
- [Where cases](#where-cases)
|
|
89
|
+
- [Where Object Clauses](#where-object-clauses)
|
|
90
|
+
- [JSON Where Clauses](#json-where-clauses)
|
|
91
|
+
- [Additional Where Clauses](#additional-where-clauses)
|
|
92
|
+
- [Logical Grouping](#logical-grouping)
|
|
93
|
+
- [Advanced Where Clauses](#advanced-where-clauses)
|
|
94
|
+
- [Where Exists Clauses](#where-exists-clauses)
|
|
95
|
+
- [Subquery Where Clauses](#subquery-where-clauses)
|
|
96
|
+
- [Conditional Where Clauses](#conditional-where-clauses)
|
|
97
|
+
- [GetGroupBy](#getgroupby)
|
|
98
|
+
- [Paginating](#paginating)
|
|
99
|
+
- [Insert Statements](#insert-statements)
|
|
100
|
+
- [Update Statements](#update-statements)
|
|
101
|
+
- [Delete Statements](#delete-statements)
|
|
102
|
+
- [Hook Statements](#hook-statements)
|
|
103
|
+
- [Faker Statements](#faker-statements)
|
|
104
|
+
- [Unset Statements](#unset-statements)
|
|
105
|
+
- [Common Table Expressions](#common-table-expressions)
|
|
106
|
+
- [Union](#union)
|
|
107
|
+
- [More Methods](#more-methods)
|
|
108
|
+
- [Database Transactions](#database-transactions)
|
|
109
|
+
- [Race Condition](#race-condition)
|
|
110
|
+
- [Connection](#connection)
|
|
111
|
+
- [Backup](#backup)
|
|
112
|
+
- [Injection](#injection)
|
|
113
|
+
- [Generating Model Classes](#generating-model-classes)
|
|
114
|
+
- [Model Conventions](#model-conventions)
|
|
115
|
+
- [Basic Model Setup](#basic-model-setup)
|
|
116
|
+
- [Table Name](#table-name)
|
|
117
|
+
- [Pattern](#pattern)
|
|
118
|
+
- [UUID](#uuid)
|
|
119
|
+
- [Timestamp](#timestamp)
|
|
120
|
+
- [Debug](#debug)
|
|
121
|
+
- [Observer](#observer)
|
|
122
|
+
- [Logger](#logger)
|
|
123
|
+
- [Hooks](#hooks)
|
|
124
|
+
- [Global Scope](#global-scope)
|
|
125
|
+
- [Schema](#schema)
|
|
126
|
+
- [Schema Model](#schema-model)
|
|
127
|
+
- [Virtual Column](#virtual-column)
|
|
128
|
+
- [Validation](#validation)
|
|
129
|
+
- [Sync](#sync)
|
|
130
|
+
- [SoftDelete](#softdelete)
|
|
131
|
+
- [Joins Model](#joins-model)
|
|
132
|
+
- [Inner Join Model Clause](#inner-join-model-clause)
|
|
133
|
+
- [Left Join , Right Join Model Clause](#left-join-right-join-model-clause)
|
|
134
|
+
- [Cross Join Model Clause](#cross-join-model-clause)
|
|
135
|
+
- [Relationships](#relationships)
|
|
136
|
+
- [One To One](#one-to-one)
|
|
137
|
+
- [One To Many](#one-to-many)
|
|
138
|
+
- [Belongs To](#belongs-to)
|
|
139
|
+
- [Many To Many](#many-to-many)
|
|
140
|
+
- [Relation](#relation)
|
|
141
|
+
- [Deeply Nested Relations](#deeply-nested-relations)
|
|
142
|
+
- [Relation Exists](#relation-exists)
|
|
143
|
+
- [Relation Count](#relation-count)
|
|
144
|
+
- [Relation Trashed](#relation-trashed)
|
|
145
|
+
- [Built in Relation Functions](#built-in-relation-functions)
|
|
146
|
+
- [Cache](#cache)
|
|
147
|
+
- [Decorator](#decorator)
|
|
148
|
+
- [Type Safety](#type-safety)
|
|
149
|
+
- [Type Safety Select](#type-safety-select)
|
|
150
|
+
- [Type Safety OrderBy](#type-safety-order-by)
|
|
151
|
+
- [Type Safety GroupBy](#type-safety-group-by)
|
|
152
|
+
- [Type Safety Where](#type-safety-where)
|
|
153
|
+
- [Type Safety Insert](#type-safety-insert)
|
|
154
|
+
- [Type Safety Update](#type-safety-update)
|
|
155
|
+
- [Type Safety Delete](#type-safety-delete)
|
|
156
|
+
- [Type Safety Relationships](#type-safety-relationships)
|
|
157
|
+
- [Type Safety Results](#type-safety-results)
|
|
158
|
+
- [Metadata](#metadata)
|
|
159
|
+
- [Audit](#audit)
|
|
160
|
+
- [Repository](#repository)
|
|
161
|
+
- [Repository Select Statements](#repository-select-statements)
|
|
162
|
+
- [Repository Insert Statements](#repository-insert-statements)
|
|
163
|
+
- [Repository Update Statements](#repository-update-statements)
|
|
164
|
+
- [Repository Delete Statements](#repository-delete-statements)
|
|
165
|
+
- [Repository Transactions](#repository-transactions)
|
|
166
|
+
- [Repository Relations](#repository-relations)
|
|
167
|
+
- [Queue](#queue)
|
|
168
|
+
- [View](#view)
|
|
169
|
+
- [Stored Procedure](#stored-procedure)
|
|
170
|
+
- [Blueprint](#blueprint)
|
|
171
|
+
- [Cli](#cli)
|
|
172
|
+
- [Make Model](#make-model)
|
|
173
|
+
- [Make Migration](#make-migration)
|
|
174
|
+
- [Migrate](#migrate)
|
|
175
|
+
- [Query](#query)
|
|
176
|
+
- [Dump](#dump)
|
|
177
|
+
- [Generate Models](#generate-models)
|
|
178
|
+
- [Migration Models](#migration-models)
|
|
179
|
+
- [Migration DB](#migration-db)
|
|
180
|
+
|
|
181
|
+
## Configuration
|
|
182
|
+
|
|
183
|
+
To establish a connection, the recommended method for creating your environment variables is by using a '.env' file. using the following:
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
DB_HOST = localhost
|
|
187
|
+
DB_PORT = 3306
|
|
188
|
+
DB_USERNAME = root
|
|
189
|
+
DB_PASSWORD = password
|
|
190
|
+
DB_DATABASE = database
|
|
191
|
+
/**
|
|
192
|
+
* @default
|
|
193
|
+
* DB_CONNECTION_LIMIT = 20
|
|
194
|
+
* DB_QUEUE_LIMIT = 0
|
|
195
|
+
* DB_TIMEOUT = 60000
|
|
196
|
+
* DB_DATE_STRINGS = false
|
|
197
|
+
*/
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### MySQL Database
|
|
201
|
+
|
|
202
|
+
To connect the application to a MySQL database, using the following:
|
|
203
|
+
```sh
|
|
204
|
+
npm install mysql2 --save
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
DB_DRIVER = mysql
|
|
209
|
+
DB_HOST = localhost
|
|
210
|
+
DB_PORT = 3306
|
|
211
|
+
DB_USERNAME = root
|
|
212
|
+
DB_PASSWORD = password
|
|
213
|
+
DB_DATABASE = database
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Mariadb Database
|
|
217
|
+
|
|
218
|
+
To connect the application to a Mariadb database, using the following:
|
|
219
|
+
|
|
220
|
+
```sh
|
|
44
221
|
npm install mariadb --save
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
DB_DRIVER = mariadb
|
|
226
|
+
DB_HOST = localhost
|
|
227
|
+
DB_PORT = 3306
|
|
228
|
+
DB_USERNAME = root
|
|
229
|
+
DB_PASSWORD = password
|
|
230
|
+
DB_DATABASE = database
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Postgres Database
|
|
45
234
|
|
|
46
|
-
|
|
235
|
+
To connect the application to a Postgres database, using the following:
|
|
236
|
+
|
|
237
|
+
```sh
|
|
47
238
|
npm install pg --save
|
|
239
|
+
```
|
|
48
240
|
|
|
49
|
-
|
|
241
|
+
```js
|
|
242
|
+
DB_DRIVER = postgres
|
|
243
|
+
DB_HOST = localhost
|
|
244
|
+
DB_PORT = 5432
|
|
245
|
+
DB_USERNAME = root
|
|
246
|
+
DB_PASSWORD = password
|
|
247
|
+
DB_DATABASE = database
|
|
50
248
|
```
|
|
51
249
|
|
|
52
|
-
|
|
250
|
+
### Cluster Database
|
|
251
|
+
If you need strict race condition control, it is required to use multiple nodes for write and read. <br>
|
|
252
|
+
Avoid using a node load balancer in this case, as it may bypass proper write/read distribution and compromise consistency.<br>
|
|
253
|
+
To connect your application to a Cluster database, use the following configuration:
|
|
53
254
|
|
|
54
|
-
|
|
255
|
+
```js
|
|
256
|
+
// ----------------------------------------------------
|
|
257
|
+
// example MariaDB Galera Cluster
|
|
55
258
|
|
|
56
|
-
|
|
259
|
+
DB_DRIVER = mariadb
|
|
260
|
+
DB_HOST = host-load-balncer ❌
|
|
261
|
+
DB_PORT = 3306
|
|
262
|
+
DB_USERNAME = root1
|
|
263
|
+
DB_PASSWORD = password1
|
|
264
|
+
DB_DATABASE = database
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
// ----------------------------------------------------
|
|
269
|
+
// MariaDB Galera Cluster
|
|
270
|
+
// host1 -> Master node
|
|
271
|
+
// host2, host3 -> slave nodes
|
|
272
|
+
DB_CLUSTER = true
|
|
273
|
+
DB_DRIVER = mariadb
|
|
274
|
+
DB_HOST = host1,host2,host3 ✅ // host1 still master by default
|
|
275
|
+
// if you want to specific master or slave
|
|
276
|
+
// master can be more than 1
|
|
277
|
+
// DB_HOST = master@host1,slave@host2,slave@host3
|
|
278
|
+
DB_PORT = 3306,3307,3308
|
|
279
|
+
DB_USERNAME = root1,root2,root3
|
|
280
|
+
DB_PASSWORD = password1,password2,password3
|
|
281
|
+
DB_DATABASE = database
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## SQL Like
|
|
285
|
+
SQL (Structured Query Language) is used to manage data in relational databases.
|
|
286
|
+
Here are the four most common commands with **tspace-mysql** examples:
|
|
287
|
+
```js
|
|
288
|
+
import { sql , OP } from 'tspace-mysql'
|
|
289
|
+
|
|
290
|
+
// select
|
|
291
|
+
await sql()
|
|
292
|
+
.select('id','name')
|
|
293
|
+
.from('users')
|
|
294
|
+
.where({
|
|
295
|
+
'name' : 'tspace'
|
|
296
|
+
'id' : OP.in([1,2,3])
|
|
297
|
+
})
|
|
298
|
+
.limit(3)
|
|
299
|
+
.orderBy('name')
|
|
300
|
+
|
|
301
|
+
// insert
|
|
302
|
+
await sql()
|
|
303
|
+
.insert('users')
|
|
304
|
+
.values({
|
|
305
|
+
email : 'tspace@example.com'
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// insert return data
|
|
309
|
+
await sql()
|
|
310
|
+
.insert('users')
|
|
311
|
+
.values({
|
|
312
|
+
email : 'tspace@example.com'
|
|
313
|
+
})
|
|
314
|
+
.returning({
|
|
315
|
+
id : true,
|
|
316
|
+
email : true,
|
|
317
|
+
enum : true
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
// update
|
|
321
|
+
await sql()
|
|
322
|
+
.update('users')
|
|
323
|
+
.where({
|
|
324
|
+
id : 1
|
|
325
|
+
})
|
|
326
|
+
.set({
|
|
327
|
+
email : 'tspace@example.com'
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
// update return data
|
|
331
|
+
await sql()
|
|
332
|
+
.update('users')
|
|
333
|
+
.where({
|
|
334
|
+
id : 1
|
|
335
|
+
})
|
|
336
|
+
.set({
|
|
337
|
+
email : 'tspace@example.com'
|
|
338
|
+
})
|
|
339
|
+
.returning()
|
|
340
|
+
|
|
341
|
+
//delete
|
|
342
|
+
await sql()
|
|
343
|
+
.delete('users')
|
|
344
|
+
.where({
|
|
345
|
+
id : 1
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Query Builder
|
|
352
|
+
|
|
353
|
+
How a database query builder works with a simple example using the following:
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
+-------------+--------------+----------------------------+
|
|
357
|
+
| table users |
|
|
358
|
+
+-------------+--------------+----------------------------+
|
|
359
|
+
| id | username | email |
|
|
360
|
+
|-------------|--------------|----------------------------|
|
|
361
|
+
| 1 | tspace | tspace@gmail.com |
|
|
362
|
+
| 2 | tspace2 | tspace2@gmail.com |
|
|
363
|
+
+-------------+--------------+----------------------------+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
+-------------+--------------+----------------------------+
|
|
367
|
+
| table posts |
|
|
368
|
+
+-------------+--------------+----------------------------+
|
|
369
|
+
| id | user_id | title |
|
|
370
|
+
|-------------|--------------|----------------------------|
|
|
371
|
+
| 1 | 1 | posts tspace |
|
|
372
|
+
| 2 | 2 | posts tspace2 |
|
|
373
|
+
+-------------+--------------+----------------------------+
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Table Name & Alias Name
|
|
378
|
+
|
|
379
|
+
```js
|
|
380
|
+
import { DB } from 'tspace-mysql'
|
|
381
|
+
|
|
382
|
+
await new DB().from('users').find(1)
|
|
383
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1' LIMIT 1;
|
|
384
|
+
|
|
385
|
+
await new DB().table('users').find(1)
|
|
386
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1' LIMIT 1;
|
|
387
|
+
|
|
388
|
+
await new DB().table('users').alias('u').find(1)
|
|
389
|
+
// SELECT * FROM `users` AS `u` WHERE `u`.`id` = '1' LIMIT 1;
|
|
390
|
+
|
|
391
|
+
await new DB().fromRaw('u',new DB('users').select('*').limit(1).toString()).find(1)
|
|
392
|
+
// SELECT * FROM ( SELECT * FROM `users` LIMIT 1 ) AS `u` WHERE `u`.`id` = '1' LIMIT 1;
|
|
393
|
+
|
|
394
|
+
await new DB().alias('u',new DB('users').select('*').limit(1).toString()).find(1)
|
|
395
|
+
// SELECT * FROM ( SELECT * FROM `users` LIMIT 1 ) AS `u` WHERE `u`.`id` = '1' LIMIT 1;
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Returning Results
|
|
400
|
+
|
|
401
|
+
```js
|
|
402
|
+
const user = await new DB("users").find(1); // Object or null
|
|
403
|
+
|
|
404
|
+
const user = await new DB("users").findOne(); // Object or null
|
|
405
|
+
|
|
406
|
+
const user = await new DB("users").first(); // Object or null
|
|
407
|
+
|
|
408
|
+
const user = await new DB("users").firstOrError(message); // Object or error
|
|
409
|
+
|
|
410
|
+
const users = await new DB("users").findMany(); // Array-object of users
|
|
411
|
+
|
|
412
|
+
const users = await new DB("users").get(); // Array-object of users
|
|
413
|
+
|
|
414
|
+
const users = await new DB("users").getGroupBy('name') // Map
|
|
415
|
+
|
|
416
|
+
const users = await new DB("users").findGroupBy('name') // Map
|
|
417
|
+
|
|
418
|
+
const users = await new DB("users").toArray(); // Array of users
|
|
419
|
+
|
|
420
|
+
const users = await new DB("users").toJSON(); // JSON of users
|
|
421
|
+
|
|
422
|
+
const user = await new DB("users").exists(); // Boolean true if user exists otherwise false
|
|
423
|
+
|
|
424
|
+
const user = await new DB("users").count(); // Number of users counted
|
|
425
|
+
|
|
426
|
+
const user = await new DB("users").avg(); // Number of users avg
|
|
427
|
+
|
|
428
|
+
const user = await new DB("users").sum(); // Number of users sum
|
|
429
|
+
|
|
430
|
+
const user = await new DB("users").max(); // Number of users max
|
|
431
|
+
|
|
432
|
+
const user = await new DB("user").min(); // Number of users min
|
|
433
|
+
|
|
434
|
+
const users = await new DB("users").toString(); // sql query string
|
|
435
|
+
|
|
436
|
+
const users = await new DB("users").toSQL(); // sql query string
|
|
437
|
+
|
|
438
|
+
const users = await new DB("users").toRawSQL(); // sql query string
|
|
439
|
+
|
|
440
|
+
const users = await new DB("users").pagination(); // Object of pagination
|
|
441
|
+
|
|
442
|
+
const users = await new DB("users").makeSelectStatement() // query string for select statement
|
|
443
|
+
|
|
444
|
+
const users = await new DB("users").makeInsertStatement() // query string for insert statement
|
|
445
|
+
|
|
446
|
+
const users = await new DB("users").makeUpdateStatement() // query string for update statement
|
|
447
|
+
|
|
448
|
+
const users = await new DB("users").makeDeleteStatement() // query string for delete statement
|
|
449
|
+
|
|
450
|
+
const users = await new DB("users").makeCreateTableStatement() // query string for create table statement
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Query Statements
|
|
455
|
+
|
|
456
|
+
```js
|
|
457
|
+
const query = await DB.query(
|
|
458
|
+
"SELECT * FROM users WHERE id = :id AND email IS :email AND name IN :username", {
|
|
459
|
+
id : 1,
|
|
460
|
+
email : null,
|
|
461
|
+
username : ['name1','name2']
|
|
462
|
+
})
|
|
463
|
+
// SELECT * FROM users WHERE id = '1' AND email IS NULL AND username in ('name1','name2');
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Select Statements
|
|
467
|
+
|
|
468
|
+
```js
|
|
469
|
+
const select = await new DB("users").select("id", "username").findOne();
|
|
470
|
+
// SELECT `users`.`id`, `users`.`username` FROM `users` LIMIT 1;
|
|
471
|
+
|
|
472
|
+
const selectRaw = await new DB("users").selectRaw("COUNT(id)").findMany();
|
|
473
|
+
// SELECT COUNT(id) FROM `users`;
|
|
474
|
+
// You can also use the DB.raw() function
|
|
475
|
+
// const selectRaw = await new DB("users").selec(DB.raw("COUNT(id)")).findMany();
|
|
476
|
+
|
|
477
|
+
const selectObject = await new DB("posts")
|
|
478
|
+
.join("posts.user_id", "users.id")
|
|
479
|
+
.select("posts.*")
|
|
480
|
+
.selectObject(
|
|
481
|
+
{ id: "users.id", name: "users.name", email: "users.email" },
|
|
482
|
+
"user"
|
|
483
|
+
)
|
|
484
|
+
.findOne();
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
SELECT
|
|
488
|
+
posts.*, JSON_OBJECT('id' , `users`.`id` , 'name' , `users`.`name` , 'email' , `users`.`email`) AS `user`
|
|
489
|
+
FROM `posts`
|
|
490
|
+
INNER JOIN `users` ON `posts`.`user_id` = `users`.`id` LIMIT 1;
|
|
491
|
+
*/
|
|
492
|
+
|
|
493
|
+
const selectArray = await new DB("users")
|
|
494
|
+
.select('id','name','email')
|
|
495
|
+
.join("users.id", "posts.user_id")
|
|
496
|
+
.select("posts.*")
|
|
497
|
+
.selectArray(
|
|
498
|
+
{ id: "posts.id", user_id: "posts.user_id", title: "posts.title" },
|
|
499
|
+
"posts"
|
|
500
|
+
)
|
|
501
|
+
.findOne();
|
|
502
|
+
/**
|
|
503
|
+
SELECT
|
|
504
|
+
`users`.`id`, `users`.`name`, `users`.`email`,
|
|
505
|
+
CASE WHEN COUNT(`posts`.`id`) = 0 THEN JSON_ARRAY()
|
|
506
|
+
ELSE JSON_ARRAYAGG(JSON_OBJECT('id' , `posts`.`id` , 'user_id' , `posts`.`user_id` , 'email' , `posts`.`title`))
|
|
507
|
+
END AS `posts`
|
|
508
|
+
FROM `users`
|
|
509
|
+
INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id` WHERE `users`.`deletedAt` IS NULL GROUP BY `users`.`id` LIMIT 1;
|
|
510
|
+
*/
|
|
511
|
+
|
|
512
|
+
await new DB("users").except("id").findOne();
|
|
513
|
+
// SELECT `users`.`email`, `users`.`username` FROM `users` LIMIT 1;
|
|
514
|
+
|
|
515
|
+
await new DB("users").distinct().select("id").findOne();
|
|
516
|
+
// SELECT DISTINCT `users`.`id` FROM `users` LIMIT 1;
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## Raw Expressions
|
|
520
|
+
|
|
521
|
+
```js
|
|
522
|
+
const users = await new DB("users")
|
|
523
|
+
.select(DB.raw("COUNT(`username`) as c"), "username")
|
|
524
|
+
.groupBy("username")
|
|
525
|
+
.having("c > 1")
|
|
526
|
+
.findMany();
|
|
527
|
+
// SELECT COUNT(`username`) as c, `users`.`username` FROM `users` GROUP BY `username` HAVING c > 1;
|
|
528
|
+
|
|
529
|
+
const users = await new DB("users")
|
|
530
|
+
.where(
|
|
531
|
+
"id",
|
|
532
|
+
DB.raw(new DB("users").select("id").where("id", "1").limit(1).toString())
|
|
533
|
+
)
|
|
534
|
+
.findMany();
|
|
535
|
+
// SELECT * FROM `users` WHERE `users`.`id` = (SELECT `users`.`id` FROM `users` WHERE `users`.`id` = '1' LIMIT 1);
|
|
536
|
+
|
|
537
|
+
const findFullName = await new User()
|
|
538
|
+
.select('name',`${DB.raw('CONCAT(firstName," ",lastName) as fullName')}`)
|
|
539
|
+
.whereRaw(`CONCAT(firstName," ",lastName) LIKE '%${search}%'`)
|
|
540
|
+
.findOne()
|
|
541
|
+
// SELECT `users`.`name`, CONCAT(firstName," ",lastName) as fullName FROM `users` WHERE CONCAT(firstName," ",lastName) LIKE '%search%' LIMIT 1;
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
## Ordering, Grouping, Limit and Offset
|
|
546
|
+
|
|
547
|
+
### Ordering
|
|
548
|
+
|
|
549
|
+
```js
|
|
550
|
+
await new DB("users").orderBy("id", "asc").findOne();
|
|
551
|
+
// SELECT * FROM `users` ORDER BY `id` ASC LIMIT 1;
|
|
552
|
+
|
|
553
|
+
await new DB("users").orderBy("id", "desc").findOne();
|
|
554
|
+
// SELECT * FROM `users` ORDER BY `id` DESC LIMIT 1;
|
|
555
|
+
|
|
556
|
+
await new DB("users").oldest("id").findOne();
|
|
557
|
+
// SELECT * FROM `users` ORDER BY `id` ASC LIMIT 1;
|
|
558
|
+
|
|
559
|
+
await new DB("users").latest("id").findOne();
|
|
560
|
+
// SELECT * FROM `users` ORDER BY `id` DESC LIMIT 1;
|
|
561
|
+
|
|
562
|
+
await new DB("users").random().findMany();
|
|
563
|
+
// SELECT * FROM `users` ORDER BY RAND();
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Grouping
|
|
567
|
+
|
|
568
|
+
```js
|
|
569
|
+
await new DB("users").groupBy("id").findOne();
|
|
570
|
+
// SELECT * FROM `users` GROUP BY `id` LIMIT 1;
|
|
571
|
+
|
|
572
|
+
await new DB("users").groupBy("id", "username").findOne();
|
|
573
|
+
// SELECT * FROM `users` GROUP BY `id`, `username` LIMIT 1;
|
|
574
|
+
|
|
575
|
+
await new DB("users")
|
|
576
|
+
.select(DB.raw("COUNT(username) as c"), "username")
|
|
577
|
+
.groupBy("username")
|
|
578
|
+
.having("c > 1")
|
|
579
|
+
.findMany();
|
|
580
|
+
// SELECT COUNT(username) as c, `users`.`username` FROM `users` GROUP BY `username` HAVING c > 1;
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Limit and Offset
|
|
584
|
+
|
|
585
|
+
```js
|
|
586
|
+
await new DB("users").limit(5).findMany();
|
|
587
|
+
// SELECT * FROM `users` LIMIT 5;
|
|
588
|
+
|
|
589
|
+
await new DB("users").limit(-1).findMany();
|
|
590
|
+
// SELECT * FROM `users` LIMIT 2147483647; // int-32 2**31 - 1
|
|
591
|
+
|
|
592
|
+
await new DB("users").offset(1).findOne();
|
|
593
|
+
// SELECT * FROM `users` LIMIT 1 OFFSET 1;
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## Joins
|
|
597
|
+
|
|
598
|
+
### Inner Join Clause
|
|
599
|
+
|
|
600
|
+
```js
|
|
601
|
+
await new DB("posts").join("posts.user_id", "users.id").findMany();
|
|
602
|
+
// SELECT * FROM `posts` INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`;
|
|
603
|
+
|
|
604
|
+
await new DB("posts")
|
|
605
|
+
.join((join) => {
|
|
606
|
+
return join
|
|
607
|
+
.on('posts.user_id','users.id')
|
|
608
|
+
.on('users.id','post_user.user_id')
|
|
609
|
+
.and('users.id','posts.user_id')
|
|
610
|
+
})
|
|
611
|
+
.findMany();
|
|
612
|
+
|
|
613
|
+
// SELECT * FROM `posts`
|
|
614
|
+
// INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
|
|
615
|
+
// INNER JOIN `post_user` ON `users`.`id` = `post_user`.`user_id` AND `users`.`id` = `posts`.`user_id`;
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Left Join, Right Join Clause
|
|
619
|
+
|
|
620
|
+
```js
|
|
621
|
+
await new DB("posts").leftJoin("posts.user_id", "users.id").findMany();
|
|
622
|
+
// SELECT * FROM `posts` LEFT JOIN `users` ON `posts`.`user_id` = `users`.`id`;
|
|
623
|
+
|
|
624
|
+
await new DB("posts").rightJoin("posts.user_id", "users.id").findMany();
|
|
625
|
+
// SELECT * FROM `posts` RIGHT JOIN `users` ON `posts`.`user_id` = `users`.`id`;
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Cross Join Clause
|
|
629
|
+
|
|
630
|
+
```js
|
|
631
|
+
await new DB("posts").crossJoin("posts.user_id", "users.id").findMany();
|
|
632
|
+
// SELECT * FROM `posts` CROSS JOIN `users` ON `posts`.`user_id` = `users`.`id`;
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
## Basic Where Clauses
|
|
636
|
+
|
|
637
|
+
### Where Clauses
|
|
638
|
+
|
|
639
|
+
```js
|
|
640
|
+
const users = await new DB("users").where("id", 1).findMany();
|
|
641
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1'
|
|
642
|
+
|
|
643
|
+
const users = await new DB("users")
|
|
644
|
+
.where("id", 1)
|
|
645
|
+
.where("username", "try to find")
|
|
646
|
+
.findMany();
|
|
647
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1' and `users`.`username` = 'try to find'
|
|
648
|
+
|
|
649
|
+
const users = await new DB("users").where("id", ">", 1).findMany();
|
|
650
|
+
// SELECT * FROM `users` WHERE `users`.`id` > '1';
|
|
651
|
+
|
|
652
|
+
const users = await new DB("users").where("id", "<>", 1).findMany();
|
|
653
|
+
// SELECT * FROM `users` WHERE `users`.`id` <> '1';
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### Or Where Clauses
|
|
657
|
+
|
|
658
|
+
```js
|
|
659
|
+
const users = await new DB("users").where("id", 1).orWhere("id", 2).findMany();
|
|
660
|
+
// SELECT * FROM `users` WHERE `users`.`id` = 1 OR `users`.`id` = 2
|
|
661
|
+
|
|
662
|
+
const users = await new DB("users")
|
|
663
|
+
.where("id", 1)
|
|
664
|
+
.whereQuery((query) => {
|
|
665
|
+
return query
|
|
666
|
+
.where("id", "<>", 2)
|
|
667
|
+
.orWhere("username", "try to find")
|
|
668
|
+
.orWhere("email", "find@example.com");
|
|
669
|
+
})
|
|
670
|
+
.findMany();
|
|
671
|
+
// SELECT * FROM `users` WHERE `users`.`id` = 1
|
|
672
|
+
// AND
|
|
673
|
+
// ( `users`.`id` <> 2 OR `users`.`username` = 'try to find' OR `users`.`email` = 'find@example.com');
|
|
674
|
+
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Where cases
|
|
678
|
+
|
|
679
|
+
```js
|
|
680
|
+
const payments = await new DB('payments')
|
|
681
|
+
.whereCases([
|
|
682
|
+
{
|
|
683
|
+
when : "payment_type = 'credit'",
|
|
684
|
+
then : "status = 'approved'"
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
when : "payment_type = 'paypal'",
|
|
688
|
+
then : "status = 'pending'"
|
|
689
|
+
}
|
|
690
|
+
],"FALSE")
|
|
691
|
+
.findMany()
|
|
692
|
+
|
|
693
|
+
// SELECT * FROM `payments`
|
|
694
|
+
// WHERE (
|
|
695
|
+
// CASE
|
|
696
|
+
// WHEN payment_type = 'credit' THEN status = 'approved'
|
|
697
|
+
// WHEN payment_type = 'paypal' THEN status = 'pending'
|
|
698
|
+
// ELSE FALSE
|
|
699
|
+
// END
|
|
700
|
+
// );
|
|
701
|
+
|
|
702
|
+
const tasks = await new DB("tasks")
|
|
703
|
+
.whereCases([
|
|
704
|
+
{
|
|
705
|
+
when : "priority = 'high'",
|
|
706
|
+
then : "DATEDIFF(due_date, NOW()) <= 3"
|
|
707
|
+
},
|
|
708
|
+
],"DATEDIFF(due_date, NOW()) <= 7")
|
|
709
|
+
.findMany()
|
|
710
|
+
|
|
711
|
+
// SELECT * FROM `tasks`
|
|
712
|
+
// WHERE (
|
|
713
|
+
// CASE
|
|
714
|
+
// WHEN priority = 'high' THEN DATEDIFF(due_date, NOW()) <= 3
|
|
715
|
+
// ELSE DATEDIFF(due_date, NOW()) <= 7
|
|
716
|
+
// END
|
|
717
|
+
// );
|
|
718
|
+
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Where Object Clauses
|
|
722
|
+
|
|
723
|
+
```js
|
|
724
|
+
import { OP } from 'tspace-mysql'
|
|
725
|
+
|
|
726
|
+
const whereObject = await new DB("users")
|
|
727
|
+
.whereObject({
|
|
728
|
+
id : OP.notEq(1),
|
|
729
|
+
username : OP.in(['user1','user2']),
|
|
730
|
+
name : OP.like('%value%')
|
|
731
|
+
})
|
|
732
|
+
.findMany();
|
|
733
|
+
|
|
734
|
+
// SELECT * FROM `users` WHERE `users`.`id` <> '1' AND `users`.`username` = 'user1' AND `users`.`name` LIKE '%value%';
|
|
735
|
+
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### JSON Where Clauses
|
|
739
|
+
|
|
740
|
+
```js
|
|
741
|
+
const whereJSON = await new DB("users")
|
|
742
|
+
.whereJSON("json", { key: "id", value: "1234" })
|
|
743
|
+
.findMany();
|
|
744
|
+
// SELECT * FROM `users` WHERE `users`.`json`->>'$.id' = '1234';
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### Additional Where Clauses
|
|
748
|
+
|
|
749
|
+
```js
|
|
750
|
+
const users = await new DB("users").whereIn("id", [1, 2]).findMany();
|
|
751
|
+
// SELECT * FROM `users` WHERE `users`.`id` IN ('1','2');
|
|
752
|
+
|
|
753
|
+
const users = await new DB("users").whereNotIn("id", [1, 2]).findMany();
|
|
754
|
+
// SELECT * FROM `users` WHERE `users`.`id` NOT IN ('1','2');
|
|
755
|
+
|
|
756
|
+
const users = await new DB("users").whereBetween("id", [1, 2]).findMany();
|
|
757
|
+
// SELECT * FROM `users` WHERE `users`.`id` BETWEEN '1' AND '2';
|
|
758
|
+
|
|
759
|
+
const users = await new DB("users").whereNotBetween("id", [1, 2]).findMany();
|
|
760
|
+
// SELECT * FROM `users` WHERE `users`.`id` NOT BETWEEN '1' AND '2';
|
|
761
|
+
|
|
762
|
+
const users = await new DB("users").whereNull("username").findMany();
|
|
763
|
+
// SELECT * FROM `users` WHERE `users`.`username` IS NULL;
|
|
764
|
+
|
|
765
|
+
const users = await new DB("users").whereNotNull("username").findMany();
|
|
766
|
+
// SELECT * FROM `users` WHERE `users`.`username` IS NOT NULL;
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### Logical Grouping
|
|
770
|
+
|
|
771
|
+
```js
|
|
772
|
+
const users = await new DB("users")
|
|
773
|
+
.whereQuery((query) => query.where("id", 1).where("username", "values"))
|
|
774
|
+
.whereIn("id", [1, 2])
|
|
775
|
+
.findOne();
|
|
776
|
+
// SELECT * FROM `users` WHERE ( `users`.`id` = '1' AND `users`.`username` = 'values') AND `users`.`id` IN ('1','2'') LIMIT 1;
|
|
777
|
+
|
|
778
|
+
const users = await new DB("users")
|
|
779
|
+
.where("id", 1)
|
|
780
|
+
.whereQuery((query) => {
|
|
781
|
+
return query
|
|
782
|
+
.where("id", "<>", 2)
|
|
783
|
+
.where("username", "try to find")
|
|
784
|
+
.where("email", "find@example.com");
|
|
785
|
+
})
|
|
786
|
+
.findMany();
|
|
787
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1'
|
|
788
|
+
// AND
|
|
789
|
+
// ( `users`.`id` <> '2' AND `users`.`username` = 'try to find' AND `users`.`email` = 'find@example.com');
|
|
790
|
+
|
|
791
|
+
const users = await new DB("users")
|
|
792
|
+
.whereAny(["name", "username", "email"], "like", `%v%`)
|
|
793
|
+
.findMany();
|
|
794
|
+
// SELECT * FROM `users` WHERE ( `users`.`name` LIKE '%v%' OR `users`.`username` LIKE '%v%' OR `users`.`email` LIKE '%v%');
|
|
795
|
+
|
|
796
|
+
const users = await new DB("users")
|
|
797
|
+
.whereAll(["name", "username", "email"], "like", `%v%`)
|
|
798
|
+
.findMany();
|
|
799
|
+
// SELECT * FROM `users` WHERE ( `users`.`name` LIKE '%v%' AND `users`.`username` LIKE '%v%' AND `users`.`email` LIKE '%v%');
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
## Advanced Where Clauses
|
|
803
|
+
|
|
804
|
+
### Where Exists Clauses
|
|
805
|
+
|
|
806
|
+
```js
|
|
807
|
+
const users = await new DB("users")
|
|
808
|
+
.whereExists(new DB("users").select("id").where("id", 1).toString())
|
|
809
|
+
.findMany();
|
|
810
|
+
// SELECT * FROM `users` WHERE EXISTS (SELECT `id` FROM `users` WHERE id = 1);
|
|
811
|
+
|
|
812
|
+
const users = await new DB("users")
|
|
813
|
+
.wherNoteExists(new DB("users").select("id").where("id", 1).toString())
|
|
814
|
+
.findMany();
|
|
815
|
+
// SELECT * FROM `users` WHERE NOT EXISTS (SELECT `id` FROM `users` WHERE id = 1);
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### Subquery Where Clauses
|
|
819
|
+
|
|
820
|
+
```js
|
|
821
|
+
const users = await new DB("users")
|
|
822
|
+
.whereSubQuery("id", "SELECT id FROM users")
|
|
823
|
+
.findMany();
|
|
824
|
+
// SELECT * FROM `users` WHERE `users`.`id` IN (SELECT id FROM users);
|
|
825
|
+
|
|
826
|
+
const users = await new DB("users")
|
|
827
|
+
.whereSubQuery("id", new DB("users").select("id").toString())
|
|
828
|
+
.findMany();
|
|
829
|
+
// SELECT * FROM `users` WHERE `users`.`id` IN (SELECT id FROM users);
|
|
830
|
+
|
|
831
|
+
const users = await new DB("users")
|
|
832
|
+
.whereSubQuery(
|
|
833
|
+
"id",
|
|
834
|
+
new DB("users")
|
|
835
|
+
.select("id")
|
|
836
|
+
.whereSubQuery("id", new DB("posts").select("user_id").toString())
|
|
837
|
+
.toString()
|
|
838
|
+
)
|
|
839
|
+
.findMany();
|
|
840
|
+
/*
|
|
841
|
+
SELECT * FROM `users`
|
|
842
|
+
WHERE `users`.`id`
|
|
843
|
+
IN (
|
|
844
|
+
SELECT `users`.`id` FROM `users`
|
|
845
|
+
WHERE `users`.`id`
|
|
846
|
+
IN (
|
|
847
|
+
SELECT `posts`.`user_id` FROM `posts`
|
|
848
|
+
)
|
|
849
|
+
);
|
|
850
|
+
*/
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
### Conditional Where Clauses
|
|
854
|
+
|
|
855
|
+
```js
|
|
856
|
+
const users = await new DB("users")
|
|
857
|
+
.where("id", 1)
|
|
858
|
+
.when(true, (query) => query.where("username", "when is actived"))
|
|
859
|
+
.findMany();
|
|
860
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1' AND `users`.`username` = 'when is actived';
|
|
861
|
+
|
|
862
|
+
const users = await new DB("users")
|
|
863
|
+
.where("id", 1)
|
|
864
|
+
.when(false, (query) => query.where("username", "when is actived"))
|
|
865
|
+
.findMany();
|
|
866
|
+
// SELECT * FROM `users` WHERE `users`.`id` = '1';
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
## GetGroupBy
|
|
870
|
+
|
|
871
|
+
```js
|
|
872
|
+
const data = await new DB("posts").getGroupBy('user_id')
|
|
873
|
+
|
|
874
|
+
// return new Map()
|
|
875
|
+
// find posts by user id
|
|
876
|
+
const userHasPosts = data.get(1)
|
|
877
|
+
|
|
878
|
+
console.log(userHasPosts)
|
|
879
|
+
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
## Paginating
|
|
883
|
+
|
|
884
|
+
```js
|
|
885
|
+
const users = await new DB("users").paginate();
|
|
886
|
+
// SELECT * FROM `users` LIMIT 15 OFFSET 0;
|
|
887
|
+
// SELECT COUNT(*) AS total FROM `users`;
|
|
888
|
+
|
|
889
|
+
const pageTwoUsers = await new DB("users").paginate({ page: 2, limit: 5 });
|
|
890
|
+
|
|
891
|
+
/*
|
|
892
|
+
SELECT * FROM `users` LIMIT 5 OFFSET 5;
|
|
893
|
+
SELECT COUNT(*) AS total FROM `users`;
|
|
894
|
+
|
|
895
|
+
the results are returned
|
|
896
|
+
{
|
|
897
|
+
meta: {
|
|
898
|
+
total: n,
|
|
899
|
+
limit: 5,
|
|
900
|
+
total_page: 5,
|
|
901
|
+
current_page: 2,
|
|
902
|
+
last_page: n,
|
|
903
|
+
next_page: 3,
|
|
904
|
+
prev_page: 1
|
|
905
|
+
},
|
|
906
|
+
data: [...your data here]
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
*/
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
## Insert Statements
|
|
913
|
+
|
|
914
|
+
```js
|
|
915
|
+
const user = await new DB("users")
|
|
916
|
+
.create({
|
|
917
|
+
name: "tspace3",
|
|
918
|
+
email: "tspace3@gmail.com",
|
|
919
|
+
})
|
|
920
|
+
.save();
|
|
921
|
+
/**
|
|
922
|
+
INSERT INTO `users`
|
|
923
|
+
(`users`.`name`,`users`.`email`)
|
|
924
|
+
VALUES
|
|
925
|
+
('tspace3','tspace3@gmail.com');
|
|
926
|
+
|
|
927
|
+
-- then return the result inserted --
|
|
928
|
+
SELECT * FROM `users` WHERE `users`.`id` = ${INSERT ID};
|
|
929
|
+
*/
|
|
930
|
+
|
|
931
|
+
const users = await new DB("users")
|
|
932
|
+
.createMultiple([
|
|
933
|
+
{
|
|
934
|
+
name: "tspace4",
|
|
935
|
+
email: "tspace4@gmail.com",
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
name: "tspace5",
|
|
939
|
+
email: "tspace5@gmail.com",
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
name: "tspace6",
|
|
943
|
+
email: "tspace6@gmail.com",
|
|
944
|
+
},
|
|
945
|
+
])
|
|
946
|
+
.save();
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
INSERT INTO `users`
|
|
950
|
+
(`users`.`name`,`users`.`email`)
|
|
951
|
+
VALUES
|
|
952
|
+
('tspace4','tspace4@gmail.com'),
|
|
953
|
+
('tspace5','tspace5@gmail.com'),
|
|
954
|
+
('tspace6','tspace6@gmail.com');
|
|
955
|
+
*/
|
|
956
|
+
|
|
957
|
+
const users = await new DB("users")
|
|
958
|
+
.where("name", "tspace4")
|
|
959
|
+
.where("email", "tspace4@gmail.com")
|
|
960
|
+
.createNotExists({
|
|
961
|
+
name: "tspace4",
|
|
962
|
+
email: "tspace4@gmail.com",
|
|
963
|
+
})
|
|
964
|
+
.save();
|
|
965
|
+
/*
|
|
966
|
+
-- if exists return null, if not exists created new data --
|
|
967
|
+
SELECT EXISTS(
|
|
968
|
+
SELECT 1 FROM `users`
|
|
969
|
+
WHERE `users`.`name` = 'tspace4'
|
|
970
|
+
AND `users`.`email` = 'tspace4@gmail.com'
|
|
971
|
+
LIMIT 1
|
|
972
|
+
) AS 'exists';
|
|
973
|
+
|
|
974
|
+
INSERT INTO `users` (`users`.`name`,`users`.`email`) VALUES ('tspace4','tspace4@gmail.com');
|
|
975
|
+
*/
|
|
976
|
+
|
|
977
|
+
const users = await new DB("users")
|
|
978
|
+
.where("name", "tspace4")
|
|
979
|
+
.where("email", "tspace4@gmail.com")
|
|
980
|
+
.createOrSelect({
|
|
981
|
+
name: "tspace4",
|
|
982
|
+
email: "tspace4@gmail.com",
|
|
983
|
+
})
|
|
984
|
+
.save();
|
|
985
|
+
/**
|
|
986
|
+
-- if has exists return data, if not exists created new data --
|
|
987
|
+
SELECT EXISTS(
|
|
988
|
+
SELECT 1 FROM `users`
|
|
989
|
+
WHERE `users`.`name` = 'tspace4'
|
|
990
|
+
AND `users`.`email` = 'tspace4@gmail.com'
|
|
991
|
+
LIMIT 1
|
|
992
|
+
) AS 'exists';
|
|
993
|
+
|
|
994
|
+
INSERT INTO `users` (`users`.`name`,`users`.`email`) VALUES ('tspace4','tspace4@gmail.com');
|
|
995
|
+
|
|
996
|
+
SELECT * FROM `users` WHERE `users`.`id` = '4';
|
|
997
|
+
*/
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
## Update Statements
|
|
1001
|
+
|
|
1002
|
+
```js
|
|
1003
|
+
const user = await new DB("users")
|
|
1004
|
+
.where("id", 1)
|
|
1005
|
+
.update({
|
|
1006
|
+
name: "tspace1**",
|
|
1007
|
+
email: "tspace1@gmail.com",
|
|
1008
|
+
})
|
|
1009
|
+
.save();
|
|
1010
|
+
/**
|
|
1011
|
+
|
|
1012
|
+
UPDATE `users` SET
|
|
1013
|
+
`users`.`name` = 'tspace1',
|
|
1014
|
+
`users`.`email` = 'tspace1@gmail.com'
|
|
1015
|
+
WHERE `users`.`id` = '1' LIMIT 1;
|
|
1016
|
+
|
|
1017
|
+
*/
|
|
1018
|
+
|
|
1019
|
+
const user = await new DB("users")
|
|
1020
|
+
.where("id", 1)
|
|
1021
|
+
.updateMany({
|
|
1022
|
+
name: "tspace1",
|
|
1023
|
+
email: "tspace1@gmail.com",
|
|
1024
|
+
})
|
|
1025
|
+
.save();
|
|
1026
|
+
/**
|
|
1027
|
+
UPDATE `users` SET
|
|
1028
|
+
`users`.`name` = 'tspace1',
|
|
1029
|
+
`users`.`email` = 'tspace1@gmail.com'
|
|
1030
|
+
WHERE `users`.`id` = '1';
|
|
1031
|
+
*/
|
|
1032
|
+
|
|
1033
|
+
const user = await new DB("users")
|
|
1034
|
+
.where("id", 1)
|
|
1035
|
+
.update(
|
|
1036
|
+
{
|
|
1037
|
+
name: "tspace1",
|
|
1038
|
+
email: "tspace1@gmail.com",
|
|
1039
|
+
},
|
|
1040
|
+
["name"]
|
|
1041
|
+
)
|
|
1042
|
+
.save();
|
|
1043
|
+
/**
|
|
1044
|
+
UPDATE `users` SET
|
|
1045
|
+
`name` =
|
|
1046
|
+
CASE WHEN (`name` = '' OR `name` IS NULL)
|
|
1047
|
+
THEN 'tspace1' ELSE `name`
|
|
1048
|
+
END,
|
|
1049
|
+
`email` =
|
|
1050
|
+
'tspace1@gmail.com'
|
|
1051
|
+
WHERE `users`.`id` = '1' LIMIT 1;
|
|
1052
|
+
*/
|
|
1053
|
+
|
|
1054
|
+
const user = await new DB("users")
|
|
1055
|
+
.updateMultiple([
|
|
1056
|
+
{
|
|
1057
|
+
when: {
|
|
1058
|
+
id: 1,
|
|
1059
|
+
name: "name1",
|
|
1060
|
+
},
|
|
1061
|
+
columns: {
|
|
1062
|
+
name: "update row1",
|
|
1063
|
+
email: "row1@example.com",
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
when: {
|
|
1068
|
+
id: 2,
|
|
1069
|
+
},
|
|
1070
|
+
columns: {
|
|
1071
|
+
name: "update row2",
|
|
1072
|
+
email: "row2@example.com",
|
|
1073
|
+
},
|
|
1074
|
+
},
|
|
1075
|
+
])
|
|
1076
|
+
.save();
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
UPDATE `users` SET
|
|
1080
|
+
`users`.`name` = (
|
|
1081
|
+
CASE WHEN `users`.`id` = '1'
|
|
1082
|
+
AND `users`.`name` = 'name1'
|
|
1083
|
+
THEN 'update row1'
|
|
1084
|
+
WHEN `users`.`id` = '2'
|
|
1085
|
+
THEN 'update row2'
|
|
1086
|
+
ELSE `users`.`name`
|
|
1087
|
+
END
|
|
1088
|
+
),
|
|
1089
|
+
`users`.`email` = (
|
|
1090
|
+
CASE WHEN `users`.`id` = '1'
|
|
1091
|
+
AND `users`.`name` = 'name1'
|
|
1092
|
+
THEN 'row1@example.com'
|
|
1093
|
+
WHEN `users`.`id` = '2'
|
|
1094
|
+
THEN 'row2@example.com'
|
|
1095
|
+
ELSE `users`.`email`
|
|
1096
|
+
END
|
|
1097
|
+
)
|
|
1098
|
+
WHERE `users`.`id` IN ('1','2') LIMIT 2;
|
|
1099
|
+
|
|
1100
|
+
*/
|
|
1101
|
+
|
|
1102
|
+
const user = await new DB("users")
|
|
1103
|
+
.where("id", 1)
|
|
1104
|
+
.updateOrCreate({
|
|
1105
|
+
name: "tspace1**",
|
|
1106
|
+
email: "tspace1@gmail.com",
|
|
1107
|
+
})
|
|
1108
|
+
.save();
|
|
1109
|
+
// if has exists return update, if not exists created new data
|
|
1110
|
+
// UPDATE `users` SET `name` = 'tspace1**',`email` = 'tspace1@gmail.com' WHERE `users`.`id` = '1' LIMIT 1;
|
|
1111
|
+
// INSERT INTO `users` (`name`,`email`) VALUES ('tspace1**','tspace1@gmail.com');
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
## Delete Statements
|
|
1115
|
+
|
|
1116
|
+
```js
|
|
1117
|
+
const deleted = await new DB("users").where("id", 1).delete();
|
|
1118
|
+
// DELETE FROM `users` WHERE `users`.`id` = '1' LIMIT 1;
|
|
1119
|
+
|
|
1120
|
+
const deleted = await new DB("users").where("id", 1).deleteMany();
|
|
1121
|
+
// DELETE FROM `users` WHERE `users`.`id` = '1' ;
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
## Hook Statements
|
|
1125
|
+
|
|
1126
|
+
```js
|
|
1127
|
+
const hookImage = async (results) => {
|
|
1128
|
+
for(const result of results) {
|
|
1129
|
+
result.image = await ...getImage()
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
const user = await new DB("users").where("id", 1).hook(hookResult).findMany();
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
## Faker Statements
|
|
1136
|
+
|
|
1137
|
+
```js
|
|
1138
|
+
await new DB("users").faker(2);
|
|
1139
|
+
/**
|
|
1140
|
+
INSERT INTO `users`
|
|
1141
|
+
(`users`.`username`,`users`.`email`)
|
|
1142
|
+
VALUES
|
|
1143
|
+
('ivsvtagyta86n571z9d81maz','fxcwkubccdi5ewos521uqexy'),
|
|
1144
|
+
('rnr4esoki7fgekmdtarqewt','gv0mzb1m3rlbinsdyb6')
|
|
1145
|
+
*/
|
|
1146
|
+
|
|
1147
|
+
// custom faker
|
|
1148
|
+
await new DB("users").faker(5, (row, index) => {
|
|
1149
|
+
return {
|
|
1150
|
+
username: `username-${index + 1}`,
|
|
1151
|
+
email: `email-${index + 1}`,
|
|
1152
|
+
};
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
|
|
1157
|
+
INSERT INTO `users`
|
|
1158
|
+
(`users`.`username`,`users`.`email`)
|
|
1159
|
+
VALUES
|
|
1160
|
+
('username-1','email-1'),
|
|
1161
|
+
('username-2','email-2'),
|
|
1162
|
+
('username-3','email-3'),
|
|
1163
|
+
('username-4','email-4'),
|
|
1164
|
+
('username-5','email-5');
|
|
1165
|
+
|
|
1166
|
+
*/
|
|
1167
|
+
|
|
1168
|
+
// fast to create
|
|
1169
|
+
await new DB("users").faker(40_000);
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
## Unset Statements
|
|
1173
|
+
|
|
1174
|
+
```js
|
|
1175
|
+
|
|
1176
|
+
const userInstance = new User().where('email','test@gmail.com')
|
|
1177
|
+
|
|
1178
|
+
const exits = await userInstance.exists()
|
|
1179
|
+
// SELECT EXISTS (SELECT 1 FROM `users` WHERE `users`.`email` = 'test@gmail.com' LIMIT 1) AS `aggregate`;
|
|
1180
|
+
|
|
1181
|
+
const user = await userInstance.orderBy('id').findOne()
|
|
1182
|
+
// SELECT * FROM `users` WHERE `users`.`email` = 'test@gmail.com' ORDER BY `users`.`id` DESC LIMIT 1;
|
|
1183
|
+
|
|
1184
|
+
const users = await userInstance.select('id').unset({ limit : true }).findMany()
|
|
1185
|
+
// SELECT `users`.`id` FROM `users` WHERE `users`.`email` = 'test@gmail.com' ORDER BY `users`.`id` DESC;
|
|
1186
|
+
|
|
1187
|
+
const usersUnsetWhereStatement = await userInstance.unset({ select : true, where : true , orderBy : true }).findMany()
|
|
1188
|
+
// SELECT * FROM `users` WHERE `users`.`deletedAt` IS NULL;
|
|
1189
|
+
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
## Common Table Expressions
|
|
1193
|
+
|
|
1194
|
+
```js
|
|
1195
|
+
|
|
1196
|
+
const user = await new User()
|
|
1197
|
+
.CTEs('z', (query) => {
|
|
1198
|
+
return query
|
|
1199
|
+
.from('posts')
|
|
1200
|
+
})
|
|
1201
|
+
.CTEs('x', (query) => {
|
|
1202
|
+
return query
|
|
1203
|
+
.from('post_user')
|
|
1204
|
+
})
|
|
1205
|
+
.select('users.*','x.*','z.*')
|
|
1206
|
+
.join('users.id','x.user_id')
|
|
1207
|
+
.join('users.id','z.user_id')
|
|
1208
|
+
.findOne()
|
|
1209
|
+
|
|
1210
|
+
// WITH z AS (SELECT posts.* FROM `posts`),
|
|
1211
|
+
// x AS (SELECT * FROM `post_user`)
|
|
1212
|
+
// SELECT users.*, z.*, x.* FROM `users` INNER JOIN `x` ON `users`.`id` = `x`.`user_id` INNER JOIN `z` ON `users`.`id` = `z`.`user_id` WHERE `users`.`deleted_at` IS NULL LIMIT 1;
|
|
1213
|
+
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
### Union
|
|
1217
|
+
|
|
1218
|
+
```js
|
|
1219
|
+
const users = await new DB('users')
|
|
1220
|
+
.where('id',1)
|
|
1221
|
+
.union(new DB('users').whereIn('id',[2]))
|
|
1222
|
+
.union(new DB('users').whereIn('id',[3,4]))
|
|
1223
|
+
.findMany()
|
|
1224
|
+
|
|
1225
|
+
// (SELECT * FROM `users` WHERE `users`.`id` = 1)
|
|
1226
|
+
// UNION (SELECT * FROM `users` WHERE `users`.`id` IN (2))
|
|
1227
|
+
// UNION (SELECT * FROM `users` WHERE `users`.`id` IN (3,4));
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
const users = await new DB('users')
|
|
1231
|
+
.unionAll(new DB('users'))
|
|
1232
|
+
.unionAll(new DB('users'))
|
|
1233
|
+
.findMany()
|
|
1234
|
+
|
|
1235
|
+
// (SELECT * FROM `users`)
|
|
1236
|
+
// UNION ALL (SELECT * FROM `users`)
|
|
1237
|
+
// UNION ALL (SELECT * FROM `users`);
|
|
1238
|
+
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
## More Methods
|
|
1242
|
+
|
|
1243
|
+
```js
|
|
1244
|
+
where(column , OP , value)
|
|
1245
|
+
whereSensitive(column , OP , value)
|
|
1246
|
+
whereId(id)
|
|
1247
|
+
whereUser(userId)
|
|
1248
|
+
whereEmail(value)
|
|
1249
|
+
whereIn(column , [])
|
|
1250
|
+
whereNotIn(column , [])
|
|
1251
|
+
whereNull(column)
|
|
1252
|
+
whereNotNull(column)
|
|
1253
|
+
whereBetween (column , [value1 , value2])
|
|
1254
|
+
whereQuery(callback)
|
|
1255
|
+
whereJson(column, { targetKey, value , OP })
|
|
1256
|
+
whereRaw(sql)
|
|
1257
|
+
whereExists(sql)
|
|
1258
|
+
whereSubQuery(colmn , rawSQL)
|
|
1259
|
+
whereNotSubQuery(colmn , rawSQL)
|
|
1260
|
+
orWhere(column , OP , value)
|
|
1261
|
+
orWhereRaw(sql)
|
|
1262
|
+
orWhereIn(column , [])
|
|
1263
|
+
orWhereSubQuery(colmn , rawSQL)
|
|
1264
|
+
when(contition , callback)
|
|
1265
|
+
select(column1 ,column2 ,...N)
|
|
1266
|
+
distinct()
|
|
1267
|
+
selectRaw(column1 ,column2 ,...N)
|
|
1268
|
+
except(column1 ,column2 ,...N)
|
|
1269
|
+
exceptTimestamp()
|
|
1270
|
+
only(column1 ,column2 ,...N)
|
|
1271
|
+
hidden(column1 ,column2 ,...N)
|
|
1272
|
+
join(primary key , table.foreign key)
|
|
1273
|
+
rightJoin (primary key , table.foreign key)
|
|
1274
|
+
leftJoin (primary key , table.foreign key)
|
|
1275
|
+
limit (limit)
|
|
1276
|
+
having (condition)
|
|
1277
|
+
havingRaw (condition)
|
|
1278
|
+
orderBy (column ,'ASC' || 'DSCE')
|
|
1279
|
+
orderByRaw(column ,'ASC' || 'DSCE')
|
|
1280
|
+
latest (column)
|
|
1281
|
+
latestRaw (column)
|
|
1282
|
+
oldest (column)
|
|
1283
|
+
oldestRaw (column)
|
|
1284
|
+
groupBy (column)
|
|
1285
|
+
groupByRaw (column)
|
|
1286
|
+
create(objects)
|
|
1287
|
+
createMultiple(array objects)
|
|
1288
|
+
update (objects)
|
|
1289
|
+
updateMany (objects)
|
|
1290
|
+
updateMultiple(array objects)
|
|
1291
|
+
createNotExists(objects)
|
|
1292
|
+
updateOrCreate (objects)
|
|
1293
|
+
onlyTrashed()
|
|
1294
|
+
connection(options)
|
|
1295
|
+
backup({ database , connection })
|
|
1296
|
+
backupToFile({ filePath, database , connection })
|
|
1297
|
+
hook((result) => ...) // callback result to function
|
|
1298
|
+
sleep(seconds)
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* registry relation in your models
|
|
1302
|
+
* @relationship
|
|
1303
|
+
*/
|
|
1304
|
+
hasOne({ name, model, localKey, foreignKey, freezeTable , as })
|
|
1305
|
+
hasMany({ name, model, localKey, foreignKey, freezeTable , as })
|
|
1306
|
+
belongsTo({ name, model, localKey, foreignKey, freezeTable , as })
|
|
1307
|
+
belongsToMany({ name, model, localKey, foreignKey, freezeTable, as, pivot })
|
|
1308
|
+
/**
|
|
1309
|
+
* @relation using registry in your models
|
|
1310
|
+
*/
|
|
1311
|
+
relations(name1 , name2,...nameN) // with(name1, name2,...nameN)
|
|
1312
|
+
/**
|
|
1313
|
+
* @relation using registry in your models ignore soft delete
|
|
1314
|
+
*/
|
|
1315
|
+
relationsAll(name1 , name2,...nameN) // withAll(name1, name2,...nameN)
|
|
1316
|
+
/**
|
|
1317
|
+
* @relation using registry in your models. if exists child data remove this data
|
|
1318
|
+
*/
|
|
1319
|
+
relationsExists(name1 , name2,...nameN) // withExists(name1, name2,...nameN)
|
|
1320
|
+
/**
|
|
1321
|
+
* @relation using registry in your models return only in trash (soft delete)
|
|
1322
|
+
*/
|
|
1323
|
+
relationsTrashed(name1 , name2,...nameN) // withTrashed(name1, name2,...nameN)
|
|
1324
|
+
/**
|
|
1325
|
+
* @relation call a name of relation in registry, callback query of data
|
|
1326
|
+
*/
|
|
1327
|
+
relationQuery(name, (callback) ) // withQuery(name1, (callback))
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* queries statements
|
|
1332
|
+
* @execute data of statements
|
|
1333
|
+
*/
|
|
1334
|
+
findMany() // get()
|
|
1335
|
+
findOne() // first()
|
|
1336
|
+
find(id)
|
|
1337
|
+
delelte()
|
|
1338
|
+
delelteMany()
|
|
1339
|
+
exists()
|
|
1340
|
+
toString()
|
|
1341
|
+
toJSON()
|
|
1342
|
+
toArray(column)
|
|
1343
|
+
count(column)
|
|
1344
|
+
sum(column)
|
|
1345
|
+
avg(column)
|
|
1346
|
+
max(column)
|
|
1347
|
+
min(column)
|
|
1348
|
+
pagination({ limit , page })
|
|
1349
|
+
save() /* for actions statements insert or update */
|
|
1350
|
+
makeSelectStatement()
|
|
1351
|
+
makeInsertStatement()
|
|
1352
|
+
makeUpdateStatement()
|
|
1353
|
+
makeDeleteStatement()
|
|
1354
|
+
makeCreateTableStatement()
|
|
1355
|
+
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
## Database Transactions
|
|
1359
|
+
|
|
1360
|
+
Within a database transaction, you can utilize the following:
|
|
1361
|
+
|
|
1362
|
+
```js
|
|
1363
|
+
const trx = await new DB().beginTransaction();
|
|
1364
|
+
|
|
1365
|
+
try {
|
|
1366
|
+
/**
|
|
1367
|
+
*
|
|
1368
|
+
* @startTransaction start transaction in scopes function
|
|
1369
|
+
*/
|
|
1370
|
+
await trx.startTransaction();
|
|
1371
|
+
|
|
1372
|
+
const user = await new User()
|
|
1373
|
+
.create({
|
|
1374
|
+
name: `tspace`,
|
|
1375
|
+
email: "tspace@example.com",
|
|
1376
|
+
})
|
|
1377
|
+
/**
|
|
1378
|
+
*
|
|
1379
|
+
* bind method for make sure this trx has same transaction in trx
|
|
1380
|
+
* @params {Function} trx
|
|
1381
|
+
*/
|
|
1382
|
+
.bind(trx)
|
|
1383
|
+
.save();
|
|
1384
|
+
|
|
1385
|
+
const posts = await new Post()
|
|
1386
|
+
.createMultiple([
|
|
1387
|
+
{
|
|
1388
|
+
user_id: user.id,
|
|
1389
|
+
title: `tspace-post1`,
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
user_id: user.id,
|
|
1393
|
+
title: `tspace-post2`,
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
user_id: user.id,
|
|
1397
|
+
title: `tspace-post3`,
|
|
1398
|
+
},
|
|
1399
|
+
])
|
|
1400
|
+
.bind(trx) // don't forget this
|
|
1401
|
+
.save();
|
|
1402
|
+
|
|
1403
|
+
/**
|
|
1404
|
+
*
|
|
1405
|
+
* @commit commit transaction to database
|
|
1406
|
+
*/
|
|
1407
|
+
// After your use commit if use same trx for actions this transction will auto commit
|
|
1408
|
+
await trx.commit();
|
|
1409
|
+
|
|
1410
|
+
// If you need to start a new transaction again, just use wait trx.startTransaction();
|
|
1411
|
+
|
|
1412
|
+
const postsAfterCommited = await new Post()
|
|
1413
|
+
.createMultiple([
|
|
1414
|
+
{
|
|
1415
|
+
user_id: user.id,
|
|
1416
|
+
title: `tspace-post1`,
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
user_id: user.id,
|
|
1420
|
+
title: `tspace-post2`,
|
|
1421
|
+
},
|
|
1422
|
+
{
|
|
1423
|
+
user_id: user.id,
|
|
1424
|
+
title: `tspace-post3`,
|
|
1425
|
+
},
|
|
1426
|
+
])
|
|
1427
|
+
// Using this trx now will auto-commit to the database.
|
|
1428
|
+
.bind(trx) // If you need to perform additional operations, use await trx.startTransaction(); again.
|
|
1429
|
+
.save();
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
// Do not perform any operations with this trx.
|
|
1433
|
+
// The transaction has already been committed, and the trx is closed.
|
|
1434
|
+
// Just ensure everything is handled at the end of the transaction.
|
|
1435
|
+
await trx.end();
|
|
1436
|
+
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
/**
|
|
1439
|
+
*
|
|
1440
|
+
* @rollback rollback transaction
|
|
1441
|
+
*/
|
|
1442
|
+
await trx.rollback();
|
|
1443
|
+
}
|
|
1444
|
+
```
|
|
1445
|
+
|
|
1446
|
+
## Race Condition
|
|
1447
|
+
|
|
1448
|
+
Within a race condition, you can utilize the following:
|
|
1449
|
+
|
|
1450
|
+
```js
|
|
1451
|
+
|
|
1452
|
+
import { Model, DB } from 'tspace-mysql'
|
|
1453
|
+
|
|
1454
|
+
class Product extends Model {}
|
|
1455
|
+
|
|
1456
|
+
class Order extends Model {}
|
|
1457
|
+
|
|
1458
|
+
async function purchaseForUpdate({userId , productId , qty } : {
|
|
1459
|
+
userId: number;
|
|
1460
|
+
productId: number;
|
|
1461
|
+
qty: number
|
|
1462
|
+
}): Promise<void> {
|
|
1463
|
+
const trx = await DB.beginTransaction()
|
|
1464
|
+
try {
|
|
1465
|
+
|
|
1466
|
+
await trx.startTransaction()
|
|
1467
|
+
|
|
1468
|
+
const product = await new Product()
|
|
1469
|
+
.where('id',productId)
|
|
1470
|
+
.rowLock('FOR_UPDATE') // don't forget this, lock this product for update
|
|
1471
|
+
.bind(trx)
|
|
1472
|
+
.first()
|
|
1473
|
+
|
|
1474
|
+
if (product == null) throw new Error("Product not found");
|
|
1475
|
+
|
|
1476
|
+
if (product.stock < qty) throw new Error("Not enough stock");
|
|
1477
|
+
|
|
1478
|
+
await new Product()
|
|
1479
|
+
.where('id',productId)
|
|
1480
|
+
.update({
|
|
1481
|
+
stock : `${DB.raw('stock')} - ${qty}`,
|
|
1482
|
+
id: productId
|
|
1483
|
+
})
|
|
1484
|
+
.bind(trx)
|
|
1485
|
+
.save()
|
|
1486
|
+
|
|
1487
|
+
await new Order()
|
|
1488
|
+
.create({
|
|
1489
|
+
user_id : userId,
|
|
1490
|
+
product_id : productId,
|
|
1491
|
+
qty
|
|
1492
|
+
})
|
|
1493
|
+
.bind(trx)
|
|
1494
|
+
.save()
|
|
1495
|
+
|
|
1496
|
+
await trx.commit();
|
|
1497
|
+
console.log(`✅ [FOR UPDATE] User ${userId} purchased ${qty}`);
|
|
1498
|
+
|
|
1499
|
+
} catch (err: any) {
|
|
1500
|
+
await trx.rollback();
|
|
1501
|
+
console.log(`❌ [FOR UPDATE] User ${userId} failed: ${err.message}`);
|
|
1502
|
+
} finally {
|
|
1503
|
+
await trx.end();
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
async function simulateRaceConnection(): Promise<void> {
|
|
1508
|
+
const MAX_CONNECTION = 500;
|
|
1509
|
+
const TASKS: Function[] = [];
|
|
1510
|
+
const STOCK = MAX_CONNECTION * 10
|
|
1511
|
+
|
|
1512
|
+
await new Product()
|
|
1513
|
+
.where('id',1)
|
|
1514
|
+
.update({
|
|
1515
|
+
stock : STOCK,
|
|
1516
|
+
})
|
|
1517
|
+
.save()
|
|
1518
|
+
|
|
1519
|
+
const successes : number[] = []
|
|
1520
|
+
const fails : number[] = []
|
|
1521
|
+
let purchased : number = 0
|
|
1522
|
+
let outOfStock : number = 0
|
|
1523
|
+
for (let i = 1; i <= MAX_CONNECTION; i++) {
|
|
1524
|
+
TASKS.push(async () => {
|
|
1525
|
+
const qty = Math.floor(Math.random() * 30) + 1
|
|
1526
|
+
await purchaseForUpdate({
|
|
1527
|
+
userId : i,
|
|
1528
|
+
productId : 1 ,
|
|
1529
|
+
qty
|
|
1530
|
+
})
|
|
1531
|
+
.then(_ => {
|
|
1532
|
+
successes.push(1)
|
|
1533
|
+
purchased += qty
|
|
1534
|
+
})
|
|
1535
|
+
.catch(_ => {
|
|
1536
|
+
fails.push(1)
|
|
1537
|
+
outOfStock += qty
|
|
1538
|
+
})
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
console.log("=== Simulation ===");
|
|
1543
|
+
const start = Date.now();
|
|
1544
|
+
await Promise.all(TASKS.map(v => v()));
|
|
1545
|
+
const end = Date.now();
|
|
1546
|
+
console.log(`USING TIME TO TEST IN ${end - start} ms`)
|
|
1547
|
+
console.log(`ALL STOCK: ${STOCK} qty`)
|
|
1548
|
+
console.log(`✅ [SUCCESS(${successes.length})] [Purchased]: ${purchased} qty`);
|
|
1549
|
+
console.log(`❌ [FAIL(${fails.length})] [OutOfStock]: ${outOfStock} qty`);
|
|
1550
|
+
console.log('======== DONE ============')
|
|
1551
|
+
process.exit(0)
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
simulateRaceConnection();
|
|
1555
|
+
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
## Connection
|
|
1559
|
+
|
|
1560
|
+
When establishing a connection, you can specify options as follows:
|
|
1561
|
+
|
|
1562
|
+
```js
|
|
1563
|
+
const connection = await new DB().getConnection({
|
|
1564
|
+
host: 'localhost',
|
|
1565
|
+
port : 3306,
|
|
1566
|
+
database: 'database'
|
|
1567
|
+
username: 'username',
|
|
1568
|
+
password: 'password',
|
|
1569
|
+
})
|
|
1570
|
+
|
|
1571
|
+
const users = await new DB('users')
|
|
1572
|
+
.bind(connection) // don't forget this
|
|
1573
|
+
.findMany()
|
|
1574
|
+
```
|
|
1575
|
+
|
|
1576
|
+
## Backup
|
|
1577
|
+
|
|
1578
|
+
To backup a database, you can perform the following steps:
|
|
1579
|
+
|
|
1580
|
+
```js
|
|
1581
|
+
/**
|
|
1582
|
+
*
|
|
1583
|
+
* @param {string} database Database selected
|
|
1584
|
+
* @param {object | null} to defalut new current connection
|
|
1585
|
+
*/
|
|
1586
|
+
const backup = await new DB().backup({
|
|
1587
|
+
database: 'try-to-backup', // clone current database to this database
|
|
1588
|
+
to ?: {
|
|
1589
|
+
host: 'localhost',
|
|
1590
|
+
port : 3306,
|
|
1591
|
+
username: 'username',
|
|
1592
|
+
password: 'password',
|
|
1593
|
+
}
|
|
1594
|
+
})
|
|
1595
|
+
/**
|
|
1596
|
+
*
|
|
1597
|
+
* @param {string} database Database selected
|
|
1598
|
+
* @param {string} filePath file path
|
|
1599
|
+
* @param {object | null} conection defalut current connection
|
|
1600
|
+
*/
|
|
1601
|
+
const backupToFile = await new DB().backupToFile({
|
|
1602
|
+
database: 'try-to-backup',
|
|
1603
|
+
filePath: 'backup.sql',
|
|
1604
|
+
connection ?: {
|
|
1605
|
+
host: 'localhost',
|
|
1606
|
+
port : 3306,
|
|
1607
|
+
database: 'database'
|
|
1608
|
+
username: 'username',
|
|
1609
|
+
password: 'password',
|
|
1610
|
+
}
|
|
1611
|
+
})
|
|
1612
|
+
// backupToFile => backup.sql
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
*
|
|
1616
|
+
* @param {string} database new db name
|
|
1617
|
+
*/
|
|
1618
|
+
await new DB().cloneDB('try-to-clone')
|
|
1619
|
+
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
## Injection
|
|
1623
|
+
|
|
1624
|
+
The 'tspace-mysql' library is configured to automatically escape SQL injection by default.
|
|
1625
|
+
Let's example a escape SQL injection and XSs injection:
|
|
1626
|
+
|
|
1627
|
+
```js
|
|
1628
|
+
const input = "admin' OR '1'='1";
|
|
1629
|
+
DB.escape(input);
|
|
1630
|
+
// "admin\' OR \'1\'=\'1"
|
|
1631
|
+
|
|
1632
|
+
//XSS
|
|
1633
|
+
const input = "text hello!<script>alert('XSS attack');</script>";
|
|
1634
|
+
DB.escapeXSS(input);
|
|
1635
|
+
// "text hello!"
|
|
1636
|
+
```
|
|
1637
|
+
|
|
1638
|
+
## Generating Model Classes
|
|
1639
|
+
|
|
1640
|
+
To get started, install the 'tspace-mysql' package globally using the following npm command:
|
|
1641
|
+
|
|
1642
|
+
```js
|
|
1643
|
+
/**
|
|
1644
|
+
*
|
|
1645
|
+
* @install global command
|
|
1646
|
+
*/
|
|
1647
|
+
npm install tspace-mysql -g
|
|
1648
|
+
|
|
1649
|
+
/**
|
|
1650
|
+
*
|
|
1651
|
+
* @make Model
|
|
1652
|
+
*/
|
|
1653
|
+
tspace-mysql make:model <model name> --dir=< directory >
|
|
1654
|
+
|
|
1655
|
+
# tspace-mysql make:model User --dir=App/Models
|
|
1656
|
+
# App/Models/User.ts
|
|
1657
|
+
```
|
|
1658
|
+
|
|
1659
|
+
## Model Conventions
|
|
1660
|
+
|
|
1661
|
+
Your database schema using models. These models represent tables in the database
|
|
1662
|
+
Let's example a basic model class:
|
|
1663
|
+
|
|
1664
|
+
```js
|
|
1665
|
+
import { Model } from "tspace-mysql";
|
|
1666
|
+
// If you want to specify a global setting for the 'Model'
|
|
1667
|
+
Model.global({
|
|
1668
|
+
uuid: true,
|
|
1669
|
+
softDelete: true,
|
|
1670
|
+
timestamp: true,
|
|
1671
|
+
logger: true,
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1674
|
+
class User extends Model {
|
|
1675
|
+
constructor() {
|
|
1676
|
+
super();
|
|
1677
|
+
/**
|
|
1678
|
+
*
|
|
1679
|
+
* Assign setting global in your model
|
|
1680
|
+
* @useMethod
|
|
1681
|
+
* this.usePattern('camelCase') // => default 'snake_case'
|
|
1682
|
+
* this.useCamelCase()
|
|
1683
|
+
* this.useSnakeCase()
|
|
1684
|
+
* this.useLogger()
|
|
1685
|
+
* this.useDebug()
|
|
1686
|
+
* this.usePrimaryKey('id')
|
|
1687
|
+
* this.useTimestamp({
|
|
1688
|
+
* createdAt : 'created_at',
|
|
1689
|
+
* updatedAt : 'updated_at'
|
|
1690
|
+
* }) // runing a timestamp when insert or update
|
|
1691
|
+
* this.useSoftDelete('deletedAt') // => default target to colmun deleted_at
|
|
1692
|
+
* this.useTable('users')
|
|
1693
|
+
* this.useTableSingular() // => 'user'
|
|
1694
|
+
* this.useTablePlural() // => 'users'
|
|
1695
|
+
* this.useUUID('uuid') // => runing a uuid (universally unique identifier) when insert new data
|
|
1696
|
+
* this.useRegistry() // => build-in functions registry
|
|
1697
|
+
* this.useLoadRelationsInRegistry() // => auto generated result from relationship to results
|
|
1698
|
+
* this.useBuiltInRelationFunctions() // => build-in functions relationships to results
|
|
1699
|
+
* this.useHooks([(r) => console.log(r)])
|
|
1700
|
+
* this.useObserver(Observe)
|
|
1701
|
+
* this.useSchema ({
|
|
1702
|
+
* id : Blueprint.int().notNull().primary().autoIncrement(),
|
|
1703
|
+
* uuid : Blueprint.varchar(50).null(),
|
|
1704
|
+
* name : Blueprint.varchar(191).notNull(),
|
|
1705
|
+
* email : Blueprint.varchar(191).notNull(),
|
|
1706
|
+
* created_at : Blueprint.timestamp().null(),
|
|
1707
|
+
* updated_at : Blueprint.timestamp().null(),
|
|
1708
|
+
* deleted_at : Blueprint.timestamp().null()
|
|
1709
|
+
* }) // auto-generated table when table is not exists and auto-create column when column not exists
|
|
1710
|
+
*
|
|
1711
|
+
* // validate input when create or update reference to the schema in 'this.useSchema'
|
|
1712
|
+
* this.useValidateSchema({
|
|
1713
|
+
* id : Number,
|
|
1714
|
+
* uuid : Number,
|
|
1715
|
+
* name : {
|
|
1716
|
+
* type : String,
|
|
1717
|
+
* length : 191
|
|
1718
|
+
* require : true
|
|
1719
|
+
* },
|
|
1720
|
+
* email : {
|
|
1721
|
+
* type : String,
|
|
1722
|
+
* require : true,
|
|
1723
|
+
* length : 191,
|
|
1724
|
+
* match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
1725
|
+
* unique : true,
|
|
1726
|
+
* fn : (email : string) => !/^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)
|
|
1727
|
+
* },
|
|
1728
|
+
* created_at : Date,
|
|
1729
|
+
* updated_at : Date,
|
|
1730
|
+
* deleted_at : Date
|
|
1731
|
+
* })
|
|
1732
|
+
*/
|
|
1733
|
+
|
|
1734
|
+
/*
|
|
1735
|
+
* the "snake case", plural name of the class will be used as the table name
|
|
1736
|
+
*
|
|
1737
|
+
* @param {string} name The table associated with the model.
|
|
1738
|
+
*/
|
|
1739
|
+
this.useTable("users");
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
export { User };
|
|
1743
|
+
export default User;
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
### Basic Model Setup
|
|
1747
|
+
|
|
1748
|
+
#### Table Name
|
|
1749
|
+
|
|
1750
|
+
```js
|
|
1751
|
+
import { Model } from 'tspace-mysql'
|
|
1752
|
+
class User extends Model {
|
|
1753
|
+
constructor() {
|
|
1754
|
+
super()
|
|
1755
|
+
// By default, the model knows that the table name for this User is 'users'
|
|
1756
|
+
|
|
1757
|
+
this.useTable('fix_table') // fixtable
|
|
1758
|
+
this.useTablePlural() // users
|
|
1759
|
+
this.useTableSingular() // user
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
```
|
|
1764
|
+
|
|
1765
|
+
#### Pattern
|
|
1766
|
+
|
|
1767
|
+
```js
|
|
1768
|
+
|
|
1769
|
+
import { Model } from 'tspace-mysql'
|
|
1770
|
+
class UserPhone extends Model {
|
|
1771
|
+
constructor() {
|
|
1772
|
+
super()
|
|
1773
|
+
// By default, the model is pattern snake_case
|
|
1774
|
+
// The table name is user_phones
|
|
1775
|
+
this.useSnakeCase()
|
|
1776
|
+
|
|
1777
|
+
this.useCamelCase()
|
|
1778
|
+
// The table name is userPhones
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
// set the pattern CamelCase for the model
|
|
1783
|
+
const userPhone = await new UserPhone().where('user_id',1).findOne()
|
|
1784
|
+
// covert 'user_id' to 'userId'
|
|
1785
|
+
// SELECT * FROM `userPhones` WHERE `userPhones`.`userId` = '1' LIMIT 1;
|
|
1786
|
+
|
|
1787
|
+
// avoid the pattern CamelCase for the model
|
|
1788
|
+
const userPhone = await new UserPhone().where(DB.freeze('user_id'),1).findOne()
|
|
1789
|
+
// SELECT * FROM `userPhones` WHERE `userPhones`.`user_id` = '1' LIMIT 1;
|
|
1790
|
+
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
#### UUID
|
|
1794
|
+
|
|
1795
|
+
```js
|
|
1796
|
+
|
|
1797
|
+
import { Model } from 'tspace-mysql'
|
|
1798
|
+
class User extends Model {
|
|
1799
|
+
constructor() {
|
|
1800
|
+
super()
|
|
1801
|
+
this.useUUID() // insert uuid when creating
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
#### Timestamp
|
|
1808
|
+
|
|
1809
|
+
```js
|
|
1810
|
+
|
|
1811
|
+
import { Model } from 'tspace-mysql'
|
|
1812
|
+
class User extends Model {
|
|
1813
|
+
constructor() {
|
|
1814
|
+
super()
|
|
1815
|
+
// insert created_at and updated_at when creating
|
|
1816
|
+
// update updated_at when updating
|
|
1817
|
+
// 'created_at' and 'updated_at' still relate to pettern the model
|
|
1818
|
+
// this.useCamelCase() will covert 'created_at' to 'createdAt' and 'updated_at' to 'updatedAt'
|
|
1819
|
+
this.useTimestamp()
|
|
1820
|
+
|
|
1821
|
+
// custom the columns
|
|
1822
|
+
this.useTimestamp({
|
|
1823
|
+
createdAt : 'createdAtCustom',
|
|
1824
|
+
updatedAt : 'updatedAtCustom'
|
|
1825
|
+
})
|
|
1826
|
+
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
```
|
|
1831
|
+
|
|
1832
|
+
#### Debug
|
|
1833
|
+
|
|
1834
|
+
```js
|
|
1835
|
+
|
|
1836
|
+
import { Model } from 'tspace-mysql'
|
|
1837
|
+
class User extends Model {
|
|
1838
|
+
constructor() {
|
|
1839
|
+
super()
|
|
1840
|
+
this.useDebug() // show the query sql in console when executing
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
```
|
|
1845
|
+
#### Observer
|
|
1846
|
+
|
|
1847
|
+
```js
|
|
1848
|
+
|
|
1849
|
+
class Observe {
|
|
1850
|
+
|
|
1851
|
+
public selected(results) {
|
|
1852
|
+
console.log({ results , selected : true })
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
public created(results) {
|
|
1856
|
+
console.log({ results , created : true })
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
public updated(results) {
|
|
1860
|
+
console.log({ results , updated : true })
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
public deleted(results) {
|
|
1864
|
+
console.log({ results , deleted : true })
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
import { Model } from 'tspace-mysql'
|
|
1869
|
+
class User extends Model {
|
|
1870
|
+
constructor() {
|
|
1871
|
+
super()
|
|
1872
|
+
this.useObserver(Observe) // returning to the observers by statements
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
```
|
|
1877
|
+
|
|
1878
|
+
#### Logger
|
|
1879
|
+
|
|
1880
|
+
```js
|
|
1881
|
+
|
|
1882
|
+
import { Model } from 'tspace-mysql'
|
|
1883
|
+
class User extends Model {
|
|
1884
|
+
constructor() {
|
|
1885
|
+
super()
|
|
1886
|
+
// keep logging everything except select to the table '$loggers'
|
|
1887
|
+
// the table will automatically be created
|
|
1888
|
+
this.useLogger()
|
|
1889
|
+
|
|
1890
|
+
// keep logging everything
|
|
1891
|
+
this.useLogger({
|
|
1892
|
+
selected : true,
|
|
1893
|
+
inserted : true,
|
|
1894
|
+
updated : true,
|
|
1895
|
+
deleted : true,
|
|
1896
|
+
})
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
```
|
|
1901
|
+
|
|
1902
|
+
#### Hooks
|
|
1903
|
+
|
|
1904
|
+
```js
|
|
1905
|
+
|
|
1906
|
+
import { Model } from 'tspace-mysql'
|
|
1907
|
+
class User extends Model {
|
|
1908
|
+
constructor() {
|
|
1909
|
+
super()
|
|
1910
|
+
// when executed will returning the results to any hooks function
|
|
1911
|
+
this.useHooks([
|
|
1912
|
+
(results1) => console.log(results1),
|
|
1913
|
+
(results2) => console.log(results2),
|
|
1914
|
+
(results3) => console.log(results3)
|
|
1915
|
+
])
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
```
|
|
1920
|
+
|
|
1921
|
+
### Global Scope
|
|
1922
|
+
```js
|
|
1923
|
+
|
|
1924
|
+
class User extends Model {
|
|
1925
|
+
constructor() {
|
|
1926
|
+
super()
|
|
1927
|
+
|
|
1928
|
+
// Every query will have the global scope applied.
|
|
1929
|
+
this.globalScope((query : User) => {
|
|
1930
|
+
return query.select('id').where('id' , '>' , 10).orderBy('id')
|
|
1931
|
+
})
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
const user = await new User().findMany()
|
|
1936
|
+
|
|
1937
|
+
// SELECT `users`.`id` FROM `users` WHERE `users`.`id` > '10' ORDER BY `users`.`id` ASC LIMIT 1
|
|
1938
|
+
|
|
1939
|
+
```
|
|
1940
|
+
|
|
1941
|
+
## Joins Model
|
|
1942
|
+
|
|
1943
|
+
### Inner Join Model Clause
|
|
1944
|
+
|
|
1945
|
+
```js
|
|
1946
|
+
await new User().joinModel(User, Post).findMany();
|
|
1947
|
+
// SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
|
|
1948
|
+
|
|
1949
|
+
// if the model use soft delete
|
|
1950
|
+
await new User().joinModel(User, Post).findMany();
|
|
1951
|
+
// SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users`
|
|
1952
|
+
// INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id`
|
|
1953
|
+
// WHERE `posts`.`deleted_at` IS NULL AND `users`.`deleted_at` IS NULL;
|
|
1954
|
+
|
|
1955
|
+
await new User().select(`${User.table}.*`,`${Post.table}.*`).joinModel(User, Post).findMany();
|
|
1956
|
+
// SELECT users.*, posts.* FROM `users`
|
|
1957
|
+
// INNER JOIN `posts` ON `users`.`id` = `posts`.`user_id`
|
|
1958
|
+
// WHERE `posts`.`deleted_at` IS NULL AND `users`.`deleted_at` IS NULL;
|
|
1959
|
+
|
|
1960
|
+
await new User().select('u.*','p.*')
|
|
1961
|
+
.joinModel({ model : User , key : 'id' , alias : 'u' }, { model : Post , key : 'user_id', alias : 'p'})
|
|
1962
|
+
.findMany();
|
|
1963
|
+
// SELECT u.*, p.* FROM `users` AS `u`
|
|
1964
|
+
// INNER JOIN `posts` AS `p` ON `u`.`id` = `p`.`user_id`
|
|
1965
|
+
// WHERE `p`.`deleted_at` IS NULL AND `u`.`deleted_at` IS NULL;
|
|
1966
|
+
|
|
1967
|
+
await new DB("posts")
|
|
1968
|
+
.join((join) => {
|
|
1969
|
+
return join
|
|
1970
|
+
.on('posts.user_id','users.id')
|
|
1971
|
+
.on('users.id','post_user.user_id')
|
|
1972
|
+
.and('users.id','posts.user_id')
|
|
1973
|
+
})
|
|
1974
|
+
.findMany()
|
|
1975
|
+
// SELECT * FROM `posts`
|
|
1976
|
+
// INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
|
|
1977
|
+
// INNER JOIN `post_user` ON `users`.`id` = `post_user`.`user_id` AND `users`.`id` = `posts`.`user_id`;
|
|
1978
|
+
```
|
|
1979
|
+
### Left Join, Right Join Model Clause
|
|
1980
|
+
|
|
1981
|
+
```js
|
|
1982
|
+
await new User().leftJoinModel(User, Post).findMany();
|
|
1983
|
+
// SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` LEFT JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
|
|
1984
|
+
|
|
1985
|
+
await new User().rightJoinModel(User, Post).findMany();
|
|
1986
|
+
// SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` RIGHT JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
|
|
1987
|
+
```
|
|
1988
|
+
|
|
1989
|
+
### Cross Join Model Clause
|
|
1990
|
+
|
|
1991
|
+
```js
|
|
1992
|
+
await new User().crossJoinModel(User, Post).findMany();
|
|
1993
|
+
// SELECT `users`.`id`, `users`.`email`, `users`.`username` FROM `users` CROSS JOIN `posts` ON `users`.`id` = `posts`.`user_id`;
|
|
1994
|
+
```
|
|
1995
|
+
|
|
1996
|
+
### Relationships
|
|
1997
|
+
|
|
1998
|
+
Relationships are defined as methods on your Model classes.
|
|
1999
|
+
Let's example a basic relationship:
|
|
2000
|
+
|
|
2001
|
+
#### One To One
|
|
2002
|
+
|
|
2003
|
+
A one-to-one relationship is used to define relationships where a single model is the parent to one child models
|
|
2004
|
+
|
|
2005
|
+
```js
|
|
2006
|
+
import { Model } from 'tspace-mysql'
|
|
2007
|
+
import Phone from '../Phone'
|
|
2008
|
+
class User extends Model {
|
|
2009
|
+
constructor(){
|
|
2010
|
+
super()
|
|
2011
|
+
this.useTimestamp()
|
|
2012
|
+
/**
|
|
2013
|
+
*
|
|
2014
|
+
* @hasOne Get the phone associated with the user.
|
|
2015
|
+
* @relationship users.id -> phones.user_id
|
|
2016
|
+
*/
|
|
2017
|
+
this.hasOne({ name : 'phone' , model : Phone })
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Mark a method for relationship
|
|
2021
|
+
* @hasOne Get the phone associated with the user. using function callback
|
|
2022
|
+
* @function
|
|
2023
|
+
*/
|
|
2024
|
+
phone (callback) {
|
|
2025
|
+
return this.hasOneBuilder({ name : 'phone' , model : Phone } , callback)
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
export default User
|
|
2029
|
+
|
|
2030
|
+
+--------------------------------------------------------------------------+
|
|
2031
|
+
|
|
2032
|
+
import User from '../User'
|
|
2033
|
+
const user = await new User().relations('phone').findOne() // You can also use the method .with('roles').
|
|
2034
|
+
// user?.phone => {...}
|
|
2035
|
+
const userUsingFunction = await new User().phone().findOne()
|
|
2036
|
+
// userUsingFunction?.phone => {...}
|
|
2037
|
+
```
|
|
2038
|
+
|
|
2039
|
+
### One To Many
|
|
2040
|
+
|
|
2041
|
+
A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models.
|
|
2042
|
+
|
|
2043
|
+
```js
|
|
2044
|
+
import { Model } from 'tspace-mysql'
|
|
2045
|
+
import Comment from '../Comment'
|
|
2046
|
+
class Post extends Model {
|
|
2047
|
+
constructor(){
|
|
2048
|
+
super()
|
|
2049
|
+
this.useTimestamp()
|
|
2050
|
+
/**
|
|
2051
|
+
*
|
|
2052
|
+
* @hasMany Get the comments for the post.
|
|
2053
|
+
* @relationship posts.id -> comments.post_id
|
|
2054
|
+
*/
|
|
2055
|
+
this.hasMany({ name : 'comments' , model : Comment })
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
*
|
|
2059
|
+
* @hasManyQuery Get the comments for the post. using function callback
|
|
2060
|
+
* @function
|
|
2061
|
+
*/
|
|
2062
|
+
comments (callback) {
|
|
2063
|
+
return this.hasManyBuilder({ name : 'comments' , model : Comment } , callback)
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
export default Post
|
|
2067
|
+
|
|
2068
|
+
+--------------------------------------------------------------------------+
|
|
2069
|
+
|
|
2070
|
+
import Post from '../Post'
|
|
2071
|
+
const posts = await new Post().relations('comments').findOne()
|
|
2072
|
+
// posts?.comments => [{...}]
|
|
2073
|
+
const postsUsingFunction = await new Post().comments().findOne()
|
|
2074
|
+
// postsUsingFunction?.comments => [{...}]
|
|
2075
|
+
```
|
|
2076
|
+
|
|
2077
|
+
#### Belongs To
|
|
2078
|
+
|
|
2079
|
+
A belongsto relationship is used to define relationships where a single model is the child to parent models.
|
|
2080
|
+
|
|
2081
|
+
```js
|
|
2082
|
+
import { Model } from 'tspace-mysql'
|
|
2083
|
+
import User from '../User'
|
|
2084
|
+
class Phone extends Model {
|
|
2085
|
+
constructor(){
|
|
2086
|
+
super()
|
|
2087
|
+
this.useTimestamp()
|
|
2088
|
+
/**
|
|
2089
|
+
*
|
|
2090
|
+
* @belongsTo Get the user that owns the phone.
|
|
2091
|
+
* @relationship phones.user_id -> users.id
|
|
2092
|
+
*/
|
|
2093
|
+
this.belognsTo({ name : 'user' , model : User })
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
*
|
|
2097
|
+
* @belongsToBuilder Get the user that owns the phone.. using function callback
|
|
2098
|
+
* @function
|
|
2099
|
+
*/
|
|
2100
|
+
user (callback) {
|
|
2101
|
+
return this.belongsToBuilder({ name : 'user' , model : User }, callback)
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
export default Phone
|
|
2105
|
+
|
|
2106
|
+
+--------------------------------------------------------------------------+
|
|
2107
|
+
|
|
2108
|
+
import Phone from '../Phone'
|
|
2109
|
+
const phone = await new Phone().relations('user').findOne()
|
|
2110
|
+
// phone?.user => {...}
|
|
2111
|
+
const phoneUsingFunction = await new Phone().user().findOne()
|
|
2112
|
+
// phoneUsingFunction?.user => {...}
|
|
2113
|
+
```
|
|
2114
|
+
|
|
2115
|
+
#### Many To Many
|
|
2116
|
+
|
|
2117
|
+
Many-to-many relations are slightly more complicated than hasOne and hasMany relationships.
|
|
2118
|
+
|
|
2119
|
+
```js
|
|
2120
|
+
import { Model } from 'tspace-mysql'
|
|
2121
|
+
import Role from '../Role'
|
|
2122
|
+
class User extends Model {
|
|
2123
|
+
constructor(){
|
|
2124
|
+
super()
|
|
2125
|
+
this.useTimestamp()
|
|
2126
|
+
/**
|
|
2127
|
+
*
|
|
2128
|
+
* @belongsToMany Get The roles that belong to the user.
|
|
2129
|
+
* @relationship users.id , roles.id => role_user.user_id , role_user.role_id
|
|
2130
|
+
*/
|
|
2131
|
+
this.belognsToMany({ name : 'roles' , model : Role })
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* @belongsToBuilder Get the user that owns the phone.. using function callback
|
|
2135
|
+
* @function
|
|
2136
|
+
*/
|
|
2137
|
+
roles (callback) {
|
|
2138
|
+
return this.belognsToManyBuilder({ model : Role } , callback)
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
export default User
|
|
2142
|
+
|
|
2143
|
+
+--------------------------------------------------------------------------+
|
|
2144
|
+
|
|
2145
|
+
import User from '../User'
|
|
2146
|
+
const user = await new User().relations('roles').findOne()
|
|
2147
|
+
// user?.roles => [{...}]
|
|
2148
|
+
const userUsingFunction = await new User().roles().findOne()
|
|
2149
|
+
// user?.roles => [{...}]
|
|
2150
|
+
```
|
|
2151
|
+
|
|
2152
|
+
#### Relation
|
|
2153
|
+
|
|
2154
|
+
Relationships are connections between entities.
|
|
2155
|
+
Let's consider an example of a relationship:
|
|
2156
|
+
|
|
2157
|
+
```js
|
|
2158
|
+
|
|
2159
|
+
+-------------+--------------+----------------------------+
|
|
2160
|
+
| table users |
|
|
2161
|
+
+-------------+--------------+----------------------------+
|
|
2162
|
+
| id | username | email |
|
|
2163
|
+
|-------------|--------------|----------------------------|
|
|
2164
|
+
| 1 | tspace1 | tspace1@gmail.com |
|
|
2165
|
+
| 2 | tspace2 | tspace2@gmail.com |
|
|
2166
|
+
| 3 | tspace3 | tspace3@gmail.com |
|
|
2167
|
+
+-------------+--------------+----------------------------+
|
|
2168
|
+
|
|
2169
|
+
+-------------+--------------+----------------------------+
|
|
2170
|
+
| table posts |
|
|
2171
|
+
+-------------+--------------+----------------------------+
|
|
2172
|
+
| id | user_id | title |
|
|
2173
|
+
|-------------|--------------|----------------------------|
|
|
2174
|
+
| 1 | 1 | posts 1 |
|
|
2175
|
+
| 2 | 1 | posts 2 |
|
|
2176
|
+
| 3 | 3 | posts 3 |
|
|
2177
|
+
+-------------+--------------+----------------------------+
|
|
2178
|
+
|
|
2179
|
+
import { Model } from 'tspace-mysql'
|
|
2180
|
+
|
|
2181
|
+
class User extends Model {
|
|
2182
|
+
constructor(){
|
|
2183
|
+
super()
|
|
2184
|
+
this.hasMany({ name : 'posts' , model : Post })
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
class Post extends Model {
|
|
2189
|
+
constructor(){
|
|
2190
|
+
super()
|
|
2191
|
+
this.belongsTo({ name : 'user' , model : User })
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
await new User()
|
|
2196
|
+
.relations('posts')
|
|
2197
|
+
.findOne()
|
|
2198
|
+
// SELECT * FROM `users` LIMIT 1;
|
|
2199
|
+
// SELECT * FROM `posts` WHERE `posts`.`userId` IN (...);
|
|
2200
|
+
|
|
2201
|
+
/*
|
|
2202
|
+
* @returns
|
|
2203
|
+
* {
|
|
2204
|
+
* id : 1,
|
|
2205
|
+
* username: "tspace1",
|
|
2206
|
+
* email : "tspace1@gmail.com",
|
|
2207
|
+
* posts : [
|
|
2208
|
+
* {
|
|
2209
|
+
* id : 1 ,
|
|
2210
|
+
* user_id : 1,
|
|
2211
|
+
* title : "post 1"
|
|
2212
|
+
* },
|
|
2213
|
+
* {
|
|
2214
|
+
* id : 2 ,
|
|
2215
|
+
* user_id : 1,
|
|
2216
|
+
* title : "post 2"
|
|
2217
|
+
* }
|
|
2218
|
+
* ]
|
|
2219
|
+
* }
|
|
2220
|
+
*/
|
|
2221
|
+
|
|
2222
|
+
```
|
|
2223
|
+
|
|
2224
|
+
#### Deeply Nested Relations
|
|
2225
|
+
|
|
2226
|
+
Relationships can involve deep connections.
|
|
2227
|
+
Let's consider an example of a deep relationship:
|
|
2228
|
+
|
|
2229
|
+
```js
|
|
2230
|
+
import { Model } from 'tspace-mysql'
|
|
2231
|
+
|
|
2232
|
+
class User extends Model {
|
|
2233
|
+
constructor(){
|
|
2234
|
+
super()
|
|
2235
|
+
this.hasMany({ name : 'posts' , model : Post })
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
+--------------------------------------------------------------------------+
|
|
2239
|
+
class Post extends Model {
|
|
2240
|
+
constructor(){
|
|
2241
|
+
super()
|
|
2242
|
+
this.hasMany({ name : 'comments' , model : Comment })
|
|
2243
|
+
this.belongsTo({ name : 'user' , model : User })
|
|
2244
|
+
this.belongsToMany({ name : 'users' , model : User , modelPivot : PostUser })
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
+--------------------------------------------------------------------------+
|
|
2248
|
+
class Comment extends Model {
|
|
2249
|
+
constructor(){
|
|
2250
|
+
super()
|
|
2251
|
+
this.hasMany({ name : 'users' , model : User })
|
|
2252
|
+
this.belongsTo({ name : 'post' , model : Post })
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
class PostUser extends Model {}
|
|
2257
|
+
+--------------------------------------------------------------------------+
|
|
2258
|
+
// Deeply nested relations
|
|
2259
|
+
await new User()
|
|
2260
|
+
.relations('posts')
|
|
2261
|
+
.relationQuery('posts', (query : Post) => {
|
|
2262
|
+
return query
|
|
2263
|
+
.relations('comments','user','users')
|
|
2264
|
+
.relationQuery('comments', (query : Comment) => {
|
|
2265
|
+
return query.relations('user','post')
|
|
2266
|
+
})
|
|
2267
|
+
.relationQuery('user', (query : User) => {
|
|
2268
|
+
return query.relations('posts').relationQuery('posts',(query : Post)=> {
|
|
2269
|
+
return query.relations('comments','user')
|
|
2270
|
+
// relation n, n, ...n
|
|
2271
|
+
})
|
|
2272
|
+
})
|
|
2273
|
+
.relationQuery('users', (query : User) => {
|
|
2274
|
+
return query
|
|
2275
|
+
})
|
|
2276
|
+
.relationQuery('users', (query : PostUser) => {
|
|
2277
|
+
return query
|
|
2278
|
+
}, { pivot : true })
|
|
2279
|
+
})
|
|
2280
|
+
.findMany()
|
|
2281
|
+
|
|
2282
|
+
// Select some columns in nested relations
|
|
2283
|
+
await new User()
|
|
2284
|
+
.relations('posts')
|
|
2285
|
+
.relationQuery('posts', (query : Post) => query.select('id','user_id','title'))
|
|
2286
|
+
.findMany()
|
|
2287
|
+
|
|
2288
|
+
// Where some columns in nested relations
|
|
2289
|
+
await new User()
|
|
2290
|
+
.relations('posts')
|
|
2291
|
+
.relationQuery('posts', (query : Post) => query.whereIn('id',[1,3,5]))
|
|
2292
|
+
.findMany()
|
|
2293
|
+
|
|
2294
|
+
// Sort data in nested relations
|
|
2295
|
+
await new User()
|
|
2296
|
+
.relations('posts')
|
|
2297
|
+
.relationQuery('posts', (query : Post) => query.latest('id'))
|
|
2298
|
+
.findMany()
|
|
2299
|
+
|
|
2300
|
+
// Limit data in nested relations
|
|
2301
|
+
await new User()
|
|
2302
|
+
.relations('posts')
|
|
2303
|
+
.relationQuery('posts', (query : Post) => {
|
|
2304
|
+
return query
|
|
2305
|
+
.limit(1)
|
|
2306
|
+
.relations('comments')
|
|
2307
|
+
.relationQuery('comments', (query : Comment) => query.limit(1))
|
|
2308
|
+
})
|
|
2309
|
+
.findMany()
|
|
2310
|
+
|
|
2311
|
+
```
|
|
2312
|
+
|
|
2313
|
+
#### Relation Exists
|
|
2314
|
+
|
|
2315
|
+
Relationships can return results only if they are not empty in relations, considering soft deletes.
|
|
2316
|
+
Let's illustrate this with an example of an existence check in relations:
|
|
2317
|
+
|
|
2318
|
+
```js
|
|
2319
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2320
|
+
| table users | |
|
|
2321
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2322
|
+
| id | username | email | deleted_at |
|
|
2323
|
+
|-------------|--------------|----------------------------|--------------------|
|
|
2324
|
+
| 1 | tspace1 | tspace1@gmail.com | |
|
|
2325
|
+
| 2 | tspace2 | tspace2@gmail.com | |
|
|
2326
|
+
| 3 | tspace3 | tspace3@gmail.com | |
|
|
2327
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2328
|
+
|
|
2329
|
+
|
|
2330
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2331
|
+
| table posts | |
|
|
2332
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2333
|
+
| id | user_id | title | deleted_at |
|
|
2334
|
+
|-------------|--------------|----------------------------|--------------------|
|
|
2335
|
+
| 1 | 1 | posts 1 |2020-07-15 00:00:00 |
|
|
2336
|
+
| 2 | 2 | posts 2 | |
|
|
2337
|
+
| 3 | 3 | posts 3 |2020-07-15 00:00:00 |
|
|
2338
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2339
|
+
|
|
2340
|
+
import { Model } from 'tspace-mysql'
|
|
2341
|
+
|
|
2342
|
+
class User extends Model {
|
|
2343
|
+
constructor(){
|
|
2344
|
+
super()
|
|
2345
|
+
this.hasMany({ name : 'posts' , model : Post })
|
|
2346
|
+
this.useSoftDelete()
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
+--------------------------------------------------------------------------+
|
|
2351
|
+
|
|
2352
|
+
class Post extends Model {
|
|
2353
|
+
constructor(){
|
|
2354
|
+
super()
|
|
2355
|
+
this.hasMany({ name : 'comments' , model : Comment })
|
|
2356
|
+
this.belongsTo({ name : 'user' , model : User })
|
|
2357
|
+
this.useSoftDelete()
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
// normal relations
|
|
2361
|
+
await new User().relations('posts').findMany()
|
|
2362
|
+
// SELECT * FROM `users` WHERE `users`.`deleted_at`;
|
|
2363
|
+
// SELECT * FROM `posts` WHERE `posts`.`userId` IN (...) AND `posts`.`deleted_at` IS NULL;
|
|
2364
|
+
|
|
2365
|
+
/*
|
|
2366
|
+
* @returns [
|
|
2367
|
+
* {
|
|
2368
|
+
* id : 1,
|
|
2369
|
+
* username: "tspace1",
|
|
2370
|
+
* email : "tspace1@gmail.com",
|
|
2371
|
+
* posts : []
|
|
2372
|
+
* },
|
|
2373
|
+
* {
|
|
2374
|
+
* id : 2,
|
|
2375
|
+
* username: "tspace2",
|
|
2376
|
+
* email : "tspace2@gmail.com",
|
|
2377
|
+
* posts : [
|
|
2378
|
+
* {
|
|
2379
|
+
* id : 2,
|
|
2380
|
+
* user_id : 2,
|
|
2381
|
+
* title : "posts 2"
|
|
2382
|
+
* }
|
|
2383
|
+
* ]
|
|
2384
|
+
* },
|
|
2385
|
+
* {
|
|
2386
|
+
* id : 3,
|
|
2387
|
+
* username: "tspace3",
|
|
2388
|
+
* email : "tspace3@gmail.com",
|
|
2389
|
+
* posts : []
|
|
2390
|
+
* }
|
|
2391
|
+
* ]
|
|
2392
|
+
*/
|
|
2393
|
+
|
|
2394
|
+
await new User().relationsExists('posts').findMany()
|
|
2395
|
+
// SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL
|
|
2396
|
+
// AND EXISTS (SELECT 1 FROM `posts` WHERE `users`.`id` = `posts`.`user_id` AND `posts`.`deletedA_at` IS NULL);
|
|
2397
|
+
|
|
2398
|
+
// SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NULL;
|
|
2399
|
+
|
|
2400
|
+
/*
|
|
2401
|
+
* @returns [
|
|
2402
|
+
* {
|
|
2403
|
+
* id : 2,
|
|
2404
|
+
* username: "tspace2",
|
|
2405
|
+
* email : "tspace2@gmail.com",
|
|
2406
|
+
* posts : [
|
|
2407
|
+
* {
|
|
2408
|
+
* id : 2,
|
|
2409
|
+
* user_id : 2,
|
|
2410
|
+
* title : "posts 2"
|
|
2411
|
+
* }
|
|
2412
|
+
* ]
|
|
2413
|
+
* }
|
|
2414
|
+
* ]
|
|
2415
|
+
* because posts id 1 and id 3 has been removed from database (using soft delete)
|
|
2416
|
+
*/
|
|
2417
|
+
|
|
2418
|
+
```
|
|
2419
|
+
|
|
2420
|
+
#### Relation Count
|
|
2421
|
+
Relationships will retrieving the count of related records without loading the data of related models
|
|
2422
|
+
Let's illustrate this with an example of an existence check in relations:
|
|
2423
|
+
```js
|
|
2424
|
+
|
|
2425
|
+
+-------------+--------------+----------------------------+
|
|
2426
|
+
| table users |
|
|
2427
|
+
+-------------+--------------+----------------------------+
|
|
2428
|
+
| id | username | email |
|
|
2429
|
+
|-------------|--------------|----------------------------|
|
|
2430
|
+
| 1 | tspace1 | tspace1@gmail.com |
|
|
2431
|
+
| 2 | tspace2 | tspace2@gmail.com |
|
|
2432
|
+
+-------------+--------------+----------------------------+
|
|
2433
|
+
|
|
2434
|
+
+-------------+--------------+----------------------------+
|
|
2435
|
+
| table posts |
|
|
2436
|
+
+-------------+--------------+----------------------------+
|
|
2437
|
+
| id | user_id | title |
|
|
2438
|
+
|-------------|--------------|----------------------------|
|
|
2439
|
+
| 1 | 1 | posts 1 |
|
|
2440
|
+
| 2 | 1 | posts 2 |
|
|
2441
|
+
| 3 | 2 | posts 3 |
|
|
2442
|
+
+-------------+--------------+----------------------------+
|
|
2443
|
+
|
|
2444
|
+
import { Model } from 'tspace-mysql'
|
|
2445
|
+
|
|
2446
|
+
class User extends Model {
|
|
2447
|
+
constructor(){
|
|
2448
|
+
super()
|
|
2449
|
+
this.hasMany({ name : 'posts' , model : Post })
|
|
2450
|
+
this.useSoftDelete()
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// you also use .withCount()
|
|
2455
|
+
await new User().relationsCount('posts').findMany()
|
|
2456
|
+
// SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL;
|
|
2457
|
+
|
|
2458
|
+
// SELECT `posts`.`user_id`, COUNT(`user_id`) AS `aggregate` FROM `posts`
|
|
2459
|
+
// WHERE `posts`.`user_id` IN ('1','2') AND `posts`.`deleted_at` IS NULL GROUP BY `posts`.`user_id`;
|
|
2460
|
+
|
|
2461
|
+
/*
|
|
2462
|
+
* @returns [
|
|
2463
|
+
* {
|
|
2464
|
+
* id : 1,
|
|
2465
|
+
* username: "tspace1",
|
|
2466
|
+
* email : "tspace1@gmail.com",
|
|
2467
|
+
* posts : 2
|
|
2468
|
+
* }
|
|
2469
|
+
* {
|
|
2470
|
+
* id : 2,
|
|
2471
|
+
* username: "tspace2",
|
|
2472
|
+
* email : "tspace2@gmail.com",
|
|
2473
|
+
* posts : 1
|
|
2474
|
+
* }
|
|
2475
|
+
* ]
|
|
2476
|
+
*/
|
|
2477
|
+
|
|
2478
|
+
```
|
|
2479
|
+
|
|
2480
|
+
#### Relation Trashed
|
|
2481
|
+
Relationships can return results only if they are deleted in table, considering soft deletes.
|
|
2482
|
+
Let's illustrate this with an example:
|
|
2483
|
+
```js
|
|
2484
|
+
|
|
2485
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2486
|
+
| table users | |
|
|
2487
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2488
|
+
| id | username | email | deleted_at |
|
|
2489
|
+
|-------------|--------------|----------------------------|--------------------|
|
|
2490
|
+
| 1 | tspace1 | tspace1@gmail.com | |
|
|
2491
|
+
| 2 | tspace2 | tspace2@gmail.com | |
|
|
2492
|
+
| 3 | tspace3 | tspace3@gmail.com |2020-07-15 00:00:00 |
|
|
2493
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2494
|
+
|
|
2495
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2496
|
+
| table posts | |
|
|
2497
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2498
|
+
| id | user_id | title | deleted_at |
|
|
2499
|
+
|-------------|--------------|----------------------------|--------------------|
|
|
2500
|
+
| 1 | 1 | posts 1 |2020-07-15 00:00:00 |
|
|
2501
|
+
| 2 | 2 | posts 2 | |
|
|
2502
|
+
| 3 | 3 | posts 3 |2020-07-15 00:00:00 |
|
|
2503
|
+
+-------------+--------------+----------------------------+--------------------+
|
|
2504
|
+
|
|
2505
|
+
import { Model } from 'tspace-mysql'
|
|
2506
|
+
|
|
2507
|
+
class User extends Model {
|
|
2508
|
+
constructor(){
|
|
2509
|
+
super()
|
|
2510
|
+
this.hasMany({ name : 'posts' , model : Post })
|
|
2511
|
+
this.useSoftDelete()
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
+--------------------------------------------------------------------------+
|
|
2516
|
+
|
|
2517
|
+
class Post extends Model {
|
|
2518
|
+
constructor(){
|
|
2519
|
+
super()
|
|
2520
|
+
this.hasMany({ name : 'comments' , model : Comment })
|
|
2521
|
+
this.belongsTo({ name : 'user' , model : User })
|
|
2522
|
+
this.useSoftDelete()
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
// normal relations
|
|
2527
|
+
await new User().relations('posts').findMany()
|
|
2528
|
+
// SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL;
|
|
2529
|
+
// SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NULL;
|
|
2530
|
+
|
|
2531
|
+
/*
|
|
2532
|
+
* @returns [
|
|
2533
|
+
* {
|
|
2534
|
+
* id : 1,
|
|
2535
|
+
* username: "tspace1",
|
|
2536
|
+
* email : "tspace1@gmail.com",
|
|
2537
|
+
* posts : []
|
|
2538
|
+
* }
|
|
2539
|
+
* {
|
|
2540
|
+
* id : 2,
|
|
2541
|
+
* username: "tspace2",
|
|
2542
|
+
* email : "tspace2@gmail.com",
|
|
2543
|
+
* posts : [
|
|
2544
|
+
* {
|
|
2545
|
+
* id : 2,
|
|
2546
|
+
* user_id : 2,
|
|
2547
|
+
* title : "posts 2"
|
|
2548
|
+
* }
|
|
2549
|
+
* ]
|
|
2550
|
+
* }
|
|
2551
|
+
* ]
|
|
2552
|
+
*/
|
|
2553
|
+
|
|
2554
|
+
// relationsTrashed
|
|
2555
|
+
await new User().relationsTrashed('posts').findMany()
|
|
2556
|
+
// SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL;
|
|
2557
|
+
// SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NOT NULL;
|
|
2558
|
+
|
|
2559
|
+
/*
|
|
2560
|
+
* @returns [
|
|
2561
|
+
* {
|
|
2562
|
+
* id : 1,
|
|
2563
|
+
* username: "tspace1",
|
|
2564
|
+
* email : "tspace1@gmail.com",
|
|
2565
|
+
* posts : [
|
|
2566
|
+
* {
|
|
2567
|
+
* id : 1,
|
|
2568
|
+
* user_id : 1,
|
|
2569
|
+
* title : "posts 1"
|
|
2570
|
+
* }
|
|
2571
|
+
* ]
|
|
2572
|
+
* }
|
|
2573
|
+
* {
|
|
2574
|
+
* id : 2,
|
|
2575
|
+
* username: "tspace2",
|
|
2576
|
+
* email : "tspace2@gmail.com",
|
|
2577
|
+
* posts : []
|
|
2578
|
+
* }
|
|
2579
|
+
* ]
|
|
2580
|
+
*/
|
|
2581
|
+
|
|
2582
|
+
// relationsTrashed + trashed
|
|
2583
|
+
await new User().relationsTrashed('posts').trashed().findMany()
|
|
2584
|
+
// SELECT * FROM `users` WHERE `users`.`deleted_at` IS NOT NULL;
|
|
2585
|
+
// SELECT * FROM `posts` WHERE `posts`.`user_id` IN (...) AND `posts`.`deleted_at` IS NOT NULL;
|
|
2586
|
+
/*
|
|
2587
|
+
* @returns [
|
|
2588
|
+
* {
|
|
2589
|
+
* id : 3,
|
|
2590
|
+
* username: "tspace3",
|
|
2591
|
+
* email : "tspace3@gmail.com",
|
|
2592
|
+
* posts : [
|
|
2593
|
+
* {
|
|
2594
|
+
* id : 3,
|
|
2595
|
+
* user_id : 3,
|
|
2596
|
+
* title : "posts 3"
|
|
2597
|
+
* }
|
|
2598
|
+
* ]
|
|
2599
|
+
* }
|
|
2600
|
+
* ]
|
|
2601
|
+
*/
|
|
2602
|
+
|
|
2603
|
+
```
|
|
2604
|
+
|
|
2605
|
+
### Built in Relation Functions
|
|
2606
|
+
Certainly, let's illustrate the use of a built-in function in the results of relationships:
|
|
2607
|
+
|
|
2608
|
+
```js
|
|
2609
|
+
import { Model } from 'tspace-mysql'
|
|
2610
|
+
|
|
2611
|
+
class User extends Model {
|
|
2612
|
+
constructor(){
|
|
2613
|
+
super()
|
|
2614
|
+
this.hasMany({ name : 'posts' , model : Post })
|
|
2615
|
+
this.useBuiltInRelationFunctions()
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
+--------------------------------------------------------------------------+
|
|
2619
|
+
class Post extends Model {
|
|
2620
|
+
constructor(){
|
|
2621
|
+
super()
|
|
2622
|
+
this.hasMany({ name : 'comments' , model : Comment })
|
|
2623
|
+
this.belongsTo({ name : 'user' , model : User })
|
|
2624
|
+
this.useBuiltInRelationFunctions()
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
+--------------------------------------------------------------------------+
|
|
2628
|
+
class Comment extends Model {
|
|
2629
|
+
constructor(){
|
|
2630
|
+
super()
|
|
2631
|
+
this.hasMany({ name : 'users' , model : User })
|
|
2632
|
+
this.belongsTo({ name : 'post' , model : Post })
|
|
2633
|
+
this.useBuiltInRelationFunctions()
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
+--------------------------------------------------------------------------+
|
|
2637
|
+
const user = await new User().findOne()
|
|
2638
|
+
const posts = await user.$posts()
|
|
2639
|
+
|
|
2640
|
+
/** Warning built-in function has Big-O effect */
|
|
2641
|
+
for (const post of posts) {
|
|
2642
|
+
const comments = await post.$comments()
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
```
|
|
2646
|
+
|
|
2647
|
+
### Cache
|
|
2648
|
+
|
|
2649
|
+
Cache can be used in a Model.
|
|
2650
|
+
Let's illustrate this with an example of a cache:
|
|
2651
|
+
|
|
2652
|
+
```js
|
|
2653
|
+
// support memory db and redis
|
|
2654
|
+
// set cache in file config .env , .env.development ... etc
|
|
2655
|
+
DB_CACHE = memory // by default
|
|
2656
|
+
|
|
2657
|
+
// for db
|
|
2658
|
+
DB_CACHE = db
|
|
2659
|
+
|
|
2660
|
+
// for redis
|
|
2661
|
+
DB_CACHE = redis://username:password@server:6379
|
|
2662
|
+
|
|
2663
|
+
const users = await new User()
|
|
2664
|
+
.cache({
|
|
2665
|
+
key : 'users', // key of the cache
|
|
2666
|
+
expires : 1000 * 60 // cache expires in 60 seconds
|
|
2667
|
+
})
|
|
2668
|
+
.sleep(5) // assume the query takes longer than 5 seconds...
|
|
2669
|
+
.findMany()
|
|
2670
|
+
|
|
2671
|
+
```
|
|
2672
|
+
|
|
2673
|
+
### Decorator
|
|
2674
|
+
|
|
2675
|
+
Decorators can be used in a Model.
|
|
2676
|
+
Let's illustrate this with an example of a decorators:
|
|
2677
|
+
|
|
2678
|
+
```js
|
|
2679
|
+
|
|
2680
|
+
import {
|
|
2681
|
+
Blueprint, Model ,
|
|
2682
|
+
Table ,TableSingular, TablePlural,
|
|
2683
|
+
UUID, SoftDelete, Timestamp,
|
|
2684
|
+
Pattern, CamelCase , snakeCase ,
|
|
2685
|
+
Column, Validate, Observer
|
|
2686
|
+
} from 'tspace-mysql'
|
|
2687
|
+
import { Post } from './Post'
|
|
2688
|
+
import { PostUser } from './PostUser'
|
|
2689
|
+
|
|
2690
|
+
class UserObserve {
|
|
2691
|
+
|
|
2692
|
+
public selected(results) {
|
|
2693
|
+
console.log({ results , selected : true })
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
public created(results) {
|
|
2697
|
+
console.log({ results , created : true })
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
public updated(results) {
|
|
2701
|
+
console.log({ results , updated : true })
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
public deleted(results) {
|
|
2705
|
+
console.log({ results , deleted : true })
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
@Pattern('camelCase')
|
|
2710
|
+
@Observer(UserObserve)
|
|
2711
|
+
@UUID()
|
|
2712
|
+
@SoftDelete()
|
|
2713
|
+
@Timestamp()
|
|
2714
|
+
@Table('users')
|
|
2715
|
+
class User extends Model {
|
|
2716
|
+
|
|
2717
|
+
@Column(() => Blueprint.int().notNull().primary().autoIncrement())
|
|
2718
|
+
public id!: number
|
|
2719
|
+
|
|
2720
|
+
@Column(() => Blueprint.varchar(50).null())
|
|
2721
|
+
public uuid!: string
|
|
2722
|
+
|
|
2723
|
+
@Column(() => Blueprint.varchar(50).null())
|
|
2724
|
+
@Validate({
|
|
2725
|
+
type : String,
|
|
2726
|
+
require : true,
|
|
2727
|
+
length : 50,
|
|
2728
|
+
match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
2729
|
+
unique : true,
|
|
2730
|
+
fn : (email : string) => /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)
|
|
2731
|
+
})
|
|
2732
|
+
public email!: string
|
|
2733
|
+
|
|
2734
|
+
@Column(() => Blueprint.varchar(50).null())
|
|
2735
|
+
public name !: string
|
|
2736
|
+
|
|
2737
|
+
@Column(() => Blueprint.varchar(50).null())
|
|
2738
|
+
public username !: string
|
|
2739
|
+
|
|
2740
|
+
@Column(() => Blueprint.varchar(50).null())
|
|
2741
|
+
public password !: string
|
|
2742
|
+
|
|
2743
|
+
@Column(() => Blueprint.timestamp().null())
|
|
2744
|
+
public createdAt!: Date
|
|
2745
|
+
|
|
2746
|
+
@Column(() => Blueprint.timestamp().null())
|
|
2747
|
+
public updatedAt!: Date
|
|
2748
|
+
|
|
2749
|
+
@Column(() => Blueprint.timestamp().null())
|
|
2750
|
+
public deletedAt!: Date
|
|
2751
|
+
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
export { User }
|
|
2755
|
+
export default User
|
|
2756
|
+
|
|
2757
|
+
```
|
|
2758
|
+
|
|
2759
|
+
### Schema
|
|
2760
|
+
|
|
2761
|
+
The schema refers to the structure of the database as it pertains to the objects and classes in the model.
|
|
2762
|
+
using the following:
|
|
2763
|
+
|
|
2764
|
+
#### Schema Model
|
|
2765
|
+
|
|
2766
|
+
```js
|
|
2767
|
+
import { Model, Blueprint , type T } from "tspace-mysql";
|
|
2768
|
+
|
|
2769
|
+
const schema = {
|
|
2770
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
2771
|
+
uuid: Blueprint.varchar(50).null().index(),
|
|
2772
|
+
name: Blueprint.varchar(191).notNull(),
|
|
2773
|
+
email: Blueprint.varchar(191).notNull(),
|
|
2774
|
+
createdAt: Blueprint.timestamp().null(),
|
|
2775
|
+
updatedAt: Blueprint.timestamp().null(),
|
|
2776
|
+
deletedAt: Blueprint.timestamp().null()
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
|
|
2780
|
+
// make type in TS
|
|
2781
|
+
type TS = T.Schema<typeof Schema>
|
|
2782
|
+
|
|
2783
|
+
// the TSchemaUser will be created like that
|
|
2784
|
+
/**
|
|
2785
|
+
{
|
|
2786
|
+
id : number,
|
|
2787
|
+
uuid : string | null,
|
|
2788
|
+
name : string,
|
|
2789
|
+
email : string,
|
|
2790
|
+
createdAt : Date | string | null,
|
|
2791
|
+
updatedAt : Date | string | null,
|
|
2792
|
+
deletedAt : Date | string | null
|
|
2793
|
+
}
|
|
2794
|
+
*/
|
|
2795
|
+
|
|
2796
|
+
|
|
2797
|
+
class User extends Model<TS> // use the schema for this User model
|
|
2798
|
+
{
|
|
2799
|
+
constructor() {
|
|
2800
|
+
super();
|
|
2801
|
+
this.useCamelCase()
|
|
2802
|
+
this.useSchema(schema)
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
```
|
|
2807
|
+
|
|
2808
|
+
#### Virtual Column
|
|
2809
|
+
```js
|
|
2810
|
+
|
|
2811
|
+
import { Model, Blueprint , type T } from "tspace-mysql";
|
|
2812
|
+
|
|
2813
|
+
const schema = {
|
|
2814
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
2815
|
+
uuid: Blueprint.varchar(50).null().index(),
|
|
2816
|
+
firstName: Blueprint.varchar(191).notNull(),
|
|
2817
|
+
lastName : Blueprint.varchar(191).notNull(),
|
|
2818
|
+
email: Blueprint.varchar(191).notNull(),
|
|
2819
|
+
createdAt: Blueprint.timestamp().null(),
|
|
2820
|
+
updatedAt: Blueprint.timestamp().null(),
|
|
2821
|
+
deletedAt: Blueprint.timestamp().null(),
|
|
2822
|
+
|
|
2823
|
+
// Define you virtual column to schema
|
|
2824
|
+
fullName : new Blueprint().virtualColumn(`CONCAT(firstName,' ', lastName)`),
|
|
2825
|
+
countPosts : new Blueprint().virtualColumn(`(SELECT COUNT(*) FROM posts WHERE posts.userid = users.id)`)
|
|
2826
|
+
|
|
2827
|
+
// if you need to custom the virtualColumn column for some method.
|
|
2828
|
+
// fullName : new Blueprint().virtualColumn({
|
|
2829
|
+
// select : `CONCAT(firstName,' ', lastName)`,
|
|
2830
|
+
// where : `CONCAT(firstName,' ', lastName)`,
|
|
2831
|
+
// orderBy : `CONCAT(firstName,' ', lastName)`,
|
|
2832
|
+
// groupBy : `CONCAT(firstName,' ', lastName)`,
|
|
2833
|
+
// }),
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
type TS = T.Schema<typeof Schema>
|
|
2837
|
+
|
|
2838
|
+
class User extends Model<TS> {
|
|
2839
|
+
constructor() {
|
|
2840
|
+
super();
|
|
2841
|
+
this.useSchema(schema)
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
const users = await new User()
|
|
2845
|
+
.select('id','firstName','lastName','fullName','countPosts')
|
|
2846
|
+
.where('fullName','LIKE',`%tspace-mysql%`)
|
|
2847
|
+
.orderBy('fullName','desc')
|
|
2848
|
+
.groupBy('fullName')
|
|
2849
|
+
.findMany()
|
|
2850
|
+
|
|
2851
|
+
// SELECT
|
|
2852
|
+
// `users`.`id`, `users`.`firstName`, `users`.`lastName`,
|
|
2853
|
+
// CONCAT(firstName,' ', lastName) AS fullName ,
|
|
2854
|
+
// (SELECT COUNT(*) FROM posts WHERE posts.userid = users.id) AS countPosts
|
|
2855
|
+
// FROM `users`
|
|
2856
|
+
// WHERE CONCAT(firstName,' ', lastName) LIKE '%tspace-mysql%'
|
|
2857
|
+
// GROUP BY CONCAT(firstName,' ', lastName)
|
|
2858
|
+
// ORDER BY CONCAT(firstName,' ', lastName) DESC
|
|
2859
|
+
|
|
2860
|
+
```
|
|
2861
|
+
|
|
2862
|
+
#### Validation
|
|
2863
|
+
|
|
2864
|
+
Validate the schema of Model
|
|
2865
|
+
let's example a validator model:
|
|
2866
|
+
|
|
2867
|
+
```js
|
|
2868
|
+
import { Model, Blueprint } from "tspace-mysql";
|
|
2869
|
+
class User extends Model {
|
|
2870
|
+
constructor() {
|
|
2871
|
+
super();
|
|
2872
|
+
this.useCamelCase();
|
|
2873
|
+
this.useSchema({
|
|
2874
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
2875
|
+
uuid: Blueprint.varchar(50).null(),
|
|
2876
|
+
name: Blueprint.varchar(191).notNull(),
|
|
2877
|
+
email: Blueprint.varchar(191).notNull(),
|
|
2878
|
+
createdAt: Blueprint.timestamp().null(),
|
|
2879
|
+
updatedAt: Blueprint.timestamp().null(),
|
|
2880
|
+
deletedAt: Blueprint.timestamp().null(),
|
|
2881
|
+
});
|
|
2882
|
+
|
|
2883
|
+
// validate input when create or update reference to the schema in 'this.useSchema'
|
|
2884
|
+
this.useValidateSchema({
|
|
2885
|
+
id: Number,
|
|
2886
|
+
uuid: Number,
|
|
2887
|
+
name: {
|
|
2888
|
+
type: String,
|
|
2889
|
+
length: 191,
|
|
2890
|
+
require: true,
|
|
2891
|
+
json: true,
|
|
2892
|
+
},
|
|
2893
|
+
email: {
|
|
2894
|
+
type: String,
|
|
2895
|
+
require: true,
|
|
2896
|
+
length: 191,
|
|
2897
|
+
match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
2898
|
+
unique: true,
|
|
2899
|
+
fn: (email: string) => {
|
|
2900
|
+
return /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)
|
|
2901
|
+
}
|
|
2902
|
+
},
|
|
2903
|
+
createdAt: Date,
|
|
2904
|
+
updatedAt: Date,
|
|
2905
|
+
deletedAt: Date,
|
|
2906
|
+
});
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
```
|
|
2910
|
+
|
|
2911
|
+
#### Sync
|
|
2912
|
+
|
|
2913
|
+
Sync the schema with the "Models" setting in your directory.
|
|
2914
|
+
This process will verify and update table columns and foreign keys as needed.
|
|
2915
|
+
Ensure that the relationships are correctly established through the 'useSchema' method in your models.
|
|
2916
|
+
Let's examine a basic sync class:
|
|
2917
|
+
|
|
2918
|
+
```js
|
|
2919
|
+
/**
|
|
2920
|
+
*
|
|
2921
|
+
* @Ex directory
|
|
2922
|
+
*
|
|
2923
|
+
* - node_modules
|
|
2924
|
+
* - src
|
|
2925
|
+
* - index.ts
|
|
2926
|
+
* - Models
|
|
2927
|
+
* - User.ts
|
|
2928
|
+
* - Post.ts
|
|
2929
|
+
*/
|
|
2930
|
+
|
|
2931
|
+
// file User
|
|
2932
|
+
class User extends Model {
|
|
2933
|
+
constructor() {
|
|
2934
|
+
super();
|
|
2935
|
+
this.hasMany({ name: "posts", model: Post });
|
|
2936
|
+
|
|
2937
|
+
// if you need to initialize data when creating the table, you can use the following.
|
|
2938
|
+
this.whenCreatingTable(async () => {
|
|
2939
|
+
return await new User()
|
|
2940
|
+
.create({
|
|
2941
|
+
...columns,
|
|
2942
|
+
})
|
|
2943
|
+
.void()
|
|
2944
|
+
.save();
|
|
2945
|
+
});
|
|
2946
|
+
|
|
2947
|
+
this.useSchema({
|
|
2948
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
2949
|
+
uuid: Blueprint.varchar(50).null(),
|
|
2950
|
+
email: Blueprint.int().notNull().unique(),
|
|
2951
|
+
name: Blueprint.varchar(255).null(),
|
|
2952
|
+
created_at: Blueprint.timestamp().null(),
|
|
2953
|
+
updated_at: Blueprint.timestamp().null(),
|
|
2954
|
+
deleted_at: Blueprint.timestamp().null(),
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
// file Post
|
|
2960
|
+
import User from "./User";
|
|
2961
|
+
class Post extends Model {
|
|
2962
|
+
constructor() {
|
|
2963
|
+
super();
|
|
2964
|
+
this.hasMany({ name: "comments", model: Comment });
|
|
2965
|
+
this.belongsTo({ name: "user", model: User });
|
|
2966
|
+
this.useSchema({
|
|
2967
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
2968
|
+
uuid: Blueprint.varchar(50).null(),
|
|
2969
|
+
user_id: Blueprint.int().notNull().foreign({
|
|
2970
|
+
references: "id",
|
|
2971
|
+
on: User,
|
|
2972
|
+
onDelete: "CASCADE",
|
|
2973
|
+
onUpdate: "CASCADE",
|
|
2974
|
+
}),
|
|
2975
|
+
title: Blueprint.varchar(255).null(),
|
|
2976
|
+
created_at: Blueprint.timestamp().null(),
|
|
2977
|
+
updated_at: Blueprint.timestamp().null(),
|
|
2978
|
+
deleted_at: Blueprint.timestamp().null(),
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
await Schema.sync(`/src/Models`, {
|
|
2984
|
+
force: true,
|
|
2985
|
+
log: true,
|
|
2986
|
+
foreign: true,
|
|
2987
|
+
changed: true,
|
|
2988
|
+
});
|
|
2989
|
+
|
|
2990
|
+
// You can also synchronize using the Model.
|
|
2991
|
+
await new User().sync({ force: true, foreign: true, changed: true });
|
|
2992
|
+
```
|
|
2993
|
+
|
|
2994
|
+
### SoftDelete
|
|
2995
|
+
|
|
2996
|
+
```js
|
|
2997
|
+
|
|
2998
|
+
import { Model } from 'tspace-mysql'
|
|
2999
|
+
class User extends Model {
|
|
3000
|
+
constructor() {
|
|
3001
|
+
super()
|
|
3002
|
+
this.useSoftDelete() // All query will be where 'deleted_at' is null
|
|
3003
|
+
|
|
3004
|
+
// You can also use patterns camelCase to covert the 'deleted_at' to 'deletedAt'
|
|
3005
|
+
// You can also customize the column 'deleted_at'
|
|
3006
|
+
this.useSoftDelete('deletedAtCustom')
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
const user = await new User().where('user_id',1).findOne()
|
|
3011
|
+
// SELECT * FROM `users` WHERE `users`.`userId` = '1' and `users`.`deletedAtCustom` IS NULL LIMIT 1;
|
|
3012
|
+
|
|
3013
|
+
// find in trashed
|
|
3014
|
+
const user = await new User().trashed().findMany()
|
|
3015
|
+
// SELECT * FROM `users` WHERE `users`.`userId` = '1' and `users`.`deletedAtCustom` IS NOT NULL;
|
|
3016
|
+
|
|
3017
|
+
```
|
|
3018
|
+
|
|
3019
|
+
### Type Safety
|
|
3020
|
+
Type Type Safety in TypeScript refers to the ability of the language to detect and prevent type errors during compile-time.
|
|
3021
|
+
Type Type Safety still works when you add additional types to your model, using the following:
|
|
3022
|
+
|
|
3023
|
+
```js
|
|
3024
|
+
// in file User.ts
|
|
3025
|
+
import { Model , Blueprint , type T } from 'tspace-mysql'
|
|
3026
|
+
import Phone from '../Phone'
|
|
3027
|
+
|
|
3028
|
+
const schemaUser = {
|
|
3029
|
+
id :Blueprint.int().notNull().primary().autoIncrement(),
|
|
3030
|
+
uuid :Blueprint.varchar(50).null(),
|
|
3031
|
+
email :Blueprint.varchar(50).null(),
|
|
3032
|
+
name :Blueprint.varchar(255).null(),
|
|
3033
|
+
username : Blueprint.varchar(255).null(),
|
|
3034
|
+
password : Blueprint.varchar(255).null(),
|
|
3035
|
+
createdAt :Blueprint.timestamp().null(),
|
|
3036
|
+
updatedAt :Blueprint.timestamp().null()
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
type TSchemaUser = T.SchemaStatic<typeof schemaUser>
|
|
3040
|
+
// TSchemaUser = T.Schema<typeof schemaUser>
|
|
3041
|
+
|
|
3042
|
+
// TSchema allowed to set any new keys without in the schema to results
|
|
3043
|
+
// TSchemaStatic not allowed to set any new keys without in the schema to results
|
|
3044
|
+
|
|
3045
|
+
class User extends Model<TSchemaUser> { // Add this '<TSchemaUser>' to activate the type for the Model.
|
|
3046
|
+
constructor() {
|
|
3047
|
+
super()
|
|
3048
|
+
this.useSchema(schemaUser)
|
|
3049
|
+
this.hasOne({ model : Phone, name : 'phone' })
|
|
3050
|
+
this.hasMany({ model : Phone, name : 'phones' })
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
export { User }
|
|
3055
|
+
export default User
|
|
3056
|
+
|
|
3057
|
+
+--------------------------------------------------------------------------+
|
|
3058
|
+
|
|
3059
|
+
// in file Phone.ts
|
|
3060
|
+
import { Model , Blueprint , type T } from 'tspace-mysql'
|
|
3061
|
+
import { User } from './User.ts'
|
|
3062
|
+
const schemaPhone = {
|
|
3063
|
+
id :Blueprint.int().notNull().primary().autoIncrement(),
|
|
3064
|
+
uuid :Blueprint.varchar(50).null(),
|
|
3065
|
+
userId : Blueprint.int().notNull(),
|
|
3066
|
+
number :Blueprint.varchar(50).notNull(),
|
|
3067
|
+
createdAt :Blueprint.timestamp().null(),
|
|
3068
|
+
updatedAt :Blueprint.timestamp().null()
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
type TSchemaPhone = T.SchemaStatic<typeof schemaPhone>
|
|
3072
|
+
|
|
3073
|
+
class Phone extends Model<TSchemaPhone> {
|
|
3074
|
+
constructor() {
|
|
3075
|
+
super()
|
|
3076
|
+
this.useSchema(schemaPhone)
|
|
3077
|
+
this.useBelongsTo({ model : User, name : 'user'})
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
export { Phone }
|
|
3082
|
+
export default Phone
|
|
3083
|
+
|
|
3084
|
+
// example basic
|
|
3085
|
+
type TS = T.Schema<typeof TSchemaUser>
|
|
3086
|
+
type TR = T.Relation<{ phone : Phone }>
|
|
3087
|
+
|
|
3088
|
+
type TSM = T.SchemaModel<User>
|
|
3089
|
+
type TRM = T.RelationModel<User>
|
|
3090
|
+
|
|
3091
|
+
type TColumn = T.Column<User>
|
|
3092
|
+
|
|
3093
|
+
type TResults = T.Results<User>
|
|
3094
|
+
type TPaginateResults = T.Results<User, { paginate : true }>
|
|
3095
|
+
|
|
3096
|
+
type TRepository = T.Repository<User>
|
|
3097
|
+
type TRepositoryTypeOf = T.RepositoryTypeOf<User>
|
|
3098
|
+
|
|
3099
|
+
+--------------------------------------------------------------------------+
|
|
3100
|
+
```
|
|
3101
|
+
|
|
3102
|
+
### Type Safety Select
|
|
3103
|
+
|
|
3104
|
+
```js
|
|
3105
|
+
import { User } from './User.ts'
|
|
3106
|
+
import { Phone } from './Phone.ts'
|
|
3107
|
+
|
|
3108
|
+
const user = await new User().select('id','username').findOne() ✅
|
|
3109
|
+
const user = await new User().select('idx','username').findOne() ❌
|
|
3110
|
+
|
|
3111
|
+
const user = await new User().except('id','username').findOne() ✅
|
|
3112
|
+
const user = await new User().except('idx','username').findOne() ❌
|
|
3113
|
+
|
|
3114
|
+
// T.SchemaStatic not allowed to set any new keys without in the schema to results
|
|
3115
|
+
user.withoutSchema = 1 ✅ // T.Schema<User>
|
|
3116
|
+
user.withoutSchema = 1 ❌ // T.SchemaStatic<User>
|
|
3117
|
+
// But can you make like this for cases
|
|
3118
|
+
const user = await new User().except('idx','username').findOne<{ withoutSchema : number }>()
|
|
3119
|
+
user.withoutSchema = 1 ✅
|
|
3120
|
+
```
|
|
3121
|
+
|
|
3122
|
+
### Type Safety OrderBy
|
|
3123
|
+
|
|
3124
|
+
```js
|
|
3125
|
+
import { User } from './User.ts'
|
|
3126
|
+
import { Phone } from './Phone.ts'
|
|
3127
|
+
|
|
3128
|
+
const users = await new User().orderBy('id','DESC').findMany() ✅
|
|
3129
|
+
const users = await new User().orderBy('idx','DESC').findMany() ❌
|
|
3130
|
+
|
|
3131
|
+
const users = await new User().latest('id').findMany() ✅
|
|
3132
|
+
const users = await new User().latest('idx').findMany() ❌
|
|
3133
|
+
|
|
3134
|
+
const users = await new User().oldest('id').findMany() ✅
|
|
3135
|
+
const users = await new User().oldest('idx').findMany() ❌
|
|
3136
|
+
|
|
3137
|
+
```
|
|
3138
|
+
|
|
3139
|
+
### Type Safety GroupBy
|
|
3140
|
+
|
|
3141
|
+
```js
|
|
3142
|
+
import { User } from './User.ts'
|
|
3143
|
+
import { Phone } from './Phone.ts'
|
|
3144
|
+
|
|
3145
|
+
const users = await new User().groupBy('id').findMany() ✅
|
|
3146
|
+
const users = await new User().groupBy('idx').findMany() ❌
|
|
3147
|
+
|
|
3148
|
+
```
|
|
3149
|
+
|
|
3150
|
+
### Type Safety Where
|
|
3151
|
+
|
|
3152
|
+
```js
|
|
3153
|
+
import { User } from './User.ts'
|
|
3154
|
+
import { Phone } from './Phone.ts'
|
|
3155
|
+
|
|
3156
|
+
const users = await new User().where('id',1).findMany() ✅
|
|
3157
|
+
const users = await new User().where('idxx',1).findMany() ❌
|
|
3158
|
+
|
|
3159
|
+
const users = await new User().where('id',1).orWhere('id',5).findMany() ✅
|
|
3160
|
+
const users = await new User().where('id',1).orWhere('idxx',5).findMany() ❌
|
|
3161
|
+
|
|
3162
|
+
const users = await new User().whereIn('id',[1]).findMany() ✅
|
|
3163
|
+
const users = await new User().whereIn('idx',[1]).findMany() ❌
|
|
3164
|
+
|
|
3165
|
+
const users = await new User().whereNull('id').findMany() ✅
|
|
3166
|
+
const users = await new User().whereNull('idx').findMany() ❌
|
|
3167
|
+
|
|
3168
|
+
const users = await new User().whereNotNull('id').findMany()
|
|
3169
|
+
const users = await new User().whereNotNull('idx').findMany()
|
|
3170
|
+
|
|
3171
|
+
const users = await new User().whereBetween('id',[1,2]).findMany() ✅
|
|
3172
|
+
const users = await new User().whereBetween('idx',[1,2]).findMany() ❌
|
|
3173
|
+
|
|
3174
|
+
const users = await new User()
|
|
3175
|
+
.whereSubQuery(
|
|
3176
|
+
'id',
|
|
3177
|
+
new User().select('id').toString()
|
|
3178
|
+
).findMany() ✅
|
|
3179
|
+
|
|
3180
|
+
const users = await new User()
|
|
3181
|
+
.whereSubQuery(
|
|
3182
|
+
'idx',
|
|
3183
|
+
new User().select('id').toString()
|
|
3184
|
+
).findMany() ❌
|
|
3185
|
+
|
|
3186
|
+
```
|
|
3187
|
+
|
|
3188
|
+
### Type Safety Insert
|
|
3189
|
+
|
|
3190
|
+
```js
|
|
3191
|
+
import { User } from './User.ts'
|
|
3192
|
+
import { Phone } from './Phone.ts'
|
|
3193
|
+
|
|
3194
|
+
const users = await new User().create({ id : 10 }).save() ✅
|
|
3195
|
+
|
|
3196
|
+
const users = await new User().create({ id : "10" }).save() ❌
|
|
3197
|
+
|
|
3198
|
+
const users = await new User().create({ idx : 10 }).save() ❌
|
|
3199
|
+
|
|
3200
|
+
```
|
|
3201
|
+
|
|
3202
|
+
### Type Safety Update
|
|
3203
|
+
|
|
3204
|
+
```js
|
|
3205
|
+
import { User } from './User.ts'
|
|
3206
|
+
import { Phone } from './Phone.ts'
|
|
3207
|
+
|
|
3208
|
+
const users = await new User().update({ id : 10 }).where('id',1).save() ✅
|
|
3209
|
+
const users = await new User().update({ id : 10 }).where('idx',1).save() ❌
|
|
3210
|
+
const users = await new User().update({ id : "10" }).where('id',1).save() ❌
|
|
3211
|
+
const users = await new User().update({ idx : 10 }).where('idx',1).save() ❌
|
|
3212
|
+
|
|
3213
|
+
```
|
|
3214
|
+
|
|
3215
|
+
### Type Safety Delete
|
|
3216
|
+
|
|
3217
|
+
```js
|
|
3218
|
+
import { User } from './User.ts'
|
|
3219
|
+
import { Phone } from './Phone.ts'
|
|
3220
|
+
|
|
3221
|
+
const users = await new User().where('id',1).delete() ✅
|
|
3222
|
+
const users = await new User().where('idx',1).delete() ❌
|
|
3223
|
+
|
|
3224
|
+
```
|
|
3225
|
+
|
|
3226
|
+
### Type Safety Relationships
|
|
3227
|
+
|
|
3228
|
+
```js
|
|
3229
|
+
import { type T } from 'tspace-mysql'
|
|
3230
|
+
import { User } from './User.ts'
|
|
3231
|
+
import { Phone } from './Phone.ts'
|
|
3232
|
+
// Case #1 : Relationship with 2 relations 'phone' and 'phones'
|
|
3233
|
+
const users = await new User()
|
|
3234
|
+
.relations('phone','phones')
|
|
3235
|
+
.findMany()
|
|
3236
|
+
|
|
3237
|
+
for(const user of users) {
|
|
3238
|
+
user.phone ❌
|
|
3239
|
+
user.phones ❌
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
// You can also specify the type for the results.
|
|
3243
|
+
// bad 👎👎👎
|
|
3244
|
+
const users = await new User()
|
|
3245
|
+
.relations('phone','phones')
|
|
3246
|
+
.findMany<{ phone : Record<string,any> , phones : any[]}>()
|
|
3247
|
+
|
|
3248
|
+
for(const user of users) {
|
|
3249
|
+
user.phone ✅
|
|
3250
|
+
user.phones ✅
|
|
3251
|
+
user.phone.id ✅
|
|
3252
|
+
user.phone.idx ✅💩💩💩
|
|
3253
|
+
user.phones.map(phone => phone.id) ✅
|
|
3254
|
+
user.phones.map(phone => phone.idx) ✅💩💩💩
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
// good 👍👍👍
|
|
3258
|
+
const users = await new User()
|
|
3259
|
+
.relations('phone','phones')
|
|
3260
|
+
.findMany<{ phone : T.SchemaModel<Phone> , phones : T.SchemaModel<Phone>[] }>()
|
|
3261
|
+
|
|
3262
|
+
for(const user of users) {
|
|
3263
|
+
user.phone ✅
|
|
3264
|
+
user.phones ✅
|
|
3265
|
+
user.phone?.id ✅
|
|
3266
|
+
user.phone?.idx ❌
|
|
3267
|
+
user.phones.map(phone => phone?.id) ✅
|
|
3268
|
+
user.phones.map(phone => phone?.idx) ❌
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
+--------------------------------------------------------------------------+
|
|
3272
|
+
|
|
3273
|
+
// Case #2 : There is a relationship between two entities, 'phone' and 'phones', both of which are related to the 'user' entity through nested relations
|
|
3274
|
+
const users = await new User()
|
|
3275
|
+
.relations('phone','phones')
|
|
3276
|
+
.relationQuery('phone' , (query : Phone) => query.relations('user'))
|
|
3277
|
+
.relationQuery('phones' , (query : Phone) => query.relations('user'))
|
|
3278
|
+
.findMany<{ phone : T.SchemaModel<Phone> , phones : T.SchemaModel<Phone>[] }>()
|
|
3279
|
+
|
|
3280
|
+
for(const user of users) {
|
|
3281
|
+
user.phone.user ❌
|
|
3282
|
+
user.phones.map(phone =>phone.user) ❌
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
// You can also specify the type for the results.
|
|
3286
|
+
// bad 👎👎👎
|
|
3287
|
+
const users = await new User()
|
|
3288
|
+
.relations('phone','phones')
|
|
3289
|
+
.relationQuery('phone' , (query : Phone) => query.relations('user'))
|
|
3290
|
+
.relationQuery('phones' , (query : Phone) => query.relations('user'))
|
|
3291
|
+
.findMany<{ phone : Record<string,any> , phones : Record<string,any>[] }>()
|
|
3292
|
+
|
|
3293
|
+
for(const user of users) {
|
|
3294
|
+
user.phone.user ✅💩💩💩
|
|
3295
|
+
user.phones.map(phone =>phone.user) ✅💩💩💩
|
|
3296
|
+
user.phone.user.idx ✅💩💩💩
|
|
3297
|
+
user.phones.map(phone =>phone.user.idx) ✅💩💩💩
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
// good 👍👍👍
|
|
3301
|
+
const users = await new User()
|
|
3302
|
+
.relations('phone','phones')
|
|
3303
|
+
.relationQuery('phone' , (query : Phone) => query.relations('user'))
|
|
3304
|
+
.relationQuery('phones' , (query : Phone) => query.relations('user'))
|
|
3305
|
+
.findMany<{
|
|
3306
|
+
phone : Partial<T.SchemaModel<Phone>> & { user : T.SchemaModel<User>};
|
|
3307
|
+
phones : (Partial<T.SchemaModel<Phone>> & { user : T.SchemaModel<User>})[];
|
|
3308
|
+
}>()
|
|
3309
|
+
|
|
3310
|
+
for(const user of users) {
|
|
3311
|
+
user.phone.user ✅
|
|
3312
|
+
user.phone.user.id ✅
|
|
3313
|
+
user.phone.userx ❌
|
|
3314
|
+
user.phone.user.idx ❌
|
|
3315
|
+
user.phones.map(phone =>phone.user.id) ✅
|
|
3316
|
+
user.phones.map(phone =>phone.user.idx) ❌
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
+--------------------------------------------------------------------------+
|
|
3320
|
+
// If you don't want to set types for every returning method such as 'findOne', 'findMany', and so on...
|
|
3321
|
+
|
|
3322
|
+
import { Model , Blueprint , type T } from 'tspace-mysql'
|
|
3323
|
+
import { Phone } from '../Phone'
|
|
3324
|
+
|
|
3325
|
+
const schemaUser = {
|
|
3326
|
+
id :Blueprint.int().notNull().primary().autoIncrement(),
|
|
3327
|
+
uuid :Blueprint.varchar(50).null(),
|
|
3328
|
+
email :Blueprint.varchar(50).null(),
|
|
3329
|
+
name :Blueprint.varchar(255).null(),
|
|
3330
|
+
username :Blueprint.varchar(255).null(),
|
|
3331
|
+
password :Blueprint.varchar(255).null(),
|
|
3332
|
+
createdAt :Blueprint.timestamp().null(),
|
|
3333
|
+
updatedAt :Blueprint.timestamp().null()
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
type TSchemaUser = T.SchemaStatic<typeof schemaUser>
|
|
3337
|
+
|
|
3338
|
+
type TRelationUser = T.Relation<{
|
|
3339
|
+
phones : Phone[]
|
|
3340
|
+
phone : Phone
|
|
3341
|
+
}>
|
|
3342
|
+
|
|
3343
|
+
// Add this '<TSchemaUser, RelationUserType>' to activate the type for the Model.
|
|
3344
|
+
class User extends Model< TSchemaUser, TRelationUser > {
|
|
3345
|
+
constructor() {
|
|
3346
|
+
super()
|
|
3347
|
+
this.useSchema(schemaUser)
|
|
3348
|
+
this.hasOne({ model : Phone, name : 'phonex' }) ❌
|
|
3349
|
+
this.hasMany({ model : Phone, name : 'phonesx' }) ❌
|
|
3350
|
+
this.hasOne({ model : Phone, name : 'phone' }) ✅
|
|
3351
|
+
this.hasMany({ model : Phone, name : 'phones' }) ✅
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
export { User }
|
|
3356
|
+
|
|
3357
|
+
+--------------------------------------------------------------------------+
|
|
3358
|
+
|
|
3359
|
+
// in file Phone.ts
|
|
3360
|
+
import { Model , Blueprint , type T } from 'tspace-mysql'
|
|
3361
|
+
import { User } from './User.ts'
|
|
3362
|
+
|
|
3363
|
+
const schemaPhone = {
|
|
3364
|
+
id :Blueprint.int().notNull().primary().autoIncrement(),
|
|
3365
|
+
uuid :Blueprint.varchar(50).null(),
|
|
3366
|
+
userId :Blueprint.int().notNull(),
|
|
3367
|
+
number :Blueprint.varchar(50).notNull(),
|
|
3368
|
+
createdAt :Blueprint.timestamp().null(),
|
|
3369
|
+
updatedAt :Blueprint.timestamp().null()
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
type TSchemaPhone = T.Schema<typeof schemaPhone>
|
|
3373
|
+
|
|
3374
|
+
type TRelationPhone = T.Relation<{
|
|
3375
|
+
user : User[]
|
|
3376
|
+
}>
|
|
3377
|
+
|
|
3378
|
+
class Phone extends Model<TSchemaPhone,TRelationPhone> {
|
|
3379
|
+
constructor() {
|
|
3380
|
+
super()
|
|
3381
|
+
this.useSchema(schemaPhone)
|
|
3382
|
+
this.useBelongsTo({ model : User, name : 'userx'}) ❌
|
|
3383
|
+
this.useBelongsTo({ model : User, name : 'user'}) ✅
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
export { Phone }
|
|
3388
|
+
|
|
3389
|
+
+--------------------------------------------------------------------------+
|
|
3390
|
+
|
|
3391
|
+
const users = await new User()
|
|
3392
|
+
.relations('phonex','phonesx') ❌
|
|
3393
|
+
.relationQuery('phonex' ❌ , (query : Phone) => query.relations('user')) ✅
|
|
3394
|
+
.relationQuery('phonesx' ❌ , (query : Phone) => query.relations('user')) ✅
|
|
3395
|
+
.findMany()
|
|
3396
|
+
|
|
3397
|
+
const users = await new User()
|
|
3398
|
+
.relations('phone','phones') ✅
|
|
3399
|
+
.relationQuery('phonex' ❌ , (query : Phone) => query.relations('user')) ✅
|
|
3400
|
+
.relationQuery('phonesx' ❌ , (query : Phone) => query.relations('user')) ✅
|
|
3401
|
+
.findMany()
|
|
3402
|
+
|
|
3403
|
+
const users = await new User()
|
|
3404
|
+
.relations('phone','phones')
|
|
3405
|
+
.relationQuery('phone' , (query : Phone) => query.relations('userx')) ❌
|
|
3406
|
+
.relationQuery('phones' , (query : Phone) => query.relations('userx')) ❌
|
|
3407
|
+
.findMany()
|
|
3408
|
+
|
|
3409
|
+
const users = await new User()
|
|
3410
|
+
.relations('phone','phones') ✅
|
|
3411
|
+
.relationQuery('phone' ✅ , (query : Phone) => query.relations('user')) ✅
|
|
3412
|
+
.relationQuery('phones'✅ , (query : Phone) => query.relations('user')) ✅
|
|
3413
|
+
.findMany()
|
|
3414
|
+
|
|
3415
|
+
for(const user of users) {
|
|
3416
|
+
user.phone.user ❌
|
|
3417
|
+
user.phone?.user ✅
|
|
3418
|
+
user.phone?.user.id ✅
|
|
3419
|
+
user.phone?.userx ❌
|
|
3420
|
+
user.phone?.user.idx ❌
|
|
3421
|
+
user.phones.map(phone =>phone?.user.id) ❌
|
|
3422
|
+
user.phones?.map(phone =>phone?.user.id) ✅
|
|
3423
|
+
user.phones?.map(phone =>phone?.user.idx) ❌
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
```
|
|
3427
|
+
|
|
3428
|
+
## Type Safety Results
|
|
3429
|
+
```js
|
|
3430
|
+
import { type T } from 'tspace-mysql'
|
|
3431
|
+
|
|
3432
|
+
const fError = async () : Promise<T.Results<User>[]> => {
|
|
3433
|
+
|
|
3434
|
+
const users = [{
|
|
3435
|
+
id : 1,
|
|
3436
|
+
uuid: "12d4f08a-a20d-4f41-abac-81391e135d60",
|
|
3437
|
+
email: "tspace@example.com"
|
|
3438
|
+
}]
|
|
3439
|
+
|
|
3440
|
+
return users // ❌
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
const fCorrect = async () : Promise<T.Results<User>[]> => {
|
|
3444
|
+
|
|
3445
|
+
const users = await new User().findMany()
|
|
3446
|
+
|
|
3447
|
+
return users // ✅
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
```
|
|
3451
|
+
|
|
3452
|
+
## Metadata
|
|
3453
|
+
Get the metadata of a Model works only when a schema is added to the Model.
|
|
3454
|
+
|
|
3455
|
+
```js
|
|
3456
|
+
import { Meta, Model , Blueprint , type T } from 'tspace-mysql';
|
|
3457
|
+
|
|
3458
|
+
const schema = {
|
|
3459
|
+
id : Blueprint.int().notNull().primary().autoIncrement(),
|
|
3460
|
+
uuid : Blueprint.varchar(50).null(),
|
|
3461
|
+
email : Blueprint.varchar(255).notNull().index('users.email@index'),
|
|
3462
|
+
name : Blueprint.varchar(255).null(),
|
|
3463
|
+
username : Blueprint.varchar(255).notNull(),
|
|
3464
|
+
password : Blueprint.varchar(255).notNull(),
|
|
3465
|
+
status : Blueprint.tinyInt().notNull().default(0),
|
|
3466
|
+
role : Blueprint.enum('admin','user').default('user'),
|
|
3467
|
+
createdAt : Blueprint.timestamp().null(),
|
|
3468
|
+
updatedAt : Blueprint.timestamp().null()
|
|
3469
|
+
}
|
|
3470
|
+
|
|
3471
|
+
type TS = T.Schema<typeof schema>
|
|
3472
|
+
|
|
3473
|
+
class User extends Model<TS> {
|
|
3474
|
+
constructor() {
|
|
3475
|
+
super()
|
|
3476
|
+
this.useSchema(schema)
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
const meta = Meta(User)
|
|
3481
|
+
|
|
3482
|
+
const table = meta.table() // 'users'
|
|
3483
|
+
const column = meta.column('id') // 'id'
|
|
3484
|
+
const columnRef = meta.columnReference('id') // `users`.`id`
|
|
3485
|
+
const columnTypeOf = meta.columnTypeOf('id') // number
|
|
3486
|
+
const columnType = meta.columnType('id') // Int
|
|
3487
|
+
const columns = meta.columns() // ['id','uuid',...'updatedAt']
|
|
3488
|
+
const hasColumn = meta.hasColumn('idx') // false
|
|
3489
|
+
const primaryKey = meta.primaryKey() // 'id'
|
|
3490
|
+
const indexes = meta.indexes() // ['users.email@index']
|
|
3491
|
+
const nullable = meta.nullable() // ['uuid','name','createdAt','updatedAt']
|
|
3492
|
+
const defaults = meta.defaults() // { id : null, uuid : null, ..., status : 0, role: 'user' ,..updatedAt : null }
|
|
3493
|
+
const enums = meta.enums('role') // [ 'admin', 'user' ]
|
|
3494
|
+
const enumsObj = meta.enum('role') // { admin: 'admin', user: 'user' }
|
|
3495
|
+
```
|
|
3496
|
+
|
|
3497
|
+
## Audit
|
|
3498
|
+
Keeps a complete history of database changes, tracking who made changes and what was changed.
|
|
3499
|
+
|
|
3500
|
+
```js
|
|
3501
|
+
await new User()
|
|
3502
|
+
// support actions SELECT, INSERT, UPDATE, and DELETE.
|
|
3503
|
+
// so you have a complete history of data changes and queries.
|
|
3504
|
+
.audit(99 , { name : 'userNumber-99' })
|
|
3505
|
+
.create({
|
|
3506
|
+
username : 'hi audit',
|
|
3507
|
+
email : 'tspace-mysql@gmail.com',
|
|
3508
|
+
// ...
|
|
3509
|
+
})
|
|
3510
|
+
.save()
|
|
3511
|
+
```
|
|
3512
|
+
|
|
3513
|
+
## Repository
|
|
3514
|
+
```js
|
|
3515
|
+
Repository is a mechanism that encapsulates all database operations related to a specific model.
|
|
3516
|
+
It provides methods for querying, inserting, updating, and deleting records in the database associated with the model.
|
|
3517
|
+
|
|
3518
|
+
** The Repository check always type Type Safety if model is used the type of schema
|
|
3519
|
+
|
|
3520
|
+
```
|
|
3521
|
+
### Repository Select Statements
|
|
3522
|
+
```js
|
|
3523
|
+
import { Repository, OP , type T } from 'tspace-mysql'
|
|
3524
|
+
import { User } from '../Models/User'
|
|
3525
|
+
|
|
3526
|
+
// Create repository instance for User entity
|
|
3527
|
+
const userRepository = Repository(User)
|
|
3528
|
+
// Fetch a single user with flexible query options
|
|
3529
|
+
const user = await userRepository.findOne({
|
|
3530
|
+
/**
|
|
3531
|
+
* 🎯 SELECT
|
|
3532
|
+
* Specify which columns should be returned.
|
|
3533
|
+
* Supports nested selection for relations.
|
|
3534
|
+
*/
|
|
3535
|
+
select: {
|
|
3536
|
+
id: true,
|
|
3537
|
+
name: true,
|
|
3538
|
+
username: true,
|
|
3539
|
+
|
|
3540
|
+
// Nested relation field selection
|
|
3541
|
+
phone: {
|
|
3542
|
+
id: true,
|
|
3543
|
+
name: true,
|
|
3544
|
+
user_id: true,
|
|
3545
|
+
}
|
|
3546
|
+
},
|
|
3547
|
+
|
|
3548
|
+
/**
|
|
3549
|
+
* 🚫 EXCEPT
|
|
3550
|
+
* Exclude specific columns from the result.
|
|
3551
|
+
* Useful for hiding audit fields or sensitive data.
|
|
3552
|
+
*/
|
|
3553
|
+
// except: {
|
|
3554
|
+
// deleted_at: true,
|
|
3555
|
+
// created_at: true,
|
|
3556
|
+
// updated_at: true,
|
|
3557
|
+
// },
|
|
3558
|
+
|
|
3559
|
+
/**
|
|
3560
|
+
* 🧮 SELECT RAW
|
|
3561
|
+
* Add computed/raw SQL fields to the result.
|
|
3562
|
+
* Automatically extends the return type.
|
|
3563
|
+
*/
|
|
3564
|
+
// selectRaw: {
|
|
3565
|
+
// fullName: DB.raw('CONCAT(?,"@",?)', ['name', 'id'])
|
|
3566
|
+
// },
|
|
3567
|
+
|
|
3568
|
+
/**
|
|
3569
|
+
* 🧩 EXTEND
|
|
3570
|
+
* Manually extend the result type with additional fields.
|
|
3571
|
+
* Supports nested objects and arrays.
|
|
3572
|
+
*/
|
|
3573
|
+
// extend: {
|
|
3574
|
+
// myNumber: Number,
|
|
3575
|
+
// myProfile: {
|
|
3576
|
+
// id: Number,
|
|
3577
|
+
// name: String
|
|
3578
|
+
// },
|
|
3579
|
+
// myOtherData: [
|
|
3580
|
+
// {
|
|
3581
|
+
// id: Number,
|
|
3582
|
+
// name: String
|
|
3583
|
+
// }
|
|
3584
|
+
// ]
|
|
3585
|
+
// },
|
|
3586
|
+
|
|
3587
|
+
/**
|
|
3588
|
+
* 🔎 WHERE
|
|
3589
|
+
* Define filtering conditions.
|
|
3590
|
+
*/
|
|
3591
|
+
where: {
|
|
3592
|
+
id: 1
|
|
3593
|
+
},
|
|
3594
|
+
|
|
3595
|
+
/**
|
|
3596
|
+
* ⚡ WHEN
|
|
3597
|
+
* Conditionally modify the query.
|
|
3598
|
+
* Only applied if `condition` is true.
|
|
3599
|
+
*/
|
|
3600
|
+
when: {
|
|
3601
|
+
condition: true,
|
|
3602
|
+
query: () => ({
|
|
3603
|
+
relations: {
|
|
3604
|
+
phone: true
|
|
3605
|
+
|
|
3606
|
+
/**
|
|
3607
|
+
* You can also customize relation query:
|
|
3608
|
+
*
|
|
3609
|
+
* phone: {
|
|
3610
|
+
* where: { id: 41 },
|
|
3611
|
+
* select: {
|
|
3612
|
+
* id: true,
|
|
3613
|
+
* user_id: true
|
|
3614
|
+
* }
|
|
3615
|
+
* }
|
|
3616
|
+
*/
|
|
3617
|
+
}
|
|
3618
|
+
})
|
|
3619
|
+
},
|
|
3620
|
+
|
|
3621
|
+
/**
|
|
3622
|
+
* 🛠 USING
|
|
3623
|
+
* Direct access to the underlying query builder.
|
|
3624
|
+
* Allows advanced customization using model methods.
|
|
3625
|
+
*/
|
|
3626
|
+
using: (query) => {
|
|
3627
|
+
return query
|
|
3628
|
+
|
|
3629
|
+
// Examples:
|
|
3630
|
+
// query.where('id', 1)
|
|
3631
|
+
// query.customMethodWhereUser(1)
|
|
3632
|
+
}
|
|
3633
|
+
})
|
|
3634
|
+
|
|
3635
|
+
|
|
3636
|
+
const users = await userRepository.findMany({
|
|
3637
|
+
select : {
|
|
3638
|
+
id : true,
|
|
3639
|
+
name : true,
|
|
3640
|
+
username : true,
|
|
3641
|
+
},
|
|
3642
|
+
limit : 3,
|
|
3643
|
+
orderBy : {
|
|
3644
|
+
id : 'ASC',
|
|
3645
|
+
name : 'DESC'
|
|
3646
|
+
},
|
|
3647
|
+
groupBy : {
|
|
3648
|
+
id : true
|
|
3649
|
+
},
|
|
3650
|
+
where : {
|
|
3651
|
+
id: OP.in([1,2,3])
|
|
3652
|
+
}
|
|
3653
|
+
})
|
|
3654
|
+
|
|
3655
|
+
const userPaginate = await userRepository.pagination({
|
|
3656
|
+
select : {
|
|
3657
|
+
id : true,
|
|
3658
|
+
name : true,
|
|
3659
|
+
username : true,
|
|
3660
|
+
},
|
|
3661
|
+
page : 1,
|
|
3662
|
+
limit : 3,
|
|
3663
|
+
where : {
|
|
3664
|
+
id: OP.in([1,2,3])
|
|
3665
|
+
}
|
|
3666
|
+
})
|
|
3667
|
+
|
|
3668
|
+
const findFullName = await userRepository.findOne({
|
|
3669
|
+
select : {
|
|
3670
|
+
name : true,
|
|
3671
|
+
[`${DB.raw('CONCAT(firstName," ",lastName) as fullName')}`]: true
|
|
3672
|
+
},
|
|
3673
|
+
whereRaw : [
|
|
3674
|
+
`CONCAT(firstName," ",lastName) LIKE '%${search}%'`
|
|
3675
|
+
]
|
|
3676
|
+
})
|
|
3677
|
+
```
|
|
3678
|
+
### Repository Insert Statements
|
|
3679
|
+
```js
|
|
3680
|
+
|
|
3681
|
+
const userRepository = Repository(User)
|
|
3682
|
+
|
|
3683
|
+
const created = await userRepository.create({
|
|
3684
|
+
data : {
|
|
3685
|
+
name : "repository-name",
|
|
3686
|
+
// ....
|
|
3687
|
+
}
|
|
3688
|
+
})
|
|
3689
|
+
|
|
3690
|
+
const createdMultiple = await u.createMultiple({
|
|
3691
|
+
data : [
|
|
3692
|
+
{
|
|
3693
|
+
name: "tspace4",
|
|
3694
|
+
// ....
|
|
3695
|
+
},
|
|
3696
|
+
{
|
|
3697
|
+
name: "tspace5",
|
|
3698
|
+
// ....
|
|
3699
|
+
},
|
|
3700
|
+
{
|
|
3701
|
+
name: "tspace6",
|
|
3702
|
+
// ....
|
|
3703
|
+
}
|
|
3704
|
+
// ....
|
|
3705
|
+
]
|
|
3706
|
+
})
|
|
3707
|
+
|
|
3708
|
+
const createdNotExists = await userRepository.createNotExists({
|
|
3709
|
+
data : {
|
|
3710
|
+
name : "repository-name",
|
|
3711
|
+
// ....
|
|
3712
|
+
},
|
|
3713
|
+
where : {
|
|
3714
|
+
id : 1
|
|
3715
|
+
}
|
|
3716
|
+
})
|
|
3717
|
+
|
|
3718
|
+
const createdOrSelected = await userRepository.createOrSelect({
|
|
3719
|
+
data : {
|
|
3720
|
+
name : "repository-name",
|
|
3721
|
+
// ....
|
|
3722
|
+
},
|
|
3723
|
+
where : {
|
|
3724
|
+
id : 1
|
|
3725
|
+
}
|
|
3726
|
+
})
|
|
3727
|
+
|
|
3728
|
+
|
|
3729
|
+
```
|
|
3730
|
+
### Repository Update Statements
|
|
3731
|
+
```js
|
|
3732
|
+
|
|
3733
|
+
const userRepository = Repository(User)
|
|
3734
|
+
|
|
3735
|
+
const updated = await userRepository.update({
|
|
3736
|
+
data : {
|
|
3737
|
+
name : "repository-name",
|
|
3738
|
+
// ....
|
|
3739
|
+
},
|
|
3740
|
+
where : {
|
|
3741
|
+
id : 1
|
|
3742
|
+
}
|
|
3743
|
+
})
|
|
3744
|
+
|
|
3745
|
+
```
|
|
3746
|
+
### Repository Delete Statements
|
|
3747
|
+
```js
|
|
3748
|
+
|
|
3749
|
+
const userRepository = Repository(User)
|
|
3750
|
+
|
|
3751
|
+
const deleted = await userRepository.delete({
|
|
3752
|
+
where : {
|
|
3753
|
+
id : 1
|
|
3754
|
+
}
|
|
3755
|
+
})
|
|
3756
|
+
|
|
3757
|
+
```
|
|
3758
|
+
|
|
3759
|
+
### Repository Transactions
|
|
3760
|
+
|
|
3761
|
+
```js
|
|
3762
|
+
import { DB , Repository } from 'tspace-mysql'
|
|
3763
|
+
import { User } from '../Models/User'
|
|
3764
|
+
const userRepository = Repository(User)
|
|
3765
|
+
|
|
3766
|
+
const transaction = await DB.beginTransaction()
|
|
3767
|
+
|
|
3768
|
+
try {
|
|
3769
|
+
await transaction.startTransaction()
|
|
3770
|
+
|
|
3771
|
+
const created = await userRepository.create({
|
|
3772
|
+
data : {
|
|
3773
|
+
name : "repository-name",
|
|
3774
|
+
// ....
|
|
3775
|
+
},
|
|
3776
|
+
transaction // add this for the transaction
|
|
3777
|
+
})
|
|
3778
|
+
|
|
3779
|
+
const updated = await userRepository.update({
|
|
3780
|
+
data : {
|
|
3781
|
+
name : "repository-name",
|
|
3782
|
+
// ....
|
|
3783
|
+
},
|
|
3784
|
+
where : {
|
|
3785
|
+
id : created.id
|
|
3786
|
+
},
|
|
3787
|
+
transaction
|
|
3788
|
+
})
|
|
3789
|
+
|
|
3790
|
+
// after your use commit if use same transction for actions this transction will auto commit
|
|
3791
|
+
await transaction.commit()
|
|
3792
|
+
|
|
3793
|
+
// ensure the nothing with transction just use end of transction
|
|
3794
|
+
await transaction.end();
|
|
3795
|
+
|
|
3796
|
+
} catch (err) {
|
|
3797
|
+
|
|
3798
|
+
await transaction.rollback()
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
```
|
|
3802
|
+
|
|
3803
|
+
### Repository Relations
|
|
3804
|
+
```js
|
|
3805
|
+
import { Repository , OP } from 'tspace-mysql'
|
|
3806
|
+
import { User } from '../Models/User'
|
|
3807
|
+
import { Phone } from '../Models/Phone'
|
|
3808
|
+
|
|
3809
|
+
const userRepository = Repository(User)
|
|
3810
|
+
|
|
3811
|
+
const userHasPhones = await userRepository.findOne({
|
|
3812
|
+
select : {
|
|
3813
|
+
id : true,
|
|
3814
|
+
name : true,
|
|
3815
|
+
username : true,
|
|
3816
|
+
phone : {
|
|
3817
|
+
id : true,
|
|
3818
|
+
user_id : true,
|
|
3819
|
+
name: true
|
|
3820
|
+
}
|
|
3821
|
+
},
|
|
3822
|
+
where : {
|
|
3823
|
+
id: 1
|
|
3824
|
+
},
|
|
3825
|
+
relations: {
|
|
3826
|
+
phone: {
|
|
3827
|
+
user : true
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
});
|
|
3831
|
+
|
|
3832
|
+
const phoneRepository = Repository(Phone)
|
|
3833
|
+
|
|
3834
|
+
const phoneBelongUser = await phoneRepository.findOne({
|
|
3835
|
+
select : '*',
|
|
3836
|
+
where : {
|
|
3837
|
+
id: 1
|
|
3838
|
+
},
|
|
3839
|
+
relations : {
|
|
3840
|
+
user : true
|
|
3841
|
+
}
|
|
3842
|
+
})
|
|
3843
|
+
|
|
3844
|
+
```
|
|
3845
|
+
|
|
3846
|
+
## Queue
|
|
3847
|
+
A lightweight, high-performance job queue built for ORM-based systems,
|
|
3848
|
+
designed to run on top of database layers with support for concurrency,
|
|
3849
|
+
retries, priorities, and job inspection.
|
|
3850
|
+
|
|
3851
|
+
- Concurrency Each worker can process multiple jobs at the same time.
|
|
3852
|
+
|
|
3853
|
+
- Priority Higher priority jobs are executed first.
|
|
3854
|
+
|
|
3855
|
+
- Retry Failed jobs are automatically retried up to maxAttempts.
|
|
3856
|
+
|
|
3857
|
+
- Delay Jobs can be scheduled for future execution using delayMs.
|
|
3858
|
+
|
|
3859
|
+
- Idle / Wake Workers automatically go idle when no jobs are available, and wake up instantly when new jobs arrive.
|
|
3860
|
+
|
|
3861
|
+
```js
|
|
3862
|
+
import { Queue, Job } from 'tspace-mysql';
|
|
3863
|
+
|
|
3864
|
+
const fakeSendEmail = async (job: Job) => {
|
|
3865
|
+
if(Math.random() < 0.5) throw new Error(`Failed job ${job.id}`)
|
|
3866
|
+
await new Promise<void>((ok) => setTimeout(ok, 2000));
|
|
3867
|
+
return `Send email Completed job ${job.id}`;
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
// start the Queue
|
|
3871
|
+
await Queue.start({
|
|
3872
|
+
inspect : true,
|
|
3873
|
+
flush : false, // flush = true -> remove all jobs
|
|
3874
|
+
hostname: 'pod1'
|
|
3875
|
+
});
|
|
3876
|
+
|
|
3877
|
+
const worker = 20;
|
|
3878
|
+
|
|
3879
|
+
// register process send email 20
|
|
3880
|
+
for(let i = 1; i <= worker; i++) {
|
|
3881
|
+
|
|
3882
|
+
Queue.process(`send-email-(${i})`, async(job) => {
|
|
3883
|
+
return await fakeSendEmail(job)
|
|
3884
|
+
} , { concurrency : 10 }) // 1 process / 10 concurrency
|
|
3885
|
+
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
// add jobs 10_000 records
|
|
3889
|
+
for(let j = 1; j <= worker * 500; j++) {
|
|
3890
|
+
|
|
3891
|
+
const i = Math.floor((Math.random() * worker) + 1);
|
|
3892
|
+
|
|
3893
|
+
Queue.add(`send-email-(${i})`, {
|
|
3894
|
+
email: `John-${i}@gmail.com`,
|
|
3895
|
+
name: `John-${i}`
|
|
3896
|
+
}, {
|
|
3897
|
+
delayMs : 1000 * Math.random() * 10,
|
|
3898
|
+
priority : i % 2 ? 9999 + Math.floor((Math.random() * 9999) + 1) : 0,
|
|
3899
|
+
maxAttempts : 3,
|
|
3900
|
+
metadata : {
|
|
3901
|
+
userId : j,
|
|
3902
|
+
name : `John-${i}`
|
|
3903
|
+
}
|
|
3904
|
+
})
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
// for view stats Jobs
|
|
3908
|
+
// await Queue.getJobOverallStats()
|
|
3909
|
+
// await Queue.getJobStats()
|
|
3910
|
+
|
|
3911
|
+
// if you want to end the Queue
|
|
3912
|
+
// await Queue.end()
|
|
3913
|
+
|
|
3914
|
+
```
|
|
3915
|
+
|
|
3916
|
+
## View
|
|
3917
|
+
|
|
3918
|
+
Your database schema can also use views. These views are represented by classes that behave similarly to models,
|
|
3919
|
+
but they are based on stored SQL queries instead of actual tables.
|
|
3920
|
+
Let's look at a basic view class example:
|
|
3921
|
+
```js
|
|
3922
|
+
|
|
3923
|
+
import { type T, Blueprint, Model , View , Meta } from 'tspace-mysql'
|
|
3924
|
+
|
|
3925
|
+
const schemaUser = {
|
|
3926
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
3927
|
+
uuid: Blueprint.varchar(50).null().index(),
|
|
3928
|
+
name: Blueprint.varchar(191).notNull(),
|
|
3929
|
+
email: Blueprint.varchar(191).notNull()
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
type TUser = T.Schema<typeof schemaUser>
|
|
3933
|
+
|
|
3934
|
+
class User extends Model<TUser> {
|
|
3935
|
+
protected boot(): void {
|
|
3936
|
+
this.useSchema(schemaUser)
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
const schemaPost = {
|
|
3941
|
+
id: Blueprint.int().notNull().primary().autoIncrement(),
|
|
3942
|
+
uuid: Blueprint.varchar(50).null().index(),
|
|
3943
|
+
user_id :Blueprint.int().notnull(),
|
|
3944
|
+
title: Blueprint.varchar(191).notNull(),
|
|
3945
|
+
content: Blueprint.varchar(191).notNull()
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
type TPost = T.Schema<typeof schemaPost>
|
|
3949
|
+
|
|
3950
|
+
class Post extends Model<TPost> {
|
|
3951
|
+
protected boot(): void {
|
|
3952
|
+
this.useSchema(schemaPost)
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3956
|
+
const schemaUserPostCountView = {
|
|
3957
|
+
id :Blueprint.int().notNull().primary().autoIncrement(),
|
|
3958
|
+
user_id :Blueprint.int().notnull(),
|
|
3959
|
+
name :Blueprint.varchar(255).null(),
|
|
3960
|
+
post_count : Blueprint.int().notnull()
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
type TSUserPostCountView = T.Schema<typeof schemaUserPostCountView>
|
|
3964
|
+
type TRUserPostCountView = T.Relation<{
|
|
3965
|
+
user: User
|
|
3966
|
+
}>
|
|
3967
|
+
|
|
3968
|
+
class UserPostCountView extends View<TSUserPostCountView,TRUserPostCountView> {
|
|
3969
|
+
|
|
3970
|
+
protected boot(): void {
|
|
3971
|
+
this.useSchema(schemaUserPostCountView)
|
|
3972
|
+
const metaUser = Meta(User)
|
|
3973
|
+
const metaPost = Meta(Post)
|
|
3974
|
+
|
|
3975
|
+
this.createView({
|
|
3976
|
+
synchronize: true,
|
|
3977
|
+
expression : new User()
|
|
3978
|
+
.selectRaw(`ROW_NUMBER() OVER (ORDER BY ${metaUser.columnRef('id')}) AS id`)
|
|
3979
|
+
.selectRaw(`${metaUser.columnRef('id')} AS user_id`)
|
|
3980
|
+
.selectRaw(metaUser.columnRef('name'))
|
|
3981
|
+
.select(metaUser.columnRef('email'))
|
|
3982
|
+
.selectRaw(`COUNT(${metaPost.columnRef('id')}) AS post_count`)
|
|
3983
|
+
.leftJoin(metaUser.columnRef('id'),metaPost.columnRef('user_id'))
|
|
3984
|
+
.groupBy(metaUser.columnRef('id'))
|
|
3985
|
+
.groupBy(metaUser.columnRef('name'))
|
|
3986
|
+
.toString()
|
|
3987
|
+
|
|
3988
|
+
// Look like this
|
|
3989
|
+
// expression :
|
|
3990
|
+
// SELECT
|
|
3991
|
+
// ROW_NUMBER() OVER (ORDER BY `users`.`id`) AS id,
|
|
3992
|
+
// `users`.`id` AS user_id, `users`.`name`, `users`.`email`,
|
|
3993
|
+
// COUNT(`posts`.`id`) AS post_count
|
|
3994
|
+
// FROM `users`
|
|
3995
|
+
// LEFT JOIN `posts` ON `users`.`id` = `posts`.`user_id`
|
|
3996
|
+
// GROUP BY `users`.`id`, `users`.`name`
|
|
3997
|
+
})
|
|
3998
|
+
|
|
3999
|
+
this.belongsTo({ name : 'user' , model : User })
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
new UserPostCountView()
|
|
4004
|
+
.with('user')
|
|
4005
|
+
.get()
|
|
4006
|
+
.then( v=> {
|
|
4007
|
+
console.log(v)
|
|
4008
|
+
})
|
|
4009
|
+
|
|
4010
|
+
```
|
|
4011
|
+
|
|
4012
|
+
## Stored Procedure
|
|
4013
|
+
StoredProcedure is a predefined set of SQL statements stored in the database that you can call (execute) by name.
|
|
4014
|
+
```js
|
|
4015
|
+
|
|
4016
|
+
import { StoredProcedure } from 'tspace-mysql'
|
|
4017
|
+
|
|
4018
|
+
type T = {
|
|
4019
|
+
AddUser: {
|
|
4020
|
+
params: {
|
|
4021
|
+
name : string;
|
|
4022
|
+
email: string;
|
|
4023
|
+
} | [string,string];
|
|
4024
|
+
result: {
|
|
4025
|
+
fieldCount: number;
|
|
4026
|
+
affectedRows: number;
|
|
4027
|
+
insertId: number;
|
|
4028
|
+
info: string;
|
|
4029
|
+
serverStatus: number;
|
|
4030
|
+
warningStatus: number;
|
|
4031
|
+
changedRows: number;
|
|
4032
|
+
}
|
|
4033
|
+
};
|
|
4034
|
+
GetUser: {
|
|
4035
|
+
params: [number];
|
|
4036
|
+
result: any[]
|
|
4037
|
+
},
|
|
4038
|
+
GetUsers: {
|
|
4039
|
+
params: [];
|
|
4040
|
+
result: any[]
|
|
4041
|
+
}
|
|
4042
|
+
};
|
|
4043
|
+
|
|
4044
|
+
class MyStoreProcedure extends StoredProcedure<T> {
|
|
4045
|
+
protected boot(): void {
|
|
4046
|
+
|
|
4047
|
+
this.createProcedure({
|
|
4048
|
+
name: 'AddUser',
|
|
4049
|
+
expression: `
|
|
4050
|
+
CREATE PROCEDURE AddUser(IN name VARCHAR(255), IN email VARCHAR(255))
|
|
4051
|
+
BEGIN
|
|
4052
|
+
INSERT INTO users (name, email) VALUES (name, email);
|
|
4053
|
+
END;
|
|
4054
|
+
`,
|
|
4055
|
+
synchronize: true
|
|
4056
|
+
});
|
|
4057
|
+
|
|
4058
|
+
this.createProcedure({
|
|
4059
|
+
name: 'GetUsers',
|
|
4060
|
+
expression: `
|
|
4061
|
+
CREATE PROCEDURE GetUsers()
|
|
4062
|
+
BEGIN
|
|
4063
|
+
SELECT * FROM users LIMIT 5;
|
|
4064
|
+
END;
|
|
4065
|
+
`,
|
|
4066
|
+
synchronize: true
|
|
4067
|
+
});
|
|
4068
|
+
|
|
4069
|
+
this.createProcedure({
|
|
4070
|
+
name: 'GetUser',
|
|
4071
|
+
expression: `
|
|
4072
|
+
CREATE PROCEDURE GetUser(IN userId INT)
|
|
4073
|
+
BEGIN
|
|
4074
|
+
SELECT * FROM users WHERE id = userId LIMIT 1;
|
|
4075
|
+
END;
|
|
4076
|
+
`,
|
|
4077
|
+
synchronize: true
|
|
4078
|
+
})
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
|
|
4082
|
+
const storeProcedure = new MyStoreProcedure()
|
|
4083
|
+
|
|
4084
|
+
storeProcedure.call('AddUser', { name : 'tspace-mysql' , email : 'tspace-mysql@example.com'})
|
|
4085
|
+
.then(r => console.log(r))
|
|
4086
|
+
.catch(e => console.log(e))
|
|
4087
|
+
|
|
4088
|
+
storeProcedure.call('GetUser',[1])
|
|
4089
|
+
.then(r => console.log(r))
|
|
4090
|
+
.catch(e => console.log(e))
|
|
4091
|
+
|
|
4092
|
+
storeProcedure.call('GetUsers',[])
|
|
4093
|
+
.then(r => console.log(r))
|
|
4094
|
+
.catch(e => console.log(e))
|
|
4095
|
+
|
|
4096
|
+
```
|
|
4097
|
+
## Blueprint
|
|
4098
|
+
|
|
4099
|
+
Blueprint is a tool used for defining database schemas programmatically.
|
|
4100
|
+
It allows developers to describe the structure of their database tables using a simple and intuitive syntax rather than writing SQL queries directly., you may use the:
|
|
4101
|
+
|
|
4102
|
+
```js
|
|
4103
|
+
import { Schema , Blueprint , DB } from 'tspace-mysql'
|
|
4104
|
+
(async () => {
|
|
4105
|
+
await new Schema().table('users', {
|
|
4106
|
+
id : Blueprint.int().notNull().primary().autoIncrement(),
|
|
4107
|
+
// or id : Blueprint.serial().primary(),
|
|
4108
|
+
uuid : Blueprint.varchar(120).null()
|
|
4109
|
+
name : Blueprint.varchar(120).default('name'),
|
|
4110
|
+
email : Blueprint.varchar(255).unique().notNull(),
|
|
4111
|
+
email_verify : Blueprint.tinyInt(),
|
|
4112
|
+
password : Blueprint.varchar(255),
|
|
4113
|
+
json : Blueprint.json(),
|
|
4114
|
+
created_at : Blueprint.null().timestamp(),
|
|
4115
|
+
updated_at : Blueprint.null().timestamp(),
|
|
4116
|
+
deleted_at : Blueprint.null().timestamp()
|
|
4117
|
+
})
|
|
4118
|
+
/**
|
|
4119
|
+
*
|
|
4120
|
+
* @Faker fake data 5 raw
|
|
4121
|
+
* await new DB().table('users').faker(5)
|
|
4122
|
+
*/
|
|
4123
|
+
})()
|
|
4124
|
+
|
|
4125
|
+
/**
|
|
4126
|
+
* To add types of the schema to the database
|
|
4127
|
+
* @Types
|
|
4128
|
+
*
|
|
4129
|
+
*/
|
|
4130
|
+
int (number)
|
|
4131
|
+
tinyInt (number)
|
|
4132
|
+
bigInt (number)
|
|
4133
|
+
double ()
|
|
4134
|
+
float ()
|
|
4135
|
+
json ()
|
|
4136
|
+
varchar (number)
|
|
4137
|
+
char (number)
|
|
4138
|
+
longText()
|
|
4139
|
+
mediumText()
|
|
4140
|
+
tinyText()
|
|
4141
|
+
text()
|
|
4142
|
+
enum(...n)
|
|
4143
|
+
date()
|
|
4144
|
+
dateTime()
|
|
4145
|
+
timestamp ()
|
|
4146
|
+
|
|
4147
|
+
/**
|
|
4148
|
+
* To add attributes of the schema to the database
|
|
4149
|
+
* @Attrbuites
|
|
4150
|
+
*
|
|
4151
|
+
*/
|
|
4152
|
+
unsigned()
|
|
4153
|
+
unique()
|
|
4154
|
+
null()
|
|
4155
|
+
notNull()
|
|
4156
|
+
primary()
|
|
4157
|
+
default(string)
|
|
4158
|
+
defaultTimestamp()
|
|
4159
|
+
autoIncrement()
|
|
4160
|
+
|
|
4161
|
+
/**
|
|
4162
|
+
* To add a foreign key to the column
|
|
4163
|
+
* @ForeginKey
|
|
4164
|
+
*/
|
|
4165
|
+
foreign({ references : ${COLUMN} , on : ${TABLE-NAME OR MODEL CLASSES} })
|
|
4166
|
+
|
|
4167
|
+
/**
|
|
4168
|
+
* To add a index key to the column
|
|
4169
|
+
* @indexKey
|
|
4170
|
+
*/
|
|
4171
|
+
index()
|
|
4172
|
+
```
|
|
4173
|
+
|
|
4174
|
+
## Cli
|
|
4175
|
+
|
|
4176
|
+
To get started, let's install tspace-mysql
|
|
4177
|
+
you may use a basic cli :
|
|
4178
|
+
|
|
4179
|
+
```sh
|
|
4180
|
+
npm install tspace-mysql -g
|
|
4181
|
+
|
|
4182
|
+
```
|
|
4183
|
+
|
|
4184
|
+
## Make Model
|
|
4185
|
+
|
|
4186
|
+
The command will be placed Model in the specific directory.
|
|
4187
|
+
|
|
4188
|
+
```sh
|
|
4189
|
+
|
|
4190
|
+
/**
|
|
4191
|
+
*
|
|
4192
|
+
* @make Model
|
|
4193
|
+
* @options
|
|
4194
|
+
* @arg --m => created scheme table for migrate. short cut migration table like Make Migration
|
|
4195
|
+
* @arg --dir=directory => created model in directory. default root directory
|
|
4196
|
+
* @arg --type=js // extension js. default ts
|
|
4197
|
+
*/
|
|
4198
|
+
tspace-mysql make:model <model name> --m --dir=.... --type=....
|
|
4199
|
+
|
|
4200
|
+
tspace-mysql make:model User --m --dir=app/Models
|
|
4201
|
+
/**
|
|
4202
|
+
*
|
|
4203
|
+
* @Ex directory
|
|
4204
|
+
*/
|
|
4205
|
+
- node_modules
|
|
4206
|
+
- app
|
|
4207
|
+
- Models
|
|
4208
|
+
User.ts
|
|
4209
|
+
```
|
|
4210
|
+
|
|
4211
|
+
## Make Migration
|
|
4212
|
+
|
|
4213
|
+
The command will be placed Migration in the specific directory.
|
|
4214
|
+
|
|
4215
|
+
```sh
|
|
4216
|
+
/**
|
|
4217
|
+
*
|
|
4218
|
+
* @make Migration Table
|
|
4219
|
+
* @options
|
|
4220
|
+
* @arg --dir=directory => created scheme table in directory. default root directory
|
|
4221
|
+
* @arg --type=js // extension js default ts
|
|
4222
|
+
*/
|
|
4223
|
+
tspace-mysql make:migration <table name> --type=... --dir=....
|
|
4224
|
+
|
|
4225
|
+
tspace-mysql make:migration users --dir=app/Models/Migrations
|
|
4226
|
+
/**
|
|
4227
|
+
*
|
|
4228
|
+
* @Ex directory
|
|
4229
|
+
*/
|
|
4230
|
+
- node_modules
|
|
4231
|
+
- app
|
|
4232
|
+
- Models
|
|
4233
|
+
- Migrations
|
|
4234
|
+
create_users_table.ts
|
|
4235
|
+
User.ts
|
|
4236
|
+
```
|
|
4237
|
+
|
|
4238
|
+
## Migrate
|
|
4239
|
+
|
|
4240
|
+
```sh
|
|
4241
|
+
/**
|
|
4242
|
+
*
|
|
4243
|
+
* @run Migrate table
|
|
4244
|
+
* @options
|
|
4245
|
+
* @arg --dir=directory => find migrate in directory. default find in root folder
|
|
4246
|
+
* @arg --type=js // extension js default ts
|
|
4247
|
+
*/
|
|
4248
|
+
tspace-mysql migrate <folder> --type=<type file js or ts> --dir=<directory for migrate>
|
|
4249
|
+
|
|
4250
|
+
tspace-mysql migrate --dir=app/Models/Migrations --type=js
|
|
4251
|
+
|
|
4252
|
+
/**
|
|
4253
|
+
*
|
|
4254
|
+
* @Ex directory
|
|
4255
|
+
*/
|
|
4256
|
+
- node_modules
|
|
4257
|
+
- app
|
|
4258
|
+
- Models
|
|
4259
|
+
- Migrations
|
|
4260
|
+
create_users_table.ts
|
|
4261
|
+
create_posts_table.ts
|
|
4262
|
+
User.ts
|
|
4263
|
+
Post.ts
|
|
4264
|
+
// => migrate all schemas in folder <Migrations>. created into database
|
|
4265
|
+
```
|
|
4266
|
+
|
|
4267
|
+
# Query
|
|
4268
|
+
|
|
4269
|
+
The command will execute a query.
|
|
4270
|
+
|
|
4271
|
+
```sh
|
|
4272
|
+
tspace-mysql query "SELECT * FROM users"
|
|
4273
|
+
|
|
4274
|
+
```
|
|
4275
|
+
|
|
4276
|
+
# Dump
|
|
4277
|
+
|
|
4278
|
+
The command will dump the database or table into a file.
|
|
4279
|
+
|
|
4280
|
+
```sh
|
|
4281
|
+
tspace-mysql dump:db --dir=<folder for dump> --values // backup with values in the tables
|
|
4282
|
+
|
|
4283
|
+
tspace-mysql dump:table "table_name" --dir=<folder for dump> --values // backup with values in the table
|
|
4284
|
+
|
|
4285
|
+
```
|
|
4286
|
+
|
|
4287
|
+
# Generate Models
|
|
4288
|
+
|
|
4289
|
+
The command will generate models from tables in the database.
|
|
4290
|
+
|
|
4291
|
+
```sh
|
|
4292
|
+
tspace-mysql generate:models --dir=<folder for creating>
|
|
4293
|
+
|
|
4294
|
+
tspace-mysql generate:models --dir=app/Models --env=development --decorators
|
|
4295
|
+
|
|
4296
|
+
```
|
|
4297
|
+
|
|
4298
|
+
# Migration Models
|
|
4299
|
+
|
|
4300
|
+
The command will generate migrations based on the schema in your models to a .sql file,
|
|
4301
|
+
can also push the migration files to the database.
|
|
4302
|
+
|
|
4303
|
+
```sh
|
|
4304
|
+
/**
|
|
4305
|
+
*
|
|
4306
|
+
* @arg --push will push the migration files to the database
|
|
4307
|
+
* @arg --generate will generate the migration files
|
|
4308
|
+
*/
|
|
4309
|
+
tspace-mysql migrations:models --dir=<path-to-migration> --models=<path to your models> --generate
|
|
4310
|
+
tspace-mysql migrations:models --dir=<path-to-migration> --push
|
|
4311
|
+
|
|
4312
|
+
tspace-mysql migrations:models --models=src/app/models --dir=migrations --generate
|
|
4313
|
+
tspace-mysql migrations:models --dir=migrations --push
|
|
4314
|
+
|
|
4315
|
+
```
|
|
4316
|
+
|
|
4317
|
+
# Migration DB
|
|
4318
|
+
|
|
4319
|
+
The command will generate migrations based on the schema in your database to a .sql file,
|
|
4320
|
+
can also push the migration files to the database.
|
|
4321
|
+
|
|
4322
|
+
```sh
|
|
4323
|
+
/**
|
|
4324
|
+
*
|
|
4325
|
+
* @arg --push will push the migration files to the database
|
|
4326
|
+
* @arg --generate will generate the migration files
|
|
4327
|
+
*/
|
|
4328
|
+
tspace-mysql migrations:db --dir=<path-to-migration> --generate --env=<YOUR_ENV> -filename=<YOUR_FILENAME>
|
|
4329
|
+
tspace-mysql migrations:db --dir=<path-to-migration> --push
|
|
4330
|
+
|
|
4331
|
+
tspace-mysql migrations:db --dir=migrations --generate --filename=dump.sql --env=development
|
|
4332
|
+
tspace-mysql migrations:db --dir=migrations --push --filename=dump.sql --env=development
|
|
57
4333
|
|
|
58
|
-
|
|
59
|
-
- [Install](https://thanathip41.github.io/tspace-mysql/#/?id=install)
|
|
60
|
-
- [Configuration](https://thanathip41.github.io/tspace-mysql/#/?id=configuration)
|
|
61
|
-
- [SQL Like](https://thanathip41.github.io/tspace-mysql/#/sql-like)
|
|
62
|
-
- [Select Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=select-statements)
|
|
63
|
-
- [Insert Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=insert-statements)
|
|
64
|
-
- [Update Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=update-statements)
|
|
65
|
-
- [Delete Statements](https://thanathip41.github.io/tspace-mysql/#/sql-like?id=delete-statements)
|
|
66
|
-
- [Query Builder](https://thanathip41.github.io/tspace-mysql/#/query-builder)
|
|
67
|
-
- [Table Name & Alias Name](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=table-name--alias-name)
|
|
68
|
-
- [Returning Results](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=returning-results)
|
|
69
|
-
- [Query Statement](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=query-statements)
|
|
70
|
-
- [Select Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=select-statements)
|
|
71
|
-
- [Insert Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=insert-statements)
|
|
72
|
-
- [Update Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=update-statements)
|
|
73
|
-
- [Delete Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=delete-statements)
|
|
74
|
-
- [Raw Expressions](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=raw-expressions)
|
|
75
|
-
- [Ordering, Grouping, Limit and Offset](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=ordering-grouping-limit-and-offset)
|
|
76
|
-
- [Ordering](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=ordering)
|
|
77
|
-
- [Grouping](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=grouping)
|
|
78
|
-
- [Limit and Offset](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=limit-and-offset)
|
|
79
|
-
- [Joins](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=joins)
|
|
80
|
-
- [Inner Join Clause](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=inner-join-clause)
|
|
81
|
-
- [Left Join, Right Join Clause](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=left-join-right-join-clause)
|
|
82
|
-
- [Cross Join Clause](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=cross-join-clause)
|
|
83
|
-
- [Basic Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=basic-where-clauses)
|
|
84
|
-
- [Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-clauses)
|
|
85
|
-
- [Or Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=or-where-clauses)
|
|
86
|
-
- [Where cases](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-cases)
|
|
87
|
-
- [Where Object Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-object-clauses)
|
|
88
|
-
- [JSON Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=json-where-clauses)
|
|
89
|
-
- [Additional Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=additional-where-clauses)
|
|
90
|
-
- [Logical Grouping](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=logical-grouping)
|
|
91
|
-
- [Advanced Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=advanced-where-clauses)
|
|
92
|
-
- [Where Exists Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=where-exists-clauses)
|
|
93
|
-
- [Subquery Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=subquery-where-clauses)
|
|
94
|
-
- [Conditional Where Clauses](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=conditional-where-clauses)
|
|
95
|
-
- [GetGroupBy](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=getgroupby)
|
|
96
|
-
- [Paginating](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=paginating)
|
|
97
|
-
- [Hook Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=hook-statements)
|
|
98
|
-
- [Faker Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=faker-statements)
|
|
99
|
-
- [Unset Statements](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=unset-statements)
|
|
100
|
-
- [Common Table Expressions](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=common-table-expressions)
|
|
101
|
-
- [Union](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=union)
|
|
102
|
-
- [More Methods](https://thanathip41.github.io/tspace-mysql/#/query-builder?id=more-methods)
|
|
103
|
-
- [Database Transactions](https://thanathip41.github.io/tspace-mysql/#/database-transactions)
|
|
104
|
-
- [Race Condition](https://thanathip41.github.io/tspace-mysql/#/race-condition)
|
|
105
|
-
- [Connection](https://thanathip41.github.io/tspace-mysql/#/connection)
|
|
106
|
-
- [Backup](https://thanathip41.github.io/tspace-mysql/#/backup)
|
|
107
|
-
- [Injection](https://thanathip41.github.io/tspace-mysql/#/injection)
|
|
108
|
-
- [Model](https://thanathip41.github.io/tspace-mysql/#/model)
|
|
109
|
-
- [Basic Model Setup](https://thanathip41.github.io/tspace-mysql/#/model?id=basic-model-setup)
|
|
110
|
-
- [Table Name](https://thanathip41.github.io/tspace-mysql/#/model?id=table-name)
|
|
111
|
-
- [Pattern](https://thanathip41.github.io/tspace-mysql/#/model?id=pattern)
|
|
112
|
-
- [UUID](https://thanathip41.github.io/tspace-mysql/#/model?id=uuid)
|
|
113
|
-
- [Timestamp](https://thanathip41.github.io/tspace-mysql/#/model?id=timestamp)
|
|
114
|
-
- [Debug](https://thanathip41.github.io/tspace-mysql/#/model?id=debug)
|
|
115
|
-
- [Observer](https://thanathip41.github.io/tspace-mysql/#/model?id=observer)
|
|
116
|
-
- [Logger](https://thanathip41.github.io/tspace-mysql/#/model?id=logger)
|
|
117
|
-
- [Hooks](https://thanathip41.github.io/tspace-mysql/#/model?id=hooks)
|
|
118
|
-
- [Global Scope](https://thanathip41.github.io/tspace-mysql/#/model?id=global-scope)
|
|
119
|
-
- [Schema](https://thanathip41.github.io/tspace-mysql/#/model?id=schema)
|
|
120
|
-
- [Schema Model](https://thanathip41.github.io/tspace-mysql/#/model?id=schema-model)
|
|
121
|
-
- [Virtual Column](https://thanathip41.github.io/tspace-mysql/#/model?id=virtual-column)
|
|
122
|
-
- [Validation](https://thanathip41.github.io/tspace-mysql/#/model?id=validation)
|
|
123
|
-
- [Sync](https://thanathip41.github.io/tspace-mysql/#/model?id=sync)
|
|
124
|
-
- [SoftDelete](https://thanathip41.github.io/tspace-mysql/#/model?id=softdelete)
|
|
125
|
-
- [Joins Model](https://thanathip41.github.io/tspace-mysql/#/model?id=joins-model)
|
|
126
|
-
- [Inner Join Model Clause](https://thanathip41.github.io/tspace-mysql/#/model?id=inner-join-model-clause)
|
|
127
|
-
- [Left Join , Right Join Model Clause](https://thanathip41.github.io/tspace-mysql/#/model?id=left-join-right-join-model-clause)
|
|
128
|
-
- [Cross Join Model Clause](https://thanathip41.github.io/tspace-mysql/#/model?id=cross-join-model-clause)
|
|
129
|
-
- [Relationships](https://thanathip41.github.io/tspace-mysql/#/model?id=relationships)
|
|
130
|
-
- [One To One](https://thanathip41.github.io/tspace-mysql/#/model?id=one-to-one)
|
|
131
|
-
- [One To Many](https://thanathip41.github.io/tspace-mysql/#/model?id=one-to-many)
|
|
132
|
-
- [Belongs To](https://thanathip41.github.io/tspace-mysql/#/model?id=belongs-to)
|
|
133
|
-
- [Many To Many](https://thanathip41.github.io/tspace-mysql/#/model?id=many-to-many)
|
|
134
|
-
- [Relation](https://thanathip41.github.io/tspace-mysql/#/model?id=relation)
|
|
135
|
-
- [Deeply Nested Relations](https://thanathip41.github.io/tspace-mysql/#/model?id=deeply-nested-relations)
|
|
136
|
-
- [Relation Exists](https://thanathip41.github.io/tspace-mysql/#/model?id=relation-exists)
|
|
137
|
-
- [Relation Count](https://thanathip41.github.io/tspace-mysql/#/model?id=relation-count)
|
|
138
|
-
- [Relation Trashed](https://thanathip41.github.io/tspace-mysql/#/model?id=relation-trashed)
|
|
139
|
-
- [Built in Relation Functions](https://thanathip41.github.io/tspace-mysql/#/model?id=built-in-relation-functions)
|
|
140
|
-
- [Cache](https://thanathip41.github.io/tspace-mysql/#/model?id=cache)
|
|
141
|
-
- [Decorator](https://thanathip41.github.io/tspace-mysql/#/model?id=decorator)
|
|
142
|
-
- [Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=type-safety)
|
|
143
|
-
- [Select Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=select-type-safety-type-safety)
|
|
144
|
-
- [OrderBy Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=order-by-type-safety)
|
|
145
|
-
- [GroupBy Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=group-by-type-safety)
|
|
146
|
-
- [Where Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=where-type-safety)
|
|
147
|
-
- [Insert Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=insert-type-safety)
|
|
148
|
-
- [Update Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=update-type-safety)
|
|
149
|
-
- [Delete Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=delete-type-safety)
|
|
150
|
-
- [Relationships Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=relationships-type-safety)
|
|
151
|
-
- [Results Type Safety](https://thanathip41.github.io/tspace-mysql/#/model?id=results-type-safety)
|
|
152
|
-
- [Metadata](https://thanathip41.github.io/tspace-mysql/#/model?id=metadata)
|
|
153
|
-
- [Audit](https://thanathip41.github.io/tspace-mysql/#/model?id=audit)
|
|
154
|
-
- [Repository](https://thanathip41.github.io/tspace-mysql/#/repository)
|
|
155
|
-
- [Select Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=select-statements)
|
|
156
|
-
- [Insert Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=insert-statements)
|
|
157
|
-
- [Update Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=update-statements)
|
|
158
|
-
- [Delete Statements](https://thanathip41.github.io/tspace-mysql/#/repository?id=delete-statements)
|
|
159
|
-
- [Transactions](https://thanathip41.github.io/tspace-mysql/#/repository?id=transactions)
|
|
160
|
-
- [Relations](https://thanathip41.github.io/tspace-mysql/#/repository?id=relations)
|
|
161
|
-
- [View](https://thanathip41.github.io/tspace-mysql/#/view)
|
|
162
|
-
- [Stored Procedure](https://thanathip41.github.io/tspace-mysql/#/stored-procedure)
|
|
163
|
-
- [Blueprint](https://thanathip41.github.io/tspace-mysql/#/blueprint)
|
|
164
|
-
- [Cli](https://thanathip41.github.io/tspace-mysql/#/cli)
|
|
165
|
-
- [Make Model](https://thanathip41.github.io/tspace-mysql/#/cli?id=make-model)
|
|
166
|
-
- [Make Migration](https://thanathip41.github.io/tspace-mysql/#/cli?id=make-migration)
|
|
167
|
-
- [Migrate](https://thanathip41.github.io/tspace-mysql/#/cli?id=migrate)
|
|
168
|
-
- [Query](https://thanathip41.github.io/tspace-mysql/#/cli?id=query)
|
|
169
|
-
- [Dump](https://thanathip41.github.io/tspace-mysql/#/cli?id=dump)
|
|
170
|
-
- [Generate Models](https://thanathip41.github.io/tspace-mysql/#/cli?id=generate-models)
|
|
171
|
-
- [Migration Models](https://thanathip41.github.io/tspace-mysql/#/cli?id=migration-models)
|
|
172
|
-
- [Migration DB](https://thanathip41.github.io/tspace-mysql/#/cli?id=migration-db)
|
|
4334
|
+
```
|