velocious 1.0.4 → 1.0.6

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.
Files changed (79) hide show
  1. package/index.mjs +1 -19
  2. package/package.json +6 -3
  3. package/spec/cli/commands/db/create-spec.mjs +1 -1
  4. package/spec/cli/commands/db/migrate-spec.mjs +2 -0
  5. package/spec/database/connection/drivers/mysql/query-parser-spec.mjs +6 -5
  6. package/spec/database/drivers/mysql/connection-spec.mjs +2 -3
  7. package/spec/database/record/create-spec.mjs +9 -0
  8. package/spec/database/record/find-spec.mjs +0 -1
  9. package/spec/database/record/query-spec.mjs +37 -0
  10. package/spec/dummy/index.mjs +14 -2
  11. package/spec/dummy/src/config/configuration.example.mjs +16 -1
  12. package/spec/dummy/src/config/configuration.peakflow.mjs +16 -1
  13. package/spec/dummy/src/database/migrations/20230728075328-create-projects.mjs +0 -1
  14. package/spec/dummy/src/database/migrations/20230728075329-create-tasks.mjs +0 -1
  15. package/spec/dummy/src/database/migrations/20250605133926-create-project-translations.mjs +16 -0
  16. package/spec/dummy/src/models/project.mjs +9 -0
  17. package/spec/dummy/src/models/task.mjs +6 -2
  18. package/src/big-brother.mjs +37 -0
  19. package/src/cli/commands/db/create.mjs +8 -6
  20. package/src/cli/commands/db/migrate.mjs +1 -2
  21. package/src/cli/commands/generate/migration.mjs +1 -1
  22. package/src/cli/commands/generate/model.mjs +1 -1
  23. package/src/configuration.mjs +39 -3
  24. package/src/database/drivers/base.mjs +60 -0
  25. package/src/database/drivers/mysql/column.mjs +8 -0
  26. package/src/database/drivers/mysql/index.mjs +34 -2
  27. package/src/database/drivers/mysql/options.mjs +1 -0
  28. package/src/database/drivers/mysql/query-parser.mjs +2 -23
  29. package/src/database/drivers/mysql/table.mjs +25 -0
  30. package/src/database/drivers/sqlite/base.mjs +108 -0
  31. package/src/database/drivers/sqlite/column.mjs +10 -0
  32. package/src/database/drivers/sqlite/index.native.mjs +22 -63
  33. package/src/database/drivers/sqlite/index.web.mjs +28 -37
  34. package/src/database/drivers/sqlite/options.mjs +2 -1
  35. package/src/database/drivers/sqlite/query-parser.mjs +2 -23
  36. package/src/database/drivers/sqlite/query.native.mjs +16 -1
  37. package/src/database/drivers/sqlite/query.web.mjs +27 -2
  38. package/src/database/drivers/sqlite/sql/create-index.mjs +4 -0
  39. package/src/database/drivers/sqlite/table.mjs +24 -0
  40. package/src/database/initializer-from-require-context.mjs +21 -0
  41. package/src/database/migrate-from-require-context.mjs +11 -2
  42. package/src/database/migration/index.mjs +34 -2
  43. package/src/database/migrator.mjs +75 -0
  44. package/src/database/pool/async-tracked-multi-connection.mjs +1 -1
  45. package/src/database/pool/base.mjs +19 -1
  46. package/src/database/pool/single-multi-use.mjs +1 -1
  47. package/src/database/query/base.mjs +2 -1
  48. package/src/database/query/create-index-base.mjs +50 -0
  49. package/src/database/query/create-table-base.mjs +40 -17
  50. package/src/database/query/index.mjs +83 -21
  51. package/src/database/query/insert-base.mjs +4 -0
  52. package/src/database/query/preloader/belongs-to.mjs +52 -0
  53. package/src/database/query/preloader/has-many.mjs +55 -0
  54. package/src/database/query/preloader.mjs +41 -0
  55. package/src/database/query/where-base.mjs +9 -0
  56. package/src/database/query/where-hash.mjs +35 -0
  57. package/src/database/query/where-plain.mjs +13 -0
  58. package/src/database/query-parser/base-query-parser.mjs +33 -0
  59. package/src/database/query-parser/group-parser.mjs +40 -0
  60. package/src/database/query-parser/joins-parser.mjs +48 -7
  61. package/src/database/query-parser/limit-parser.mjs +40 -0
  62. package/src/database/query-parser/options.mjs +4 -3
  63. package/src/database/query-parser/order-parser.mjs +39 -0
  64. package/src/database/query-parser/select-parser.mjs +5 -1
  65. package/src/database/query-parser/where-parser.mjs +39 -0
  66. package/src/database/record/index.mjs +464 -29
  67. package/src/database/record/instance-relationships/base.mjs +28 -0
  68. package/src/database/record/instance-relationships/belongs-to.mjs +20 -0
  69. package/src/database/record/instance-relationships/has-many.mjs +47 -0
  70. package/src/database/record/relationships/base.mjs +32 -0
  71. package/src/database/record/relationships/belongs-to.mjs +12 -0
  72. package/src/database/record/relationships/has-many.mjs +12 -0
  73. package/src/database/table-data/index.mjs +15 -25
  74. package/src/http-server/worker-handler/worker-thread.mjs +7 -4
  75. package/src/templates/generate-model.mjs +3 -1
  76. package/src/utils/rest-args-error.mjs +9 -0
  77. package/src/database/drivers/index.mjs +0 -5
  78. package/src/database/index.mjs +0 -15
  79. package/src/database/migrator/index.mjs +0 -15
