@rvoh/dream 2.18.1 → 2.19.0

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 (222) hide show
  1. package/dist/cjs/src/Dream.js +103 -0
  2. package/dist/cjs/src/db/DreamDbConnection.js +6 -0
  3. package/dist/cjs/src/decorators/class/SoftDelete.js +12 -0
  4. package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +113 -0
  5. package/dist/cjs/src/dream/Query.js +137 -0
  6. package/dist/cjs/src/dream/QueryDriver/Base.js +84 -0
  7. package/dist/cjs/src/dream/QueryDriver/Kysely.js +358 -67
  8. package/dist/cjs/src/errors/CannotNamespaceAssociationFilterToAnotherTable.js +27 -0
  9. package/dist/cjs/src/helpers/cli/generateMigrationContent.js +21 -13
  10. package/dist/esm/src/Dream.js +103 -0
  11. package/dist/esm/src/db/DreamDbConnection.js +6 -0
  12. package/dist/esm/src/decorators/class/SoftDelete.js +12 -0
  13. package/dist/esm/src/dream/DreamClassTransactionBuilder.js +113 -0
  14. package/dist/esm/src/dream/Query.js +137 -0
  15. package/dist/esm/src/dream/QueryDriver/Base.js +84 -0
  16. package/dist/esm/src/dream/QueryDriver/Kysely.js +358 -67
  17. package/dist/esm/src/errors/CannotNamespaceAssociationFilterToAnotherTable.js +27 -0
  18. package/dist/esm/src/helpers/cli/generateMigrationContent.js +21 -13
  19. package/dist/types/src/Dream.d.ts +97 -4
  20. package/dist/types/src/decorators/class/SoftDelete.d.ts +12 -0
  21. package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +103 -0
  22. package/dist/types/src/dream/Query.d.ts +127 -0
  23. package/dist/types/src/dream/QueryDriver/Base.d.ts +69 -0
  24. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +166 -8
  25. package/dist/types/src/errors/CannotNamespaceAssociationFilterToAnotherTable.d.ts +8 -0
  26. package/dist/types/src/types/associations/shared.d.ts +4 -1
  27. package/dist/types/src/types/dream.d.ts +17 -0
  28. package/dist/types/src/types/types/associations/shared.ts +10 -1
  29. package/dist/types/src/types/types/dream.ts +53 -0
  30. package/dist/types/src/types/types/variadic.ts +179 -140
  31. package/dist/types/src/types/variadic.d.ts +15 -10
  32. package/docs/assets/hierarchy.js +1 -1
  33. package/docs/assets/search.js +1 -1
  34. package/docs/classes/db.DreamMigrationHelpers.html +11 -11
  35. package/docs/classes/db.KyselyQueryDriver.html +78 -34
  36. package/docs/classes/db.PostgresQueryDriver.html +79 -35
  37. package/docs/classes/db.QueryDriverBase.html +77 -33
  38. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  39. package/docs/classes/errors.ColumnOverflow.html +3 -3
  40. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  41. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  42. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  43. package/docs/classes/errors.DecryptionError.html +2 -2
  44. package/docs/classes/errors.DecryptionParseError.html +2 -2
  45. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  46. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  47. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  48. package/docs/classes/errors.InvalidClockTime.html +2 -2
  49. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  50. package/docs/classes/errors.InvalidDateTime.html +2 -2
  51. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  52. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  53. package/docs/classes/errors.NotNullViolation.html +3 -3
  54. package/docs/classes/errors.RecordNotFound.html +3 -3
  55. package/docs/classes/errors.ValidationError.html +3 -3
  56. package/docs/classes/index.CalendarDate.html +33 -33
  57. package/docs/classes/index.ClockTime.html +32 -32
  58. package/docs/classes/index.ClockTimeTz.html +35 -35
  59. package/docs/classes/index.DateTime.html +86 -86
  60. package/docs/classes/index.Decorators.html +19 -19
  61. package/docs/classes/index.Dream.html +182 -119
  62. package/docs/classes/index.DreamApp.html +10 -10
  63. package/docs/classes/index.DreamTransaction.html +2 -2
  64. package/docs/classes/index.Env.html +2 -2
  65. package/docs/classes/index.Query.html +144 -57
  66. package/docs/classes/system.CliFileWriter.html +4 -4
  67. package/docs/classes/system.DreamBin.html +2 -2
  68. package/docs/classes/system.DreamCLI.html +7 -7
  69. package/docs/classes/system.DreamImporter.html +2 -2
  70. package/docs/classes/system.DreamLogos.html +2 -2
  71. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  72. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  73. package/docs/classes/system.PathHelpers.html +3 -3
  74. package/docs/classes/utils.Encrypt.html +3 -3
  75. package/docs/classes/utils.Range.html +2 -2
  76. package/docs/functions/db.closeAllDbConnections.html +1 -1
  77. package/docs/functions/db.dreamDbConnections.html +1 -1
  78. package/docs/functions/db.untypedDb.html +1 -1
  79. package/docs/functions/db.validateColumn.html +1 -1
  80. package/docs/functions/db.validateTable.html +1 -1
  81. package/docs/functions/errors.pgErrorType.html +1 -1
  82. package/docs/functions/index.DreamSerializer.html +1 -1
  83. package/docs/functions/index.ObjectSerializer.html +1 -1
  84. package/docs/functions/index.ReplicaSafe.html +1 -1
  85. package/docs/functions/index.STI.html +1 -1
  86. package/docs/functions/index.SoftDelete.html +12 -1
  87. package/docs/functions/utils.camelize.html +1 -1
  88. package/docs/functions/utils.capitalize.html +1 -1
  89. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  90. package/docs/functions/utils.compact.html +1 -1
  91. package/docs/functions/utils.groupBy.html +1 -1
  92. package/docs/functions/utils.hyphenize.html +1 -1
  93. package/docs/functions/utils.intersection.html +1 -1
  94. package/docs/functions/utils.isEmpty.html +1 -1
  95. package/docs/functions/utils.normalizeUnicode.html +1 -1
  96. package/docs/functions/utils.pascalize.html +1 -1
  97. package/docs/functions/utils.percent.html +1 -1
  98. package/docs/functions/utils.range.html +1 -1
  99. package/docs/functions/utils.round.html +1 -1
  100. package/docs/functions/utils.sanitizeString.html +1 -1
  101. package/docs/functions/utils.snakeify.html +1 -1
  102. package/docs/functions/utils.sort.html +1 -1
  103. package/docs/functions/utils.sortBy.html +1 -1
  104. package/docs/functions/utils.sortObjectByKey.html +1 -1
  105. package/docs/functions/utils.sortObjectByValue.html +1 -1
  106. package/docs/functions/utils.uncapitalize.html +1 -1
  107. package/docs/functions/utils.uniq.html +1 -1
  108. package/docs/hierarchy.html +1 -1
  109. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  110. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  111. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  112. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  113. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  114. package/docs/interfaces/types.DecoratorContext.html +2 -2
  115. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  116. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  117. package/docs/interfaces/types.DreamDbConfig.html +5 -5
  118. package/docs/interfaces/types.DurationObject.html +2 -2
  119. package/docs/interfaces/types.EncryptOptions.html +2 -2
  120. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  121. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  122. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  123. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  124. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  125. package/docs/types/openapi.OpenapiFormats.html +1 -1
  126. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  127. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  128. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  129. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  130. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  131. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  132. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  133. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  134. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  135. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  136. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  137. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  138. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  139. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  140. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  141. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  142. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  143. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  144. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  145. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  146. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  147. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  148. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  149. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  150. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  151. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  152. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  153. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  154. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  155. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  156. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  157. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  158. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  159. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  160. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  161. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  162. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  163. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  164. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  165. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  166. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  167. package/docs/types/types.CalendarDateObject.html +1 -1
  168. package/docs/types/types.Camelized.html +1 -1
  169. package/docs/types/types.ClockTimeObject.html +1 -1
  170. package/docs/types/types.DbConnectionType.html +1 -1
  171. package/docs/types/types.DbTypes.html +1 -1
  172. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  173. package/docs/types/types.DreamAttributes.html +1 -1
  174. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  175. package/docs/types/types.DreamClassColumn.html +1 -1
  176. package/docs/types/types.DreamColumn.html +1 -1
  177. package/docs/types/types.DreamColumnNames.html +1 -1
  178. package/docs/types/types.DreamLogLevel.html +1 -1
  179. package/docs/types/types.DreamLogger.html +2 -2
  180. package/docs/types/types.DreamModelSerializerType.html +1 -1
  181. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  182. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  183. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  184. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  185. package/docs/types/types.DreamSerializable.html +1 -1
  186. package/docs/types/types.DreamSerializableArray.html +1 -1
  187. package/docs/types/types.DreamSerializerKey.html +1 -1
  188. package/docs/types/types.DreamSerializers.html +1 -1
  189. package/docs/types/types.DreamVirtualColumns.html +1 -1
  190. package/docs/types/types.DurationUnit.html +1 -1
  191. package/docs/types/types.EncryptAlgorithm.html +1 -1
  192. package/docs/types/types.HasManyStatement.html +1 -1
  193. package/docs/types/types.HasOneStatement.html +1 -1
  194. package/docs/types/types.Hyphenized.html +1 -1
  195. package/docs/types/types.Pascalized.html +1 -1
  196. package/docs/types/types.PrimaryKeyType.html +1 -1
  197. package/docs/types/types.RoundingPrecision.html +1 -1
  198. package/docs/types/types.SerializerCasing.html +1 -1
  199. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  200. package/docs/types/types.Snakeified.html +1 -1
  201. package/docs/types/types.StrictInterface.html +1 -1
  202. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  203. package/docs/types/types.UpdateableProperties.html +1 -1
  204. package/docs/types/types.ValidationType.html +1 -1
  205. package/docs/types/types.ViewModel.html +2 -2
  206. package/docs/types/types.ViewModelClass.html +1 -1
  207. package/docs/types/types.WeekdayName.html +1 -1
  208. package/docs/types/types.WhereStatementForDream.html +1 -1
  209. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  210. package/docs/variables/index.DreamConst.html +1 -1
  211. package/docs/variables/index.ops.html +1 -1
  212. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  213. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  214. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  215. package/docs/variables/system.primaryKeyTypes.html +1 -1
  216. package/package.json +3 -3
  217. package/dist/cjs/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +0 -11
  218. package/dist/cjs/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +0 -17
  219. package/dist/esm/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +0 -11
  220. package/dist/esm/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +0 -17
  221. package/dist/types/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.d.ts +0 -13
  222. package/dist/types/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.d.ts +0 -12