@@ -0,0 +1,75 @@
1
+ import {digg} from "diggerize"
2
+ import TableData from "./table-data/index"
3
+
4
+ export default class VelociousDatabaseMigrator {
5
+ constructor({configuration}) {
6
+ this.configuration = configuration
7
+ }
8
+
9
+ async prepare() {
10
+ const exists = await this.migrationsTableExist()
11
+
12
+ if (!exists) await this.createMigrationsTable()
13
+
14
+ await this.loadMigrationsVersions()
15
+ }
16
+
17
+ async createMigrationsTable() {
18
+ const schemaMigrationsTable = new TableData("schema_migrations", {ifNotExists: true})
19
+
20
+ schemaMigrationsTable.string("version", {null: false, primaryKey: true})
21
+
22
+ await this.configuration.getDatabasePool().withConnection(async (db) => {
23
+ const createSchemaMigrationsTableSqls = db.createTableSql(schemaMigrationsTable)
24
+
25
+ for (const createSchemaMigrationsTableSql of createSchemaMigrationsTableSqls) {
26
+ await db.query(createSchemaMigrationsTableSql)
27
+ }
28
+ })
29
+ }
30
+
31
+ hasRunMigrationVersion(version) {
32
+ if (!this.migrationsVersions) {
33
+ throw new Error("Migrations versions hasn't been loaded yet")
34
+ }
35
+
36
+ if (version in this.migrationsVersions) {
37
+ return true
38
+ }
39
+
40
+ return false
41
+ }
42
+
43
+ async loadMigrationsVersions() {
44
+ const db = this.configuration.getDatabasePool()
45
+
46
+ this.migrationsVersions = {}
47
+
48
+ await db.withConnection(async () => {
49
+ const rows = await db.select("schema_migrations")
50
+
51
+ for (const row of rows) {
52
+ const version = digg(row, "version")
53
+
54
+ this.migrationsVersions[version] = true
55
+ }
56
+ })
57
+ }
58
+
59
+ async migrationsTableExist() {
60
+ let exists = false
61
+
62
+ await this.configuration.getDatabasePool().withConnection(async (db) => {
63
+ const tables = await db.getTables()
64
+
65
+ for (const table of tables) {
66
+ if (table.getName() == "schema_migrations") {
67
+ exists = true
68
+ break
69
+ }
70
+ }
71
+ })
72
+
73
+ return exists
74
+ }
75
+ }
@@ -58,7 +58,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
58
58
 
59
59
  await this.asyncLocalStorage.run(id, async () => {
60
60
  try {
61
- await callback()
61
+ await callback(connection)
62
62
  } finally {
63
63
  this.checkin(connection)
64
64
  }
@@ -34,11 +34,29 @@ class VelociousDatabasePoolBase {
34
34
  }
35
35
  }
36
36
 
37
- const forwardMethods = ["createTableSql", "deleteSql", "insertSql", "query", "quote", "updateSql"]
37
+ const forwardMethods = [
38
+ "createIndex",
39
+ "createIndexSql",
40
+ "createTable",
41
+ "createTableSql",
42
+ "delete",
43
+ "deleteSql",
44
+ "getTables",
45
+ "insert",
46
+ "insertSql",
47
+ "query",
48
+ "quote",
49
+ "select",
50
+ "update",
51
+ "updateSql"
52
+ ]
38
53
 
39
54
  for (const forwardMethod of forwardMethods) {
40
55
  VelociousDatabasePoolBase.prototype[forwardMethod] = function(...args) {
41
56
  const connection = this.getCurrentConnection()
57
+ const connectionMethod = connection[forwardMethod]
58
+
59
+ if (!connectionMethod) throw new Error(`${forwardMethod} isn't defined on driver`)
42
60
 
43
61
  return connection[forwardMethod](...args)
44
62
  }
@@ -27,7 +27,7 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
27
27
 
28
28
  async withConnection(callback) {
29
29
  await this.checkout() // Ensure a connection is present
30
- await callback()
30
+ await callback(this.connection)
31
31
  }
32
32
 
33
33
  getCurrentConnection() {
@@ -3,7 +3,8 @@ export default class VelociousDatabaseQueryBase {
3
3
  this.driver = driver
4
4
  }
5
5
 
6
- getOptions = () => this.driver?.options()
6
+ getDriver = () => this.driver
7
+ getOptions = () => this.getDriver()?.options()
7
8
 
8
9
  toSql() {
9
10
  throw new Error("'toSql' wasn't implemented")
@@ -0,0 +1,50 @@
1
+ import {digs} from "diggerize"
2
+ import QueryBase from "./base.mjs"
3
+
4
+ export default class VelociousDatabaseQueryCreateIndexBase extends QueryBase {
5
+ constructor({columns, driver, ifNotExists, name, unique, tableName}) {
6
+ super({driver})
7
+ this.columns = columns
8
+ this.name = name
9
+ this.tableName = tableName
10
+ this.ifNotExists = ifNotExists
11
+ this.unique = unique
12
+ }
13
+
14
+ generateIndexName() {
15
+ let indexName = `index_on_${this.tableName}_`
16
+
17
+ for (const columnIndex in this.columns) {
18
+ if (columnIndex > 0) indexName += "_"
19
+
20
+ indexName += this.columns[columnIndex]
21
+ }
22
+
23
+ return indexName
24
+ }
25
+
26
+ toSql() {
27
+ const {tableName} = this
28
+ const {columnQuote, indexQuote, tableQuote} = digs(this.getOptions(), "columnQuote", "indexQuote", "tableQuote")
29
+ let sql = "CREATE"
30
+
31
+ if (this.unique) sql += " UNIQUE"
32
+
33
+ sql += " INDEX"
34
+
35
+ if (this.ifNotExists) sql += " IF NOT EXISTS"
36
+
37
+ sql += ` ${indexQuote}${this.name || this.generateIndexName()}${indexQuote}`
38
+ sql += ` ON ${tableQuote}${tableName}${tableQuote} (`
39
+
40
+ for (const columnIndex in this.columns) {
41
+ if (columnIndex > 0) sql += ", "
42
+
43
+ sql += `${columnQuote}${this.columns[columnIndex]}${columnQuote}`
44
+ }
45
+
46
+ sql += ")"
47
+
48
+ return sql
49
+ }
50
+ }
@@ -1,14 +1,18 @@
1
+ import CreateIndexBase from "./create-index-base.mjs"
1
2
  import QueryBase from "./base.mjs"
2
3
 
3
4
  export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
4
- constructor({driver, ifNotExists, tableData}) {
5
+ constructor({driver, ifNotExists, indexInCreateTable = true, tableData}) {
5
6
  super({driver})
6
7
  this.ifNotExists = ifNotExists
8
+ this.indexInCreateTable = indexInCreateTable
7
9
  this.tableData = tableData
8
10
  }
9
11
 
10
12
  toSql() {
11
13
  const {tableData} = this
14
+ const sqls = []
15
+
12
16
  let sql = "CREATE TABLE"
13
17
 
14
18
  if (this.ifNotExists || tableData.getIfNotExists()) sql += " IF NOT EXISTS"
@@ -36,34 +40,53 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
36
40
 
37
41
  if (column.args.autoIncrement) sql += " AUTO_INCREMENT"
38
42
  if (column.args.primaryKey) sql += " PRIMARY KEY"
43
+ if (column.args.null === false) sql += " NOT NULL"
39
44
  }
40
45
 
41
- for (const index of tableData.getIndexes()) {
42
- sql += ","
46
+ if (this.indexInCreateTable) {
47
+ for (const index of tableData.getIndexes()) {
48
+ sql += ","
43
49
 
44
- if (index.getUnique()) {
45
- sql += " UNIQUE"
46
- }
50
+ if (index.getUnique()) {
51
+ sql += " UNIQUE"
52
+ }
47
53
 
48
- sql += " INDEX"
54
+ sql += " INDEX"
49
55
 
50
- if (index.getName()) {
51
- sql += ` ${index.getName()}`
52
- }
56
+ if (index.getName()) {
57
+ sql += ` ${index.getName()}`
58
+ }
53
59
 
54
- sql += " ("
60
+ sql += " ("
55
61
 
56
- index.getColumns().forEach((column, columnIndex) => {
57
- if (columnIndex > 0) sql += ", "
62
+ index.getColumns().forEach((column, columnIndex) => {
63
+ if (columnIndex > 0) sql += ", "
58
64
 
59
- sql += this.driver.quoteColumn(column.name)
60
- })
65
+ sql += this.driver.quoteColumn(column.name)
66
+ })
61
67
 
62
- sql += ")"
68
+ sql += ")"
69
+ }
63
70
  }
64
71
 
65
72
  sql += ")"
66
73
 
67
- return sql
74
+ sqls.push(sql)
75
+
76
+ if (!this.indexInCreateTable) {
77
+ for (const index of tableData.getIndexes()) {
78
+ const createIndexArgs = {
79
+ columns: index.getColumns(),
80
+ driver: this.getDriver(),
81
+ tableName: tableData.getName(),
82
+ unique: index.getUnique()
83
+ }
84
+ const sql = new CreateIndexBase(createIndexArgs).toSql()
85
+
86
+ sqls.push(sql)
87
+ }
88
+ }
89
+
90
+ return [sql]
68
91
  }
69
92
  }
@@ -1,20 +1,27 @@
1
1
  import FromPlain from "./from-plain.mjs"
2
+ import {incorporate} from "incorporator"
3
+ import * as inflection from "inflection"
2
4
  import JoinPlain from "./join-plain.mjs"
3
5
  import OrderPlain from "./order-plain.mjs"
6
+ import Preloader from "./preloader.mjs"
4
7
  import SelectPlain from "./select-plain.mjs"
8
+ import WhereHash from "./where-hash.mjs"
9
+ import WherePlain from "./where-plain.mjs"
5
10
 
6
11
  export default class VelociousDatabaseQuery {
7
- constructor({driver, froms = [], joins = [], handler, limits = [], modelClass, orders = [], selects = [], wheres = []}) {
8
- if (!driver) throw new Error("No driver given")
9
- if (!handler) throw new Error("No handler given")
12
+ constructor({driver, froms = [], groups = [], joins = [], handler, limits = [], modelClass, orders = [], preload = {}, selects = [], wheres = []}) {
13
+ if (!driver) throw new Error("No driver given to query")
14
+ if (!handler) throw new Error("No handler given to query")
10
15
 
11
16
  this.driver = driver
12
17
  this.handler = handler
13
18
  this.modelClass = modelClass
14
19
  this._froms = froms
20
+ this._groups = groups
15
21
  this._joins = joins
16
22
  this._limits = limits
17
23
  this._orders = orders
24
+ this._preload = preload
18
25
  this._selects = selects
19
26
  this._wheres = wheres
20
27
  }
@@ -24,10 +31,12 @@ export default class VelociousDatabaseQuery {
24
31
  driver: this.driver,
25
32
  froms: [...this._froms],
26
33
  handler: this.handler.clone(),
34
+ groups: [...this._groups],
27
35
  joins: [...this._joins],
28
36
  limits: [...this._limits],
29
37
  modelClass: this.modelClass,
30
38
  orders: [...this._orders],
39
+ preload: {...this._preload},
31
40
  selects: [...this._selects],
32
41
  wheres: [...this._wheres]
33
42
  })
@@ -35,8 +44,38 @@ export default class VelociousDatabaseQuery {
35
44
  return newQuery
36
45
  }
37
46
 
38
- getOptions() {
39
- return this.driver.options()
47
+ getOptions = () => this.driver.options()
48
+
49
+ async findBy(conditions) {
50
+ const newConditions = {}
51
+
52
+ for (const key in conditions) {
53
+ const keyUnderscore = inflection.underscore(key)
54
+
55
+ newConditions[keyUnderscore] = conditions[key]
56
+ }
57
+
58
+ return await this.clone().where(newConditions).first()
59
+ }
60
+
61
+ async findOrCreateBy(conditions) {
62
+ const record = await this.findOrInitializeBy(conditions)
63
+
64
+ if (record.isNewRecord()) {
65
+ await record.save()
66
+ }
67
+
68
+ return record
69
+ }
70
+
71
+ async findOrInitializeBy(conditions) {
72
+ const record = await this.findBy(conditions)
73
+
74
+ if (record) return record
75
+
76
+ const newRecord = new this.modelClass(conditions)
77
+
78
+ return newRecord
40
79
  }
41
80
 
42
81
  async first() {
@@ -55,24 +94,31 @@ export default class VelociousDatabaseQuery {
55
94
  return this
56
95
  }
57
96
 
58
- async last() {
59
- return await this.clone().reverseOrder().first()
60
- }
61
-
62
- limit(value) {
63
- this._limits.push(value)
97
+ group(group) {
98
+ this._groups.push(group)
64
99
  return this
65
100
  }
66
101
 
67
102
  joins(join) {
68
- if (typeof join == "string") join = new JoinPlain({plain: join, query: this})
69
-
70
- join.query = this
103
+ if (typeof join == "string") {
104
+ join = new JoinPlain({plain: join, query: this})
105
+ } else if (typeof join == "object") {
106
+ // Do nothing
107
+ } else {
108
+ throw new Error(`Unknown type of join: ${typeof join}`)
109
+ }
71
110
 
72
111
  this._joins.push(join)
73
112
  return this
74
113
  }
75
114
 
115
+ last = async () => await this.clone().reverseOrder().first()
116
+
117
+ limit(value) {
118
+ this._limits.push(value)
119
+ return this
120
+ }
121
+
76
122
  order(order) {
77
123
  if (typeof order == "number" || typeof order == "string") order = new OrderPlain({plain: order, query: this})
78
124
 
@@ -82,6 +128,11 @@ export default class VelociousDatabaseQuery {
82
128
  return this
83
129
  }
84
130
 
131
+ preload(data) {
132
+ incorporate(this._preload, data)
133
+ return this
134
+ }
135
+
85
136
  reorder(order) {
86
137
  this._orders = []
87
138
  this.order(order)
@@ -119,23 +170,34 @@ export default class VelociousDatabaseQuery {
119
170
  const models = []
120
171
 
121
172
  for (const result of results) {
122
- const model = new this.modelClass(result)
173
+ const model = new this.modelClass()
123
174
 
175
+ model.loadExistingRecord(result)
124
176
  models.push(model)
125
177
  }
126
178
 
179
+ if (Object.keys(this._preload).length > 0 && models.length > 0) {
180
+ const preloader = new Preloader({
181
+ modelClass: this.modelClass,
182
+ models,
183
+ preload: this._preload
184
+ })
185
+
186
+ await preloader.run()
187
+ }
188
+
127
189
  return models
128
190
  }
129
191
 
130
- toSql() {
131
- return this.driver.queryToSql(this)
132
- }
192
+ toSql = () => this.driver.queryToSql(this)
133
193
 
134
194
  where(where) {
135
195
  if (typeof where == "string") {
136
- where = new WherePlain({plain: where})
137
- } else if (typeof where == "object" && where.constructor.name == "object") {
138
- where = new WhereHash({hash: where})
196
+ where = new WherePlain(this, where)
197
+ } else if (typeof where == "object" && (where.constructor.name == "object" || where.constructor.name == "Object")) {
198
+ where = new WhereHash(this, where)
199
+ } else {
200
+ throw new Error(`Invalid type of where: ${typeof where} (${where.constructor.name})`)
139
201
  }
140
202
 
141
203
  this._wheres.push(where)
@@ -1,5 +1,9 @@
1
1
  export default class VelociousDatabaseQueryInsertBase {
2
2
  constructor({driver, tableName, data}) {
3
+ if (!driver) throw new Error("No driver given to insert base")
4
+ if (!tableName) throw new Error(`Invalid table name given to insert base: ${tableName}`)
5
+ if (!data) throw new Error("No data given to insert base")
6
+
3
7
  this.data = data
4
8
  this.driver = driver
5
9
  this.tableName = tableName
@@ -0,0 +1,52 @@
1
+ import * as inflection from "inflection"
2
+ import restArgsError from "../../../utils/rest-args-error.mjs"
3
+
4
+ export default class VelociousDatabaseQueryPreloaderBelongsTo {
5
+ constructor({models, relationship, ...restArgs}) {
6
+ restArgsError(restArgs)
7
+
8
+ this.models = models
9
+ this.relationship = relationship
10
+ }
11
+
12
+ async run() {
13
+ const foreignKeyValues = []
14
+ const modelsById = {}
15
+ const foreignKey = this.relationship.getForeignKey()
16
+ const foreignKeyCamelized = inflection.camelize(foreignKey, true)
17
+ const preloadCollections = {}
18
+
19
+ for (const model of this.models) {
20
+ const foreignKeyValue = model[foreignKeyCamelized]()
21
+
22
+ preloadCollections[model.id()] = []
23
+ foreignKeyValues.push(foreignKeyValue)
24
+ modelsById[model.id()] = model
25
+ }
26
+
27
+ const whereArgs = {}
28
+ const primaryKey = this.relationship.getPrimaryKey()
29
+
30
+ whereArgs[primaryKey] = foreignKeyValues
31
+
32
+ // Load target models to be preloaded on the given models
33
+ const targetModels = await this.relationship.getTargetModelClass().where(whereArgs).toArray()
34
+ const targetModelsById = {}
35
+
36
+ for (const targetModel of targetModels) {
37
+ targetModelsById[targetModel.id()] = targetModel
38
+ }
39
+
40
+ // Set the target preloaded models on the given models
41
+ for (const model of this.models) {
42
+ const foreignKeyValue = model[foreignKeyCamelized]()
43
+ const targetModel = targetModelsById[foreignKeyValue]
44
+ const modelRelationship = model.getRelationshipByName(this.relationship.getRelationshipName())
45
+
46
+ modelRelationship.setPreloaded(true)
47
+ modelRelationship.setLoaded(targetModel)
48
+ }
49
+
50
+ return targetModels
51
+ }
52
+ }
@@ -0,0 +1,55 @@
1
+ import * as inflection from "inflection"
2
+ import restArgsError from "../../../utils/rest-args-error.mjs"
3
+
4
+ export default class VelociousDatabaseQueryPreloaderHasMany {
5
+ constructor({models, relationship, ...restArgs}) {
6
+ restArgsError(restArgs)
7
+
8
+ this.models = models
9
+ this.relationship = relationship
10
+ }
11
+
12
+ async run() {
13
+ const modelIds = []
14
+ const modelsById = {}
15
+ const foreignKey = this.relationship.getForeignKey()
16
+ const foreignKeyCamelized = inflection.camelize(foreignKey, true)
17
+ const preloadCollections = {}
18
+
19
+ for (const model of this.models) {
20
+ preloadCollections[model.id()] = []
21
+ modelIds.push(model.id())
22
+
23
+ if (!(model.id in modelsById)) modelsById[model.id()] = []
24
+
25
+ modelsById[model.id()].push(model)
26
+ }
27
+
28
+ const whereArgs = {}
29
+
30
+ whereArgs[foreignKey] = modelIds
31
+
32
+ // Load target models to be preloaded on the given models
33
+ const targetModels = await this.relationship.getTargetModelClass().where(whereArgs).toArray()
34
+
35
+ for (const targetModel of targetModels) {
36
+ const foreignKeyValue = targetModel[foreignKeyCamelized]()
37
+
38
+ preloadCollections[foreignKeyValue].push(targetModel)
39
+ }
40
+
41
+ // Set the target preloaded models on the given models
42
+ for (const modelId in preloadCollections) {
43
+ const preloadedCollection = preloadCollections[modelId]
44
+
45
+ for (const model of modelsById[modelId]) {
46
+ const modelRelationship = model.getRelationshipByName(this.relationship.getRelationshipName())
47
+
48
+ modelRelationship.setPreloaded(true)
49
+ modelRelationship.addToLoaded(preloadedCollection)
50
+ }
51
+ }
52
+
53
+ return targetModels
54
+ }
55
+ }
@@ -0,0 +1,41 @@
1
+ import BelongsToPreloader from "./preloader/belongs-to.mjs"
2
+ import HasManyPreloader from "./preloader/has-many.mjs"
3
+ import restArgsError from "../../utils/rest-args-error.mjs"
4
+
5
+ export default class VelociousDatabaseQueryPreloader {
6
+ constructor({modelClass, models, preload, ...restArgs}) {
7
+ restArgsError(restArgs)
8
+
9
+ this.modelClass = modelClass
10
+ this.models = models
11
+ this.preload = preload
12
+ }
13
+
14
+ async run() {
15
+ for (const preloadRelationshipName in this.preload) {
16
+ const relationship = this.modelClass.getRelationshipByName(preloadRelationshipName)
17
+ let targetModels
18
+
19
+ if (relationship.getType() == "belongsTo") {
20
+ const hasManyPreloader = new BelongsToPreloader({models: this.models, relationship: relationship})
21
+
22
+ targetModels = await hasManyPreloader.run()
23
+ } else if (relationship.getType() == "hasMany") {
24
+ const hasManyPreloader = new HasManyPreloader({models: this.models, relationship: relationship})
25
+
26
+ targetModels = await hasManyPreloader.run()
27
+ } else {
28
+ throw new Error(`Unknown relationship type: ${relationship.getType()}`)
29
+ }
30
+
31
+ // Handle any further preloads in the tree
32
+ const newPreload = this.preload[preloadRelationshipName]
33
+
34
+ if (typeof newPreload == "object" && targetModels.length > 0) {
35
+ const preloader = new VelociousDatabaseQueryPreloader({modelClass: relationship.getTargetModelClass(), models: targetModels, preload: newPreload})
36
+
37
+ await preloader.run()
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,9 @@
1
+ export default class VelociousDatabaseQueryWhereBase {
2
+ getOptions() {
3
+ return this.query.getOptions()
4
+ }
5
+
6
+ toSql() {
7
+ throw new Error("'toSql' wasn't implemented")
8
+ }
9
+ }
@@ -0,0 +1,35 @@
1
+ import WhereBase from "./where-base.mjs"
2
+
3
+ export default class VelociousDatabaseQueryWhereHash extends WhereBase {
4
+ constructor(query, hash) {
5
+ super()
6
+ this.hash = hash
7
+ this.query = query
8
+ }
9
+
10
+ toSql() {
11
+ const options = this.getOptions()
12
+ let sql = "("
13
+ let index = 0
14
+
15
+ for (const whereKey in this.hash) {
16
+ const whereValue = this.hash[whereKey]
17
+
18
+ if (index > 0) sql += " AND "
19
+
20
+ sql += `${options.quoteColumnName(whereKey)}`
21
+
22
+ if (Array.isArray(whereValue)) {
23
+ sql += ` IN (${whereValue.map((value) => options.quote(value)).join(", ")})`
24
+ } else {
25
+ sql += ` = ${options.quote(whereValue)}`
26
+ }
27
+
28
+ index++
29
+ }
30
+
31
+ sql += ")"
32
+
33
+ return sql
34
+ }
35
+ }
@@ -0,0 +1,13 @@
1
+ import WhereBase from "./where-base.mjs"
2
+
3
+ export default class VelociousDatabaseQueryWhereHash extends WhereBase {
4
+ constructor(query, plain) {
5
+ super()
6
+ this.plain = plain
7
+ this.query = query
8
+ }
9
+
10
+ toSql() {
11
+ return this.plain
12
+ }
13
+ }