@@ -0,0 +1,27 @@
1
+ export default class CannotNamespaceAssociationFilterToAnotherTable extends Error {
2
+ dreamClass;
3
+ key;
4
+ expectedAlias;
5
+ constructor(dreamClass, key, expectedAlias) {
6
+ super();
7
+ this.dreamClass = dreamClass;
8
+ this.key = key;
9
+ this.expectedAlias = expectedAlias;
10
+ }
11
+ get message() {
12
+ return `
13
+ Cannot filter on an association via a where-clause key namespaced to another table.
14
+
15
+ dream class: ${this.dreamClass.sanitizedName}
16
+ where-clause key: ${this.key}
17
+ table alias this where clause applies to: ${this.expectedAlias}
18
+
19
+ A Dream instance (or array of Dream instances) as a where-clause value resolves
20
+ an association on the model the where clause applies to, so the key may not be
21
+ namespaced to a different table. To filter a joined table by one of its
22
+ associations, pass the filter in the join's on-clause instead, e.g.:
23
+
24
+ .innerJoin('association', { and: { myBelongsToAssociation: instances } })
25
+ `;
26
+ }
27
+ }
@@ -8,8 +8,13 @@ import globalClassNameFromFullyQualifiedModelName from '../globalClassNameFromFu
8
8
  import snakeify from '../snakeify.js';
9
9
  import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
10
10
  const STI_TYPE_COLUMN_NAME = 'type';
11
- const DELETED_AT_COLUMN_NAME = 'deleted_at';
12
- const COLUMNS_TO_INDEX = [STI_TYPE_COLUMN_NAME, DELETED_AT_COLUMN_NAME];
11
+ // deleted_at is deliberately NOT in this list: the SoftDelete default scope's
12
+ // `WHERE deleted_at IS NULL` is unselective on healthy tables, Dream internals
13
+ // never issue a query a deleted_at index could serve, and the useful partial
14
+ // index alternatives are dialect-specific SQL the generator must not emit by
15
+ // default. See spec/unit/cli/generateMigrationContent.spec.ts
16
+ // ("deleted_at is deliberately NOT indexed") and the CHANGELOG.
17
+ const COLUMNS_TO_INDEX = [STI_TYPE_COLUMN_NAME];
13
18
  export default function generateMigrationContent({ connectionName = 'default', table, columnsWithTypes = [], primaryKeyType = 'bigserial', createOrAlter = 'create', stiChildClassName, softDelete = false, } = {}) {
14
19
  const altering = createOrAlter === 'alter';
15
20
  let requireCitextExtension = false;
@@ -43,13 +48,18 @@ export default function generateMigrationContent({ connectionName = 'default', t
43
48
  return acc;
44
49
  /**
45
50
  * Automatically set email columns to citext since different casings of
46
- * email address are the same email address
51
+ * email address are the same email address. Skip this when the user
52
+ * explicitly asked for an encrypted column, since encrypted columns
53
+ * are always stored as encrypted text and must not be overridden by
54
+ * the name-based heuristic.
47
55
  */
48
- const attributeType = /email$/.test(nonStandardAttributeName)
49
- ? 'citext'
50
- : /uuid$/.test(nonStandardAttributeName)
51
- ? 'uuid'
52
- : _attributeType;
56
+ const attributeType = _attributeType === 'encrypted' || _attributeType === 'encrypted[]'
57
+ ? _attributeType
58
+ : /email$/.test(nonStandardAttributeName)
59
+ ? 'citext'
60
+ : /uuid$/.test(nonStandardAttributeName)
61
+ ? 'uuid'
62
+ : _attributeType;
53
63
  const processedAttrType = camelize(attributeType)?.toLowerCase();
54
64
  const userWantsThisOptional = optionalFromDescriptors(descriptors);
55
65
  // when creating a migration for an STI child, we don't want to include notNull;
@@ -115,6 +125,7 @@ export default function generateMigrationContent({ connectionName = 'default', t
115
125
  attributeName = `encrypted_${attributeName}`;
116
126
  columnDefs.push(generateColumnStr(attributeName, 'text', descriptors, {
117
127
  omitInlineNonNull,
128
+ skipUniqueHeuristic: true,
118
129
  }));
119
130
  break;
120
131
  // TODO: determine if we need to support encrypted[] in the future
@@ -145,9 +156,6 @@ export default function generateMigrationContent({ connectionName = 'default', t
145
156
  }
146
157
  return acc;
147
158
  }, { columnDefs: [], columnDrops: [], indexDefs: [] });
148
- if (emitDeletedAtColumn) {
149
- indexDefs.push(columnIndexStatement(table, DELETED_AT_COLUMN_NAME));
150
- }
151
159
  if (!table) {
152
160
  return `\
153
161
  import { Kysely, sql } from 'kysely'
@@ -274,12 +282,12 @@ function generateDecimalStr(attributeName, { descriptors, omitInlineNonNull: opt
274
282
  : `'decimal(${scale}, ${precision})'`;
275
283
  return `.addColumn('${attributeName}', ${decimalStatement}${optional ? '' : `, col => ${columnModifiers}`})`;
276
284
  }
277
- function generateColumnStr(attributeName, attributeType, descriptors, { omitInlineNonNull: optional }) {
285
+ function generateColumnStr(attributeName, attributeType, descriptors, { omitInlineNonNull: optional, skipUniqueHeuristic = false, }) {
278
286
  let returnStr = `.addColumn('${attributeName}', ${attributeTypeString(attributeType)}`;
279
287
  const providedDefaultArg = descriptors.find(d => /^default\(/.test(d));
280
288
  const providedDefault = providedDefaultArg?.replace(/^default\(/, '')?.replace(/\)$/, '');
281
289
  const notNull = !optional;
282
- const isUnique = /(email|token|uuid)$/.test(attributeName);
290
+ const isUnique = !skipUniqueHeuristic && /(email|token|uuid)$/.test(attributeName);
283
291
  const hasExtraValues = providedDefault || notNull || isUnique;
284
292
  const isArray = /\[\]$/.test(attributeType);
285
293
  const needsJsonDefault = notNull && !isArray && (attributeType === 'jsonb' || attributeType === 'json');
@@ -716,6 +716,23 @@ export default class Dream {
716
716
  * @returns The number of records corresponding to this model
717
717
  */
718
718
  static count<T extends typeof Dream>(this: T): Promise<number>;
719
+ /**
720
+ * Retrieves the number of records in each group, keyed by the
721
+ * value of the provided group column (a SQL `GROUP BY`).
722
+ *
723
+ * ```ts
724
+ * await User.countBy('name')
725
+ * // Map(2) { 'fred' => 2, 'zed' => 1 }
726
+ * ```
727
+ *
728
+ * Only groups with at least one matching row appear in the Map; seed absent
729
+ * groups yourself with `map.get(key) ?? 0`. When the group column is nullable,
730
+ * records with a `null` value are grouped under a real `null` key.
731
+ *
732
+ * @param groupColumn - the column to group by
733
+ * @returns A Map from each present group value to the number of records in that group
734
+ */
735
+ static countBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], number>>;
719
736
  /**
720
737
  * Retrieves the max value of the specified column
721
738
  * for this model's records.
@@ -729,6 +746,25 @@ export default class Dream {
729
746
  * @returns the max value of the specified column for this model's records
730
747
  */
731
748
  static max<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
749
+ /**
750
+ * Retrieves the max value of the specified column within each group, keyed by
751
+ * the value of the provided group column (a SQL `GROUP BY`).
752
+ *
753
+ * ```ts
754
+ * await CompositionAsset.maxBy('name', 'score')
755
+ * // Map(2) { 'primary' => 9, 'secondary' => 4 }
756
+ * ```
757
+ *
758
+ * Only groups with at least one matching row appear in the Map. When the group
759
+ * column is nullable, records with a `null` value are grouped under a real
760
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
761
+ * value.
762
+ *
763
+ * @param groupColumn - the column to group by
764
+ * @param aggregatedColumn - the column to take the max of within each group
765
+ * @returns A Map from each present group value to the max of the aggregated column in that group
766
+ */
767
+ static maxBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
732
768
  /**
733
769
  * Retrieves the min value of the specified column
734
770
  * for this model's records.
@@ -743,6 +779,25 @@ export default class Dream {
743
779
  * @returns the min value of the specified column for this model's records
744
780
  */
745
781
  static min<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
782
+ /**
783
+ * Retrieves the min value of the specified column within each group, keyed by
784
+ * the value of the provided group column (a SQL `GROUP BY`).
785
+ *
786
+ * ```ts
787
+ * await CompositionAsset.minBy('name', 'score')
788
+ * // Map(2) { 'primary' => 1, 'secondary' => 4 }
789
+ * ```
790
+ *
791
+ * Only groups with at least one matching row appear in the Map. When the group
792
+ * column is nullable, records with a `null` value are grouped under a real
793
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
794
+ * value.
795
+ *
796
+ * @param groupColumn - the column to group by
797
+ * @param aggregatedColumn - the column to take the min of within each group
798
+ * @returns A Map from each present group value to the min of the aggregated column in that group
799
+ */
800
+ static minBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
746
801
  /**
747
802
  * Retrieves the sum for all values of the specified column
748
803
  * for this model's records.
@@ -757,6 +812,25 @@ export default class Dream {
757
812
  * @returns the sum for all values of the specified column for this model's records
758
813
  */
759
814
  static sum<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
815
+ /**
816
+ * Retrieves the sum of the specified column within each group, keyed by
817
+ * the value of the provided group column (a SQL `GROUP BY`).
818
+ *
819
+ * ```ts
820
+ * await CompositionAsset.sumBy('name', 'score')
821
+ * // Map(2) { 'primary' => 10, 'secondary' => 4 }
822
+ * ```
823
+ *
824
+ * Only groups with at least one matching row appear in the Map. When the group
825
+ * column is nullable, records with a `null` value are grouped under a real
826
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
827
+ * value.
828
+ *
829
+ * @param groupColumn - the column to group by
830
+ * @param aggregatedColumn - the column to sum within each group
831
+ * @returns A Map from each present group value to the sum of the aggregated column in that group
832
+ */
833
+ static sumBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
760
834
  /**
761
835
  * Retrieves the average for all values of the specified column
762
836
  * for this model's records.
@@ -771,6 +845,25 @@ export default class Dream {
771
845
  * @returns the average for all values of the specified column for this model's records
772
846
  */
773
847
  static avg<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
848
+ /**
849
+ * Retrieves the average of the specified column within each group, keyed by
850
+ * the value of the provided group column (a SQL `GROUP BY`).
851
+ *
852
+ * ```ts
853
+ * await CompositionAsset.avgBy('name', 'score')
854
+ * // Map(2) { 'primary' => 5, 'secondary' => 4 }
855
+ * ```
856
+ *
857
+ * Only groups with at least one matching row appear in the Map. When the group
858
+ * column is nullable, records with a `null` value are grouped under a real
859
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
860
+ * value.
861
+ *
862
+ * @param groupColumn - the column to group by
863
+ * @param aggregatedColumn - the column to average within each group
864
+ * @returns A Map from each present group value to the average of the aggregated column in that group
865
+ */
866
+ static avgBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
774
867
  /**
775
868
  * Persists a new record, setting the provided attributes.
776
869
  * Automatically sets createdAt and updatedAt timestamps.
@@ -2651,9 +2744,9 @@ export default class Dream {
2651
2744
  [x: string]: any;
2652
2745
  } | Partial<{
2653
2746
  [x: string]: any;
2654
- }> | Partial<{
2747
+ }> | {
2655
2748
  [x: string]: any;
2656
- }>>>;
2749
+ }>>;
2657
2750
  /**
2658
2751
  * Takes the attributes passed in and sets their values internally,
2659
2752
  * bypassing any custom setters defined for these attributes.
@@ -2670,9 +2763,9 @@ export default class Dream {
2670
2763
  [x: string]: any;
2671
2764
  } | Partial<{
2672
2765
  [x: string]: any;
2673
- }> | Partial<{
2766
+ }> | {
2674
2767
  [x: string]: any;
2675
- }>>>;
2768
+ }>>;
2676
2769
  private _setAttributes;
2677
2770
  /**
2678
2771
  * Saves the state of the current instance to the
@@ -34,5 +34,17 @@ export declare const SOFT_DELETE_SCOPE_NAME = "dream:SoftDelete";
34
34
  * return 'customDatetimeField' as const
35
35
  * }
36
36
  * }
37
+ *
38
+ * Note on indexing: Dream deliberately does not index `deleted_at`.
39
+ * The default scope's `WHERE deleted_at IS NULL` matches nearly every
40
+ * row on a healthy table, so a plain b-tree on the column is rarely
41
+ * chosen by the planner while still costing index size and write
42
+ * amplification, and Dream itself never issues a query such an index
43
+ * could serve. If your app needs one, add it yourself based on your
44
+ * own access patterns — on Postgres, the two useful shapes are a
45
+ * composite partial index on your hot lookup columns with
46
+ * `WHERE deleted_at IS NULL` (fast scoped reads), or a partial index
47
+ * `WHERE deleted_at IS NOT NULL` (purge/GC sweeps over soft-deleted
48
+ * rows). Both are Postgres-specific syntax.
37
49
  */
38
50
  export default function SoftDelete(): (target: typeof Dream) => void;
@@ -56,6 +56,25 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
56
56
  * @returns The number of records corresponding to this model
57
57
  */
58
58
  count<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>>(this: I): Promise<number>;
59
+ /**
60
+ * Retrieves the number of records in each group, keyed by the
61
+ * value of the provided group column (a SQL `GROUP BY`).
62
+ *
63
+ * ```ts
64
+ * await ApplicationModel.transaction(async txn => {
65
+ * await User.txn(txn).countBy('name')
66
+ * // Map(2) { 'fred' => 2, 'zed' => 1 }
67
+ * })
68
+ * ```
69
+ *
70
+ * Only groups with at least one matching row appear in the Map; seed absent
71
+ * groups yourself with `map.get(key) ?? 0`. When the group column is nullable,
72
+ * records with a `null` value are grouped under a real `null` key.
73
+ *
74
+ * @param groupColumn - the column to group by
75
+ * @returns A Map from each present group value to the number of records in that group
76
+ */
77
+ countBy<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, T extends DreamColumnNames<DreamInstance>>(this: I, groupColumn: T): Promise<Map<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]], number>>;
59
78
  /**
60
79
  * Returns a new Query instance, specifying a limit
61
80
  *
@@ -99,6 +118,27 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
99
118
  * @returns The max value of the specified column for this model's records
100
119
  */
101
120
  max<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, T extends DreamColumnNames<DreamInstance>>(this: I, columnName: T): Promise<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>;
121
+ /**
122
+ * Retrieves the max value of the specified column within each group, keyed by
123
+ * the value of the provided group column (a SQL `GROUP BY`).
124
+ *
125
+ * ```ts
126
+ * await ApplicationModel.transaction(async txn => {
127
+ * await CompositionAsset.txn(txn).maxBy('name', 'score')
128
+ * // Map(2) { 'primary' => 9, 'secondary' => 4 }
129
+ * })
130
+ * ```
131
+ *
132
+ * Only groups with at least one matching row appear in the Map. When the group
133
+ * column is nullable, records with a `null` value are grouped under a real
134
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
135
+ * value.
136
+ *
137
+ * @param groupColumn - the column to group by
138
+ * @param aggregatedColumn - the column to take the max of within each group
139
+ * @returns A Map from each present group value to the max of the aggregated column in that group
140
+ */
141
+ maxBy<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, GroupColumnName extends DreamColumnNames<DreamInstance>, AggregatedColumnName extends DreamColumnNames<DreamInstance>>(this: I, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]], DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>>;
102
142
  /**
103
143
  * Retrieves the min value of the specified column
104
144
  * for this model's records.
@@ -114,6 +154,27 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
114
154
  * @returns The min value of the specified column for this model's records
115
155
  */
116
156
  min<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, T extends DreamColumnNames<DreamInstance>>(this: I, columnName: T): Promise<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>;
157
+ /**
158
+ * Retrieves the min value of the specified column within each group, keyed by
159
+ * the value of the provided group column (a SQL `GROUP BY`).
160
+ *
161
+ * ```ts
162
+ * await ApplicationModel.transaction(async txn => {
163
+ * await CompositionAsset.txn(txn).minBy('name', 'score')
164
+ * // Map(2) { 'primary' => 1, 'secondary' => 4 }
165
+ * })
166
+ * ```
167
+ *
168
+ * Only groups with at least one matching row appear in the Map. When the group
169
+ * column is nullable, records with a `null` value are grouped under a real
170
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
171
+ * value.
172
+ *
173
+ * @param groupColumn - the column to group by
174
+ * @param aggregatedColumn - the column to take the min of within each group
175
+ * @returns A Map from each present group value to the min of the aggregated column in that group
176
+ */
177
+ minBy<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, GroupColumnName extends DreamColumnNames<DreamInstance>, AggregatedColumnName extends DreamColumnNames<DreamInstance>>(this: I, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]], DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>>;
117
178
  /**
118
179
  * Retrieves the sum value of the specified column
119
180
  * for this Query
@@ -127,6 +188,27 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
127
188
  * @returns the sum of the values of the specified column for this Query
128
189
  */
129
190
  sum<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, T extends DreamColumnNames<DreamInstance>>(this: I, columnName: T): Promise<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>;
191
+ /**
192
+ * Retrieves the sum of the specified column within each group, keyed by
193
+ * the value of the provided group column (a SQL `GROUP BY`).
194
+ *
195
+ * ```ts
196
+ * await ApplicationModel.transaction(async txn => {
197
+ * await CompositionAsset.txn(txn).sumBy('name', 'score')
198
+ * // Map(2) { 'primary' => 10, 'secondary' => 4 }
199
+ * })
200
+ * ```
201
+ *
202
+ * Only groups with at least one matching row appear in the Map. When the group
203
+ * column is nullable, records with a `null` value are grouped under a real
204
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
205
+ * value.
206
+ *
207
+ * @param groupColumn - the column to group by
208
+ * @param aggregatedColumn - the column to sum within each group
209
+ * @returns A Map from each present group value to the sum of the aggregated column in that group
210
+ */
211
+ sumBy<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, GroupColumnName extends DreamColumnNames<DreamInstance>, AggregatedColumnName extends DreamColumnNames<DreamInstance>>(this: I, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]], DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>>;
130
212
  /**
131
213
  * Retrieves the average value of the specified column
132
214
  * for this Query
@@ -140,6 +222,27 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
140
222
  * @returns the average of the values of the specified column for this Query
141
223
  */
142
224
  avg<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, T extends DreamColumnNames<DreamInstance>>(this: I, columnName: T): Promise<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][T & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>;
225
+ /**
226
+ * Retrieves the average of the specified column within each group, keyed by
227
+ * the value of the provided group column (a SQL `GROUP BY`).
228
+ *
229
+ * ```ts
230
+ * await ApplicationModel.transaction(async txn => {
231
+ * await CompositionAsset.txn(txn).avgBy('name', 'score')
232
+ * // Map(2) { 'primary' => 5, 'secondary' => 4 }
233
+ * })
234
+ * ```
235
+ *
236
+ * Only groups with at least one matching row appear in the Map. When the group
237
+ * column is nullable, records with a `null` value are grouped under a real
238
+ * `null` key; a group whose aggregated values are all `null` yields a `null`
239
+ * value.
240
+ *
241
+ * @param groupColumn - the column to group by
242
+ * @param aggregatedColumn - the column to average within each group
243
+ * @returns A Map from each present group value to the average of the aggregated column in that group
244
+ */
245
+ avgBy<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>, GroupColumnName extends DreamColumnNames<DreamInstance>, AggregatedColumnName extends DreamColumnNames<DreamInstance>>(this: I, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][GroupColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]], DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]["coercedType" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]][AggregatedColumnName & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]["columns" & keyof DreamInstance["schema"][DreamInstance["table"] & keyof DreamInstance["schema"]]]]]>>;
143
246
  /**
144
247
  * Persists a new record, setting the provided attributes.
145
248
  * Automatically sets createdAt and updatedAt timestamps.
@@ -697,6 +697,37 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
697
697
  * @returns The number of records matching the Query
698
698
  */
699
699
  count(): Promise<number>;
700
+ /**
701
+ * Retrieves the number of records in each group, keyed by the
702
+ * value of the provided group column (a SQL `GROUP BY`).
703
+ *
704
+ * ```ts
705
+ * await User.query().countBy('name')
706
+ * // Map(2) { 'fred' => 2, 'zed' => 1 }
707
+ *
708
+ * await User.where({ email: ops.ilike('%gmail.com') }).countBy('name')
709
+ * // Map(1) { 'fred' => 3 }
710
+ * ```
711
+ *
712
+ * Any `where` clauses, base scopes (soft-delete / STI), and joined-association
713
+ * `and` clauses on the Query carry through automatically. A joined-association
714
+ * column may be grouped on by passing its namespaced name (e.g.
715
+ * `'compositionAssets.name'`).
716
+ *
717
+ * Only groups with at least one matching row appear in the Map (inherent to
718
+ * `GROUP BY`); seed absent groups yourself with `map.get(key) ?? 0`. When the
719
+ * group column is nullable, records with a `null` value are grouped under a real
720
+ * `null` key.
721
+ *
722
+ * ```ts
723
+ * await Composition.query().innerJoin('compositionAssets').countBy('compositionAssets.name')
724
+ * // Map(2) { 'primary' => 3, null => 1 }
725
+ * ```
726
+ *
727
+ * @param groupColumn - the column to group by (base or joined-association-namespaced)
728
+ * @returns A Map from each present group value to the number of records in that group
729
+ */
730
+ countBy<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], GroupColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, GroupKey extends NamespacedOrBaseModelColumnTypes<[GroupColumnName], Q, DreamInstance>[0]>(groupColumn: GroupColumnName): Promise<Map<GroupKey, number>>;
700
731
  /**
701
732
  * Returns new Query with distinct clause applied.
702
733
  * If no column is specified, applies distinct to the primary key.
@@ -739,6 +770,30 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
739
770
  *
740
771
  */
741
772
  max<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], ColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, ReturnType extends NamespacedOrBaseModelColumnTypes<[ColumnName], Q, DreamInstance>[0]>(columnName: ColumnName): Promise<ReturnType>;
773
+ /**
774
+ * Retrieves the max value of the specified column within each group, keyed by
775
+ * the value of the provided group column (a SQL `GROUP BY`).
776
+ *
777
+ * ```ts
778
+ * await CompositionAsset.query().maxBy('name', 'score')
779
+ * // Map(2) { 'primary' => 9, 'secondary' => 4 }
780
+ * ```
781
+ *
782
+ * Any `where` clauses, base scopes (soft-delete / STI), and joined-association
783
+ * `and` clauses on the Query carry through automatically. A joined-association
784
+ * column may be grouped on or aggregated by passing its namespaced name (e.g.
785
+ * `'compositionAssets.name'`).
786
+ *
787
+ * Only groups with at least one matching row appear in the Map (inherent to
788
+ * `GROUP BY`). When the group column is nullable, records with a `null` value
789
+ * are grouped under a real `null` key; a group whose aggregated values are all
790
+ * `null` yields a `null` value.
791
+ *
792
+ * @param groupColumn - the column to group by (base or joined-association-namespaced)
793
+ * @param aggregatedColumn - the column to take the max of within each group
794
+ * @returns A Map from each present group value to the max of the aggregated column in that group
795
+ */
796
+ maxBy<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], GroupColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, AggregatedColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, GroupKey extends NamespacedOrBaseModelColumnTypes<[GroupColumnName], Q, DreamInstance>[0], AggregatedType extends NamespacedOrBaseModelColumnTypes<[AggregatedColumnName], Q, DreamInstance>[0]>(groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<GroupKey, AggregatedType>>;
742
797
  /**
743
798
  * Retrieves the min value of the specified column
744
799
  * for this Query
@@ -752,6 +807,30 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
752
807
  * @returns the min value of the specified column for this Query
753
808
  */
754
809
  min<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], ColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, ReturnType extends NamespacedOrBaseModelColumnTypes<[ColumnName], Q, DreamInstance>[0]>(columnName: ColumnName): Promise<ReturnType>;
810
+ /**
811
+ * Retrieves the min value of the specified column within each group, keyed by
812
+ * the value of the provided group column (a SQL `GROUP BY`).
813
+ *
814
+ * ```ts
815
+ * await CompositionAsset.query().minBy('name', 'score')
816
+ * // Map(2) { 'primary' => 1, 'secondary' => 4 }
817
+ * ```
818
+ *
819
+ * Any `where` clauses, base scopes (soft-delete / STI), and joined-association
820
+ * `and` clauses on the Query carry through automatically. A joined-association
821
+ * column may be grouped on or aggregated by passing its namespaced name (e.g.
822
+ * `'compositionAssets.name'`).
823
+ *
824
+ * Only groups with at least one matching row appear in the Map (inherent to
825
+ * `GROUP BY`). When the group column is nullable, records with a `null` value
826
+ * are grouped under a real `null` key; a group whose aggregated values are all
827
+ * `null` yields a `null` value.
828
+ *
829
+ * @param groupColumn - the column to group by (base or joined-association-namespaced)
830
+ * @param aggregatedColumn - the column to take the min of within each group
831
+ * @returns A Map from each present group value to the min of the aggregated column in that group
832
+ */
833
+ minBy<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], GroupColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, AggregatedColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, GroupKey extends NamespacedOrBaseModelColumnTypes<[GroupColumnName], Q, DreamInstance>[0], AggregatedType extends NamespacedOrBaseModelColumnTypes<[AggregatedColumnName], Q, DreamInstance>[0]>(groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<GroupKey, AggregatedType>>;
755
834
  /**
756
835
  * Retrieves the sum value of the specified column
757
836
  * for this Query
@@ -765,6 +844,30 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
765
844
  * @returns the sum of the values of the specified column for this Query
766
845
  */
767
846
  sum<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], ColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, ReturnType extends NamespacedOrBaseModelColumnTypes<[ColumnName], Q, DreamInstance>[0]>(columnName: ColumnName): Promise<ReturnType>;
847
+ /**
848
+ * Retrieves the sum of the specified column within each group, keyed by
849
+ * the value of the provided group column (a SQL `GROUP BY`).
850
+ *
851
+ * ```ts
852
+ * await CompositionAsset.query().sumBy('name', 'score')
853
+ * // Map(2) { 'primary' => 10, 'secondary' => 4 }
854
+ * ```
855
+ *
856
+ * Any `where` clauses, base scopes (soft-delete / STI), and joined-association
857
+ * `and` clauses on the Query carry through automatically. A joined-association
858
+ * column may be grouped on or aggregated by passing its namespaced name (e.g.
859
+ * `'compositionAssets.name'`).
860
+ *
861
+ * Only groups with at least one matching row appear in the Map (inherent to
862
+ * `GROUP BY`). When the group column is nullable, records with a `null` value
863
+ * are grouped under a real `null` key; a group whose aggregated values are all
864
+ * `null` yields a `null` value.
865
+ *
866
+ * @param groupColumn - the column to group by (base or joined-association-namespaced)
867
+ * @param aggregatedColumn - the column to sum within each group
868
+ * @returns A Map from each present group value to the sum of the aggregated column in that group
869
+ */
870
+ sumBy<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], GroupColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, AggregatedColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, GroupKey extends NamespacedOrBaseModelColumnTypes<[GroupColumnName], Q, DreamInstance>[0], AggregatedType extends NamespacedOrBaseModelColumnTypes<[AggregatedColumnName], Q, DreamInstance>[0]>(groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<GroupKey, AggregatedType>>;
768
871
  /**
769
872
  * Retrieves the average value of the specified column
770
873
  * for this Query
@@ -778,6 +881,30 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
778
881
  * @returns the average of the values of the specified column for this Query
779
882
  */
780
883
  avg<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], ColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, ReturnType extends NamespacedOrBaseModelColumnTypes<[ColumnName], Q, DreamInstance>[0]>(columnName: ColumnName): Promise<ReturnType>;
884
+ /**
885
+ * Retrieves the average of the specified column within each group, keyed by
886
+ * the value of the provided group column (a SQL `GROUP BY`).
887
+ *
888
+ * ```ts
889
+ * await CompositionAsset.query().avgBy('name', 'score')
890
+ * // Map(2) { 'primary' => 5, 'secondary' => 4 }
891
+ * ```
892
+ *
893
+ * Any `where` clauses, base scopes (soft-delete / STI), and joined-association
894
+ * `and` clauses on the Query carry through automatically. A joined-association
895
+ * column may be grouped on or aggregated by passing its namespaced name (e.g.
896
+ * `'compositionAssets.name'`).
897
+ *
898
+ * Only groups with at least one matching row appear in the Map (inherent to
899
+ * `GROUP BY`). When the group column is nullable, records with a `null` value
900
+ * are grouped under a real `null` key; a group whose aggregated values are all
901
+ * `null` yields a `null` value.
902
+ *
903
+ * @param groupColumn - the column to group by (base or joined-association-namespaced)
904
+ * @param aggregatedColumn - the column to average within each group
905
+ * @returns A Map from each present group value to the average of the aggregated column in that group
906
+ */
907
+ avgBy<Q extends Query<DreamInstance, QueryTypeOpts>, DB extends DreamInstance['DB'], GroupColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, AggregatedColumnName extends ColumnNamesAccountingForJoinedAssociations<Q['queryTypeOpts']['joinedAssociations'], DB, QueryTypeOpts['rootTableName'], QueryTypeOpts['rootTableAlias']>, GroupKey extends NamespacedOrBaseModelColumnTypes<[GroupColumnName], Q, DreamInstance>[0], AggregatedType extends NamespacedOrBaseModelColumnTypes<[AggregatedColumnName], Q, DreamInstance>[0]>(groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<GroupKey, AggregatedType>>;
781
908
  /**
782
909
  * Plucks the provided fields from the given dream class table
783
910
  *