@rvoh/dream 2.18.1 → 2.20.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.
- package/dist/cjs/src/Dream.js +186 -0
- package/dist/cjs/src/db/DreamDbConnection.js +6 -0
- package/dist/cjs/src/decorators/class/SoftDelete.js +12 -0
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +113 -0
- package/dist/cjs/src/dream/Query.js +148 -0
- package/dist/cjs/src/dream/QueryDriver/Base.js +84 -0
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +420 -81
- package/dist/cjs/src/dream/internal/filterRowToKnownColumns.js +41 -0
- package/dist/cjs/src/dream/internal/saveDream.js +6 -1
- package/dist/cjs/src/dream/internal/sqlResultToDreamInstance.js +9 -2
- package/dist/cjs/src/errors/CannotNamespaceAssociationFilterToAnotherTable.js +27 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreAssociationColumn.js +38 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreEncryptedColumn.js +20 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnorePrimaryKey.js +18 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreSoftDeleteColumn.js +21 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreSortablePositionColumn.js +20 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreSortableScopeColumn.js +24 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreStiTypeColumn.js +17 -0
- package/dist/cjs/src/errors/schema-builder/ConflictingIgnoredColumns.js +27 -0
- package/dist/cjs/src/errors/schema-builder/IgnoredColumnMustBeCamelCase.js +21 -0
- package/dist/cjs/src/helpers/cli/ASTConnectionBuilder.js +38 -1
- package/dist/cjs/src/helpers/cli/ASTKyselyCodegenEnhancer.js +60 -0
- package/dist/cjs/src/helpers/cli/generateMigrationContent.js +21 -13
- package/dist/cjs/src/helpers/cli/resolveIgnoredColumns.js +198 -0
- package/dist/esm/src/Dream.js +186 -0
- package/dist/esm/src/db/DreamDbConnection.js +6 -0
- package/dist/esm/src/decorators/class/SoftDelete.js +12 -0
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +113 -0
- package/dist/esm/src/dream/Query.js +148 -0
- package/dist/esm/src/dream/QueryDriver/Base.js +84 -0
- package/dist/esm/src/dream/QueryDriver/Kysely.js +420 -81
- package/dist/esm/src/dream/internal/filterRowToKnownColumns.js +41 -0
- package/dist/esm/src/dream/internal/saveDream.js +6 -1
- package/dist/esm/src/dream/internal/sqlResultToDreamInstance.js +9 -2
- package/dist/esm/src/errors/CannotNamespaceAssociationFilterToAnotherTable.js +27 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreAssociationColumn.js +38 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreEncryptedColumn.js +20 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnorePrimaryKey.js +18 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreSoftDeleteColumn.js +21 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreSortablePositionColumn.js +20 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreSortableScopeColumn.js +24 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreStiTypeColumn.js +17 -0
- package/dist/esm/src/errors/schema-builder/ConflictingIgnoredColumns.js +27 -0
- package/dist/esm/src/errors/schema-builder/IgnoredColumnMustBeCamelCase.js +21 -0
- package/dist/esm/src/helpers/cli/ASTConnectionBuilder.js +38 -1
- package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +60 -0
- package/dist/esm/src/helpers/cli/generateMigrationContent.js +21 -13
- package/dist/esm/src/helpers/cli/resolveIgnoredColumns.js +198 -0
- package/dist/types/src/Dream.d.ts +178 -4
- package/dist/types/src/decorators/class/SoftDelete.d.ts +12 -0
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +103 -0
- package/dist/types/src/dream/Query.d.ts +138 -0
- package/dist/types/src/dream/QueryDriver/Base.d.ts +69 -0
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +187 -8
- package/dist/types/src/dream/internal/filterRowToKnownColumns.d.ts +30 -0
- package/dist/types/src/errors/CannotNamespaceAssociationFilterToAnotherTable.d.ts +8 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreAssociationColumn.d.ts +12 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreEncryptedColumn.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/CannotIgnorePrimaryKey.d.ts +6 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreSoftDeleteColumn.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreSortablePositionColumn.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreSortableScopeColumn.d.ts +8 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreStiTypeColumn.d.ts +6 -0
- package/dist/types/src/errors/schema-builder/ConflictingIgnoredColumns.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/IgnoredColumnMustBeCamelCase.d.ts +7 -0
- package/dist/types/src/helpers/cli/ASTConnectionBuilder.d.ts +20 -0
- package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +13 -0
- package/dist/types/src/helpers/cli/resolveIgnoredColumns.d.ts +41 -0
- package/dist/types/src/types/associations/shared.d.ts +4 -1
- package/dist/types/src/types/dream.d.ts +17 -0
- package/dist/types/src/types/types/associations/shared.ts +10 -1
- package/dist/types/src/types/types/dream.ts +53 -0
- package/dist/types/src/types/types/variadic.ts +179 -140
- package/dist/types/src/types/variadic.d.ts +15 -10
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +96 -34
- package/docs/classes/db.PostgresQueryDriver.html +97 -35
- package/docs/classes/db.QueryDriverBase.html +77 -33
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.DecryptionError.html +2 -2
- package/docs/classes/errors.DecryptionParseError.html +2 -2
- package/docs/classes/errors.DecryptionRotationError.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.InvalidClockTime.html +2 -2
- package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
- package/docs/classes/errors.InvalidDateTime.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +33 -33
- package/docs/classes/index.ClockTime.html +32 -32
- package/docs/classes/index.ClockTimeTz.html +35 -35
- package/docs/classes/index.DateTime.html +86 -86
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +247 -119
- package/docs/classes/index.DreamApp.html +10 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +155 -57
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +7 -7
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +3 -3
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +12 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +5 -5
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.CalendarDateDurationUnit.html +1 -1
- package/docs/types/types.CalendarDateObject.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.ClockTimeObject.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +2 -2
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.DurationUnit.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.PrimaryKeyType.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +2 -2
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WeekdayName.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/system.primaryKeyTypes.html +1 -1
- package/package.json +3 -3
- package/dist/cjs/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +0 -11
- package/dist/cjs/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +0 -17
- package/dist/esm/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +0 -11
- package/dist/esm/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +0 -17
- package/dist/types/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.d.ts +0 -13
- package/dist/types/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.d.ts +0 -12
package/dist/cjs/src/Dream.js
CHANGED
|
@@ -976,6 +976,25 @@ export default class Dream {
|
|
|
976
976
|
static async count() {
|
|
977
977
|
return await this.query().count();
|
|
978
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Retrieves the number of records in each group, keyed by the
|
|
981
|
+
* value of the provided group column (a SQL `GROUP BY`).
|
|
982
|
+
*
|
|
983
|
+
* ```ts
|
|
984
|
+
* await User.countBy('name')
|
|
985
|
+
* // Map(2) { 'fred' => 2, 'zed' => 1 }
|
|
986
|
+
* ```
|
|
987
|
+
*
|
|
988
|
+
* Only groups with at least one matching row appear in the Map; seed absent
|
|
989
|
+
* groups yourself with `map.get(key) ?? 0`. When the group column is nullable,
|
|
990
|
+
* records with a `null` value are grouped under a real `null` key.
|
|
991
|
+
*
|
|
992
|
+
* @param groupColumn - the column to group by
|
|
993
|
+
* @returns A Map from each present group value to the number of records in that group
|
|
994
|
+
*/
|
|
995
|
+
static async countBy(groupColumn) {
|
|
996
|
+
return await this.query().countBy(groupColumn);
|
|
997
|
+
}
|
|
979
998
|
/**
|
|
980
999
|
* Retrieves the max value of the specified column
|
|
981
1000
|
* for this model's records.
|
|
@@ -991,6 +1010,27 @@ export default class Dream {
|
|
|
991
1010
|
static async max(columnName) {
|
|
992
1011
|
return await this.query().max(columnName);
|
|
993
1012
|
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Retrieves the max value of the specified column within each group, keyed by
|
|
1015
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1016
|
+
*
|
|
1017
|
+
* ```ts
|
|
1018
|
+
* await CompositionAsset.maxBy('name', 'score')
|
|
1019
|
+
* // Map(2) { 'primary' => 9, 'secondary' => 4 }
|
|
1020
|
+
* ```
|
|
1021
|
+
*
|
|
1022
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
1023
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
1024
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
1025
|
+
* value.
|
|
1026
|
+
*
|
|
1027
|
+
* @param groupColumn - the column to group by
|
|
1028
|
+
* @param aggregatedColumn - the column to take the max of within each group
|
|
1029
|
+
* @returns A Map from each present group value to the max of the aggregated column in that group
|
|
1030
|
+
*/
|
|
1031
|
+
static async maxBy(groupColumn, aggregatedColumn) {
|
|
1032
|
+
return await this.query().maxBy(groupColumn, aggregatedColumn);
|
|
1033
|
+
}
|
|
994
1034
|
/**
|
|
995
1035
|
* Retrieves the min value of the specified column
|
|
996
1036
|
* for this model's records.
|
|
@@ -1007,6 +1047,27 @@ export default class Dream {
|
|
|
1007
1047
|
static async min(columnName) {
|
|
1008
1048
|
return await this.query().min(columnName);
|
|
1009
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Retrieves the min value of the specified column within each group, keyed by
|
|
1052
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1053
|
+
*
|
|
1054
|
+
* ```ts
|
|
1055
|
+
* await CompositionAsset.minBy('name', 'score')
|
|
1056
|
+
* // Map(2) { 'primary' => 1, 'secondary' => 4 }
|
|
1057
|
+
* ```
|
|
1058
|
+
*
|
|
1059
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
1060
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
1061
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
1062
|
+
* value.
|
|
1063
|
+
*
|
|
1064
|
+
* @param groupColumn - the column to group by
|
|
1065
|
+
* @param aggregatedColumn - the column to take the min of within each group
|
|
1066
|
+
* @returns A Map from each present group value to the min of the aggregated column in that group
|
|
1067
|
+
*/
|
|
1068
|
+
static async minBy(groupColumn, aggregatedColumn) {
|
|
1069
|
+
return await this.query().minBy(groupColumn, aggregatedColumn);
|
|
1070
|
+
}
|
|
1010
1071
|
/**
|
|
1011
1072
|
* Retrieves the sum for all values of the specified column
|
|
1012
1073
|
* for this model's records.
|
|
@@ -1023,6 +1084,27 @@ export default class Dream {
|
|
|
1023
1084
|
static async sum(columnName) {
|
|
1024
1085
|
return await this.query().sum(columnName);
|
|
1025
1086
|
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Retrieves the sum of the specified column within each group, keyed by
|
|
1089
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1090
|
+
*
|
|
1091
|
+
* ```ts
|
|
1092
|
+
* await CompositionAsset.sumBy('name', 'score')
|
|
1093
|
+
* // Map(2) { 'primary' => 10, 'secondary' => 4 }
|
|
1094
|
+
* ```
|
|
1095
|
+
*
|
|
1096
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
1097
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
1098
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
1099
|
+
* value.
|
|
1100
|
+
*
|
|
1101
|
+
* @param groupColumn - the column to group by
|
|
1102
|
+
* @param aggregatedColumn - the column to sum within each group
|
|
1103
|
+
* @returns A Map from each present group value to the sum of the aggregated column in that group
|
|
1104
|
+
*/
|
|
1105
|
+
static async sumBy(groupColumn, aggregatedColumn) {
|
|
1106
|
+
return await this.query().sumBy(groupColumn, aggregatedColumn);
|
|
1107
|
+
}
|
|
1026
1108
|
/**
|
|
1027
1109
|
* Retrieves the average for all values of the specified column
|
|
1028
1110
|
* for this model's records.
|
|
@@ -1039,6 +1121,27 @@ export default class Dream {
|
|
|
1039
1121
|
static async avg(columnName) {
|
|
1040
1122
|
return await this.query().avg(columnName);
|
|
1041
1123
|
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Retrieves the average of the specified column within each group, keyed by
|
|
1126
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1127
|
+
*
|
|
1128
|
+
* ```ts
|
|
1129
|
+
* await CompositionAsset.avgBy('name', 'score')
|
|
1130
|
+
* // Map(2) { 'primary' => 5, 'secondary' => 4 }
|
|
1131
|
+
* ```
|
|
1132
|
+
*
|
|
1133
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
1134
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
1135
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
1136
|
+
* value.
|
|
1137
|
+
*
|
|
1138
|
+
* @param groupColumn - the column to group by
|
|
1139
|
+
* @param aggregatedColumn - the column to average within each group
|
|
1140
|
+
* @returns A Map from each present group value to the average of the aggregated column in that group
|
|
1141
|
+
*/
|
|
1142
|
+
static async avgBy(groupColumn, aggregatedColumn) {
|
|
1143
|
+
return await this.query().avgBy(groupColumn, aggregatedColumn);
|
|
1144
|
+
}
|
|
1042
1145
|
/**
|
|
1043
1146
|
* Persists a new record, setting the provided attributes.
|
|
1044
1147
|
* Automatically sets createdAt and updatedAt timestamps.
|
|
@@ -1358,6 +1461,12 @@ export default class Dream {
|
|
|
1358
1461
|
* 3. each nested association will result in an additional record which duplicates data from the outer record. E.g., given `.leftJoinPreload('a', 'b', 'c')`, if each `a` has 10 `b` and each `b` has 10 `c`, then for one `a`, 100 records will be returned, each of which has all of the columns of `a`. `.preload('a', 'b', 'c')` would perform three separate SQL queries, but the data for a single `a` would only be returned once.
|
|
1359
1462
|
* 4. the individual query becomes more complex the more associations are included
|
|
1360
1463
|
* 5. associations loading associations loading associations could result in exponential amounts of data; in those cases, `.preload(...).findEach(...)` avoids instantiating massive amounts of data at once
|
|
1464
|
+
* 6. leftJoinPreload must enumerate every compiled column of every joined
|
|
1465
|
+
* model, so unlike base-model reads, `preload`/`load`, and saves, it does
|
|
1466
|
+
* not tolerate schema/image skew from an unplanned column drop during a
|
|
1467
|
+
* rolling deploy; see {@link Query.leftJoinPreload} and the
|
|
1468
|
+
* `ignoredColumns` getter for the two-deploy process that makes a
|
|
1469
|
+
* planned drop safe.
|
|
1361
1470
|
*
|
|
1362
1471
|
* ```ts
|
|
1363
1472
|
* const user = await User.leftJoinPreload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
|
|
@@ -2237,6 +2346,83 @@ export default class Dream {
|
|
|
2237
2346
|
get table() {
|
|
2238
2347
|
throw new DreamMissingRequiredOverride(this.constructor, 'table');
|
|
2239
2348
|
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Columns that Dream should behave as though they do not exist.
|
|
2351
|
+
*
|
|
2352
|
+
* Declaring a column ignored removes it from the generated types the next
|
|
2353
|
+
* time `sync` runs: it is omitted from both the db types file (the Kysely
|
|
2354
|
+
* `DB` interface) and the dream schema file, so it disappears from
|
|
2355
|
+
* `columns()` and from every place that flows from `columns()` — select
|
|
2356
|
+
* lists built for `preload`/`load` and `leftJoinPreload`, save hydration,
|
|
2357
|
+
* attribute definition, and param safety. References to the column in
|
|
2358
|
+
* application code become type errors, which is the point: they must be
|
|
2359
|
+
* removed before the column can be dropped.
|
|
2360
|
+
*
|
|
2361
|
+
* This enables safely dropping a column under rolling deploys — the same
|
|
2362
|
+
* problem Rails solves with `ignored_columns`. The safety requirement is
|
|
2363
|
+
* that no image that can run against the post-drop schema names the
|
|
2364
|
+
* column in any SQL it generates. To satisfy it: remove all application
|
|
2365
|
+
* code that uses the column, declare it here, and run `sync`; then let
|
|
2366
|
+
* the drop migration run only once no image lacking the declaration can
|
|
2367
|
+
* run against the database — whether the migration ships in a later
|
|
2368
|
+
* deploy of its own, or together with the declaration in a pipeline that
|
|
2369
|
+
* runs migrations only after the new images have rolled out. Once the
|
|
2370
|
+
* column is dropped, remove this declaration and resync.
|
|
2371
|
+
*
|
|
2372
|
+
* Precondition: as soon as an image built with this declaration runs,
|
|
2373
|
+
* the column is never placed in INSERT column lists, so before that
|
|
2374
|
+
* image can run, the column must be nullable or carry a database
|
|
2375
|
+
* default — a live `NOT NULL` column without a default fails every
|
|
2376
|
+
* create against the table.
|
|
2377
|
+
*
|
|
2378
|
+
* Dropping the column while an image that names it can still run leaves
|
|
2379
|
+
* a window during which those containers (including a rolled-back image)
|
|
2380
|
+
* fail with `42703 column does not exist` — `leftJoinPreload` in
|
|
2381
|
+
* particular has no runtime tolerance for this, since it must enumerate
|
|
2382
|
+
* aliased columns.
|
|
2383
|
+
*
|
|
2384
|
+
* This is a mechanism for the drop window, not for permanently hiding
|
|
2385
|
+
* wide columns: the ignored column is still transferred from the
|
|
2386
|
+
* database on every `RETURNING *` / `select *` until it is actually
|
|
2387
|
+
* dropped.
|
|
2388
|
+
*
|
|
2389
|
+
* The declaration is read only while `sync` generates the types files; it
|
|
2390
|
+
* has no runtime behavior of its own. A declared-but-not-synced model is
|
|
2391
|
+
* therefore not yet protected — CI should verify that `sync` produces no
|
|
2392
|
+
* diff. `sync` will fail loudly if a declared name is not camelCase
|
|
2393
|
+
* (generated column names are camelized, so any other shape could never
|
|
2394
|
+
* match and would be silently inert), if models sharing a table declare
|
|
2395
|
+
* different ignored columns, or if a model attempts to ignore a column
|
|
2396
|
+
* the framework itself reads and writes by name: its primary key, an STI
|
|
2397
|
+
* model's `type` column, any association's foreign key, polymorphic type
|
|
2398
|
+
* field, or `primaryKeyOverride` column, an `@Sortable` position field or
|
|
2399
|
+
* plain-column `@Sortable` scope, an `@Encrypted` backing column, or a
|
|
2400
|
+
* SoftDelete model's `deletedAt` column.
|
|
2401
|
+
*
|
|
2402
|
+
* Because an ignored column vanishes from `columns()`, runtime access via
|
|
2403
|
+
* type escape hatches behaves exactly like any unknown attribute: reads
|
|
2404
|
+
* return `undefined`, and writes assign a plain instance property that is
|
|
2405
|
+
* never persisted.
|
|
2406
|
+
*
|
|
2407
|
+
* ```ts
|
|
2408
|
+
* class User extends ApplicationModel {
|
|
2409
|
+
* public override get ignoredColumns() {
|
|
2410
|
+
* return ['legacyEmail'] as const
|
|
2411
|
+
* }
|
|
2412
|
+
* }
|
|
2413
|
+
* ```
|
|
2414
|
+
*
|
|
2415
|
+
* NOTE: this getter is intentionally typed as `readonly string[]` rather
|
|
2416
|
+
* than as a union of known column names: during the deploy that declares
|
|
2417
|
+
* a column ignored, the regenerated types no longer contain the column,
|
|
2418
|
+
* so a column-name-derived type would reject the very declaration that
|
|
2419
|
+
* removed it.
|
|
2420
|
+
*
|
|
2421
|
+
* @returns The list of column names this model should ignore
|
|
2422
|
+
*/
|
|
2423
|
+
get ignoredColumns() {
|
|
2424
|
+
return [];
|
|
2425
|
+
}
|
|
2240
2426
|
/**
|
|
2241
2427
|
* @internal
|
|
2242
2428
|
*
|
|
@@ -103,6 +103,12 @@ async function destroyConnectionWithinTimeout(conn, label) {
|
|
|
103
103
|
reportLeakedDbConnections(label);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// teardown is best-effort (the registry entry has already been removed,
|
|
108
|
+
// so a subsequent getConnection() builds a fresh pool), but a rejected
|
|
109
|
+
// destroy() must not vanish silently
|
|
110
|
+
DreamApp.logWithLevel('error', `[dream] failed to close db connection "${label}"`, error);
|
|
111
|
+
}
|
|
106
112
|
finally {
|
|
107
113
|
if (timer)
|
|
108
114
|
clearTimeout(timer);
|
|
@@ -35,6 +35,18 @@ export const SOFT_DELETE_SCOPE_NAME = 'dream:SoftDelete';
|
|
|
35
35
|
* return 'customDatetimeField' as const
|
|
36
36
|
* }
|
|
37
37
|
* }
|
|
38
|
+
*
|
|
39
|
+
* Note on indexing: Dream deliberately does not index `deleted_at`.
|
|
40
|
+
* The default scope's `WHERE deleted_at IS NULL` matches nearly every
|
|
41
|
+
* row on a healthy table, so a plain b-tree on the column is rarely
|
|
42
|
+
* chosen by the planner while still costing index size and write
|
|
43
|
+
* amplification, and Dream itself never issues a query such an index
|
|
44
|
+
* could serve. If your app needs one, add it yourself based on your
|
|
45
|
+
* own access patterns — on Postgres, the two useful shapes are a
|
|
46
|
+
* composite partial index on your hot lookup columns with
|
|
47
|
+
* `WHERE deleted_at IS NULL` (fast scoped reads), or a partial index
|
|
48
|
+
* `WHERE deleted_at IS NOT NULL` (purge/GC sweeps over soft-deleted
|
|
49
|
+
* rows). Both are Postgres-specific syntax.
|
|
38
50
|
*/
|
|
39
51
|
export default function SoftDelete() {
|
|
40
52
|
return function (target) {
|
|
@@ -58,6 +58,27 @@ export default class DreamClassTransactionBuilder {
|
|
|
58
58
|
async count() {
|
|
59
59
|
return this.queryInstance().count();
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves the number of records in each group, keyed by the
|
|
63
|
+
* value of the provided group column (a SQL `GROUP BY`).
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* await ApplicationModel.transaction(async txn => {
|
|
67
|
+
* await User.txn(txn).countBy('name')
|
|
68
|
+
* // Map(2) { 'fred' => 2, 'zed' => 1 }
|
|
69
|
+
* })
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* Only groups with at least one matching row appear in the Map; seed absent
|
|
73
|
+
* groups yourself with `map.get(key) ?? 0`. When the group column is nullable,
|
|
74
|
+
* records with a `null` value are grouped under a real `null` key.
|
|
75
|
+
*
|
|
76
|
+
* @param groupColumn - the column to group by
|
|
77
|
+
* @returns A Map from each present group value to the number of records in that group
|
|
78
|
+
*/
|
|
79
|
+
async countBy(groupColumn) {
|
|
80
|
+
return this.queryInstance().countBy(groupColumn);
|
|
81
|
+
}
|
|
61
82
|
/**
|
|
62
83
|
* Returns a new Query instance, specifying a limit
|
|
63
84
|
*
|
|
@@ -107,6 +128,29 @@ export default class DreamClassTransactionBuilder {
|
|
|
107
128
|
async max(columnName) {
|
|
108
129
|
return this.queryInstance().max(columnName);
|
|
109
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Retrieves the max value of the specified column within each group, keyed by
|
|
133
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
134
|
+
*
|
|
135
|
+
* ```ts
|
|
136
|
+
* await ApplicationModel.transaction(async txn => {
|
|
137
|
+
* await CompositionAsset.txn(txn).maxBy('name', 'score')
|
|
138
|
+
* // Map(2) { 'primary' => 9, 'secondary' => 4 }
|
|
139
|
+
* })
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
143
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
144
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
145
|
+
* value.
|
|
146
|
+
*
|
|
147
|
+
* @param groupColumn - the column to group by
|
|
148
|
+
* @param aggregatedColumn - the column to take the max of within each group
|
|
149
|
+
* @returns A Map from each present group value to the max of the aggregated column in that group
|
|
150
|
+
*/
|
|
151
|
+
async maxBy(groupColumn, aggregatedColumn) {
|
|
152
|
+
return this.queryInstance().maxBy(groupColumn, aggregatedColumn);
|
|
153
|
+
}
|
|
110
154
|
/**
|
|
111
155
|
* Retrieves the min value of the specified column
|
|
112
156
|
* for this model's records.
|
|
@@ -124,6 +168,29 @@ export default class DreamClassTransactionBuilder {
|
|
|
124
168
|
async min(columnName) {
|
|
125
169
|
return this.queryInstance().min(columnName);
|
|
126
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Retrieves the min value of the specified column within each group, keyed by
|
|
173
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
174
|
+
*
|
|
175
|
+
* ```ts
|
|
176
|
+
* await ApplicationModel.transaction(async txn => {
|
|
177
|
+
* await CompositionAsset.txn(txn).minBy('name', 'score')
|
|
178
|
+
* // Map(2) { 'primary' => 1, 'secondary' => 4 }
|
|
179
|
+
* })
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
183
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
184
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
185
|
+
* value.
|
|
186
|
+
*
|
|
187
|
+
* @param groupColumn - the column to group by
|
|
188
|
+
* @param aggregatedColumn - the column to take the min of within each group
|
|
189
|
+
* @returns A Map from each present group value to the min of the aggregated column in that group
|
|
190
|
+
*/
|
|
191
|
+
async minBy(groupColumn, aggregatedColumn) {
|
|
192
|
+
return this.queryInstance().minBy(groupColumn, aggregatedColumn);
|
|
193
|
+
}
|
|
127
194
|
/**
|
|
128
195
|
* Retrieves the sum value of the specified column
|
|
129
196
|
* for this Query
|
|
@@ -139,6 +206,29 @@ export default class DreamClassTransactionBuilder {
|
|
|
139
206
|
async sum(columnName) {
|
|
140
207
|
return this.queryInstance().sum(columnName);
|
|
141
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Retrieves the sum of the specified column within each group, keyed by
|
|
211
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
212
|
+
*
|
|
213
|
+
* ```ts
|
|
214
|
+
* await ApplicationModel.transaction(async txn => {
|
|
215
|
+
* await CompositionAsset.txn(txn).sumBy('name', 'score')
|
|
216
|
+
* // Map(2) { 'primary' => 10, 'secondary' => 4 }
|
|
217
|
+
* })
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
221
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
222
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
223
|
+
* value.
|
|
224
|
+
*
|
|
225
|
+
* @param groupColumn - the column to group by
|
|
226
|
+
* @param aggregatedColumn - the column to sum within each group
|
|
227
|
+
* @returns A Map from each present group value to the sum of the aggregated column in that group
|
|
228
|
+
*/
|
|
229
|
+
async sumBy(groupColumn, aggregatedColumn) {
|
|
230
|
+
return this.queryInstance().sumBy(groupColumn, aggregatedColumn);
|
|
231
|
+
}
|
|
142
232
|
/**
|
|
143
233
|
* Retrieves the average value of the specified column
|
|
144
234
|
* for this Query
|
|
@@ -154,6 +244,29 @@ export default class DreamClassTransactionBuilder {
|
|
|
154
244
|
async avg(columnName) {
|
|
155
245
|
return this.queryInstance().avg(columnName);
|
|
156
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Retrieves the average of the specified column within each group, keyed by
|
|
249
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
250
|
+
*
|
|
251
|
+
* ```ts
|
|
252
|
+
* await ApplicationModel.transaction(async txn => {
|
|
253
|
+
* await CompositionAsset.txn(txn).avgBy('name', 'score')
|
|
254
|
+
* // Map(2) { 'primary' => 5, 'secondary' => 4 }
|
|
255
|
+
* })
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
259
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
260
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
261
|
+
* value.
|
|
262
|
+
*
|
|
263
|
+
* @param groupColumn - the column to group by
|
|
264
|
+
* @param aggregatedColumn - the column to average within each group
|
|
265
|
+
* @returns A Map from each present group value to the average of the aggregated column in that group
|
|
266
|
+
*/
|
|
267
|
+
async avgBy(groupColumn, aggregatedColumn) {
|
|
268
|
+
return this.queryInstance().avgBy(groupColumn, aggregatedColumn);
|
|
269
|
+
}
|
|
157
270
|
/**
|
|
158
271
|
* Persists a new record, setting the provided attributes.
|
|
159
272
|
* Automatically sets createdAt and updatedAt timestamps.
|
|
@@ -442,6 +442,17 @@ export default class Query {
|
|
|
442
442
|
* 3. each nested association will result in an additional record which duplicates data from the outer record. E.g., given `.leftJoinPreload('a', 'b', 'c')`, if each `a` has 10 `b` and each `b` has 10 `c`, then for one `a`, 100 records will be returned, each of which has all of the columns of `a`. `.preload('a', 'b', 'c')` would perform three separate SQL queries, but the data for a single `a` would only be returned once.
|
|
443
443
|
* 4. the individual query becomes more complex the more associations are included
|
|
444
444
|
* 5. associations loading associations loading associations could result in exponential amounts of data; in those cases, `.preload(...).findEach(...)` avoids instantiating massive amounts of data at once
|
|
445
|
+
* 6. unlike base-model reads, `preload`/`load`, and saves — which tolerate
|
|
446
|
+
* schema/image skew (e.g. a rolling deploy dropping a column while
|
|
447
|
+
* containers compiled against the previous schema are still draining) —
|
|
448
|
+
* leftJoinPreload must enumerate every compiled column of every joined
|
|
449
|
+
* model under per-alias names (per-alias `*` is not expressible in a
|
|
450
|
+
* single flat row), so an **unplanned** column drop breaks
|
|
451
|
+
* leftJoinPreload queries for the duration of the rollout window. A
|
|
452
|
+
* **planned** drop is safe when performed via the two-deploy process
|
|
453
|
+
* documented on the `ignoredColumns` getter of Dream: declaring the
|
|
454
|
+
* column ignored removes it from the generated schema, so leftJoinPreload
|
|
455
|
+
* stops naming it a full deploy before the column is actually dropped.
|
|
445
456
|
*
|
|
446
457
|
*
|
|
447
458
|
* ```ts
|
|
@@ -980,6 +991,39 @@ export default class Query {
|
|
|
980
991
|
async count() {
|
|
981
992
|
return await this.dbDriverInstance().count();
|
|
982
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* Retrieves the number of records in each group, keyed by the
|
|
996
|
+
* value of the provided group column (a SQL `GROUP BY`).
|
|
997
|
+
*
|
|
998
|
+
* ```ts
|
|
999
|
+
* await User.query().countBy('name')
|
|
1000
|
+
* // Map(2) { 'fred' => 2, 'zed' => 1 }
|
|
1001
|
+
*
|
|
1002
|
+
* await User.where({ email: ops.ilike('%gmail.com') }).countBy('name')
|
|
1003
|
+
* // Map(1) { 'fred' => 3 }
|
|
1004
|
+
* ```
|
|
1005
|
+
*
|
|
1006
|
+
* Any `where` clauses, base scopes (soft-delete / STI), and joined-association
|
|
1007
|
+
* `and` clauses on the Query carry through automatically. A joined-association
|
|
1008
|
+
* column may be grouped on by passing its namespaced name (e.g.
|
|
1009
|
+
* `'compositionAssets.name'`).
|
|
1010
|
+
*
|
|
1011
|
+
* Only groups with at least one matching row appear in the Map (inherent to
|
|
1012
|
+
* `GROUP BY`); seed absent groups yourself with `map.get(key) ?? 0`. When the
|
|
1013
|
+
* group column is nullable, records with a `null` value are grouped under a real
|
|
1014
|
+
* `null` key.
|
|
1015
|
+
*
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* await Composition.query().innerJoin('compositionAssets').countBy('compositionAssets.name')
|
|
1018
|
+
* // Map(2) { 'primary' => 3, null => 1 }
|
|
1019
|
+
* ```
|
|
1020
|
+
*
|
|
1021
|
+
* @param groupColumn - the column to group by (base or joined-association-namespaced)
|
|
1022
|
+
* @returns A Map from each present group value to the number of records in that group
|
|
1023
|
+
*/
|
|
1024
|
+
async countBy(groupColumn) {
|
|
1025
|
+
return await this.dbDriverInstance().countBy(groupColumn);
|
|
1026
|
+
}
|
|
983
1027
|
/**
|
|
984
1028
|
* Returns new Query with distinct clause applied.
|
|
985
1029
|
* If no column is specified, applies distinct to the primary key.
|
|
@@ -1038,6 +1082,32 @@ export default class Query {
|
|
|
1038
1082
|
async max(columnName) {
|
|
1039
1083
|
return await this.dbDriverInstance().max(columnName);
|
|
1040
1084
|
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Retrieves the max value of the specified column within each group, keyed by
|
|
1087
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1088
|
+
*
|
|
1089
|
+
* ```ts
|
|
1090
|
+
* await CompositionAsset.query().maxBy('name', 'score')
|
|
1091
|
+
* // Map(2) { 'primary' => 9, 'secondary' => 4 }
|
|
1092
|
+
* ```
|
|
1093
|
+
*
|
|
1094
|
+
* Any `where` clauses, base scopes (soft-delete / STI), and joined-association
|
|
1095
|
+
* `and` clauses on the Query carry through automatically. A joined-association
|
|
1096
|
+
* column may be grouped on or aggregated by passing its namespaced name (e.g.
|
|
1097
|
+
* `'compositionAssets.name'`).
|
|
1098
|
+
*
|
|
1099
|
+
* Only groups with at least one matching row appear in the Map (inherent to
|
|
1100
|
+
* `GROUP BY`). When the group column is nullable, records with a `null` value
|
|
1101
|
+
* are grouped under a real `null` key; a group whose aggregated values are all
|
|
1102
|
+
* `null` yields a `null` value.
|
|
1103
|
+
*
|
|
1104
|
+
* @param groupColumn - the column to group by (base or joined-association-namespaced)
|
|
1105
|
+
* @param aggregatedColumn - the column to take the max of within each group
|
|
1106
|
+
* @returns A Map from each present group value to the max of the aggregated column in that group
|
|
1107
|
+
*/
|
|
1108
|
+
async maxBy(groupColumn, aggregatedColumn) {
|
|
1109
|
+
return await this.dbDriverInstance().maxBy(groupColumn, aggregatedColumn);
|
|
1110
|
+
}
|
|
1041
1111
|
/**
|
|
1042
1112
|
* Retrieves the min value of the specified column
|
|
1043
1113
|
* for this Query
|
|
@@ -1053,6 +1123,32 @@ export default class Query {
|
|
|
1053
1123
|
async min(columnName) {
|
|
1054
1124
|
return await this.dbDriverInstance().min(columnName);
|
|
1055
1125
|
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Retrieves the min value of the specified column within each group, keyed by
|
|
1128
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1129
|
+
*
|
|
1130
|
+
* ```ts
|
|
1131
|
+
* await CompositionAsset.query().minBy('name', 'score')
|
|
1132
|
+
* // Map(2) { 'primary' => 1, 'secondary' => 4 }
|
|
1133
|
+
* ```
|
|
1134
|
+
*
|
|
1135
|
+
* Any `where` clauses, base scopes (soft-delete / STI), and joined-association
|
|
1136
|
+
* `and` clauses on the Query carry through automatically. A joined-association
|
|
1137
|
+
* column may be grouped on or aggregated by passing its namespaced name (e.g.
|
|
1138
|
+
* `'compositionAssets.name'`).
|
|
1139
|
+
*
|
|
1140
|
+
* Only groups with at least one matching row appear in the Map (inherent to
|
|
1141
|
+
* `GROUP BY`). When the group column is nullable, records with a `null` value
|
|
1142
|
+
* are grouped under a real `null` key; a group whose aggregated values are all
|
|
1143
|
+
* `null` yields a `null` value.
|
|
1144
|
+
*
|
|
1145
|
+
* @param groupColumn - the column to group by (base or joined-association-namespaced)
|
|
1146
|
+
* @param aggregatedColumn - the column to take the min of within each group
|
|
1147
|
+
* @returns A Map from each present group value to the min of the aggregated column in that group
|
|
1148
|
+
*/
|
|
1149
|
+
async minBy(groupColumn, aggregatedColumn) {
|
|
1150
|
+
return await this.dbDriverInstance().minBy(groupColumn, aggregatedColumn);
|
|
1151
|
+
}
|
|
1056
1152
|
/**
|
|
1057
1153
|
* Retrieves the sum value of the specified column
|
|
1058
1154
|
* for this Query
|
|
@@ -1068,6 +1164,32 @@ export default class Query {
|
|
|
1068
1164
|
async sum(columnName) {
|
|
1069
1165
|
return await this.dbDriverInstance().sum(columnName);
|
|
1070
1166
|
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Retrieves the sum of the specified column within each group, keyed by
|
|
1169
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1170
|
+
*
|
|
1171
|
+
* ```ts
|
|
1172
|
+
* await CompositionAsset.query().sumBy('name', 'score')
|
|
1173
|
+
* // Map(2) { 'primary' => 10, 'secondary' => 4 }
|
|
1174
|
+
* ```
|
|
1175
|
+
*
|
|
1176
|
+
* Any `where` clauses, base scopes (soft-delete / STI), and joined-association
|
|
1177
|
+
* `and` clauses on the Query carry through automatically. A joined-association
|
|
1178
|
+
* column may be grouped on or aggregated by passing its namespaced name (e.g.
|
|
1179
|
+
* `'compositionAssets.name'`).
|
|
1180
|
+
*
|
|
1181
|
+
* Only groups with at least one matching row appear in the Map (inherent to
|
|
1182
|
+
* `GROUP BY`). When the group column is nullable, records with a `null` value
|
|
1183
|
+
* are grouped under a real `null` key; a group whose aggregated values are all
|
|
1184
|
+
* `null` yields a `null` value.
|
|
1185
|
+
*
|
|
1186
|
+
* @param groupColumn - the column to group by (base or joined-association-namespaced)
|
|
1187
|
+
* @param aggregatedColumn - the column to sum within each group
|
|
1188
|
+
* @returns A Map from each present group value to the sum of the aggregated column in that group
|
|
1189
|
+
*/
|
|
1190
|
+
async sumBy(groupColumn, aggregatedColumn) {
|
|
1191
|
+
return await this.dbDriverInstance().sumBy(groupColumn, aggregatedColumn);
|
|
1192
|
+
}
|
|
1071
1193
|
/**
|
|
1072
1194
|
* Retrieves the average value of the specified column
|
|
1073
1195
|
* for this Query
|
|
@@ -1083,6 +1205,32 @@ export default class Query {
|
|
|
1083
1205
|
async avg(columnName) {
|
|
1084
1206
|
return await this.dbDriverInstance().avg(columnName);
|
|
1085
1207
|
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Retrieves the average of the specified column within each group, keyed by
|
|
1210
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
1211
|
+
*
|
|
1212
|
+
* ```ts
|
|
1213
|
+
* await CompositionAsset.query().avgBy('name', 'score')
|
|
1214
|
+
* // Map(2) { 'primary' => 5, 'secondary' => 4 }
|
|
1215
|
+
* ```
|
|
1216
|
+
*
|
|
1217
|
+
* Any `where` clauses, base scopes (soft-delete / STI), and joined-association
|
|
1218
|
+
* `and` clauses on the Query carry through automatically. A joined-association
|
|
1219
|
+
* column may be grouped on or aggregated by passing its namespaced name (e.g.
|
|
1220
|
+
* `'compositionAssets.name'`).
|
|
1221
|
+
*
|
|
1222
|
+
* Only groups with at least one matching row appear in the Map (inherent to
|
|
1223
|
+
* `GROUP BY`). When the group column is nullable, records with a `null` value
|
|
1224
|
+
* are grouped under a real `null` key; a group whose aggregated values are all
|
|
1225
|
+
* `null` yields a `null` value.
|
|
1226
|
+
*
|
|
1227
|
+
* @param groupColumn - the column to group by (base or joined-association-namespaced)
|
|
1228
|
+
* @param aggregatedColumn - the column to average within each group
|
|
1229
|
+
* @returns A Map from each present group value to the average of the aggregated column in that group
|
|
1230
|
+
*/
|
|
1231
|
+
async avgBy(groupColumn, aggregatedColumn) {
|
|
1232
|
+
return await this.dbDriverInstance().avgBy(groupColumn, aggregatedColumn);
|
|
1233
|
+
}
|
|
1086
1234
|
/**
|
|
1087
1235
|
* Plucks the provided fields from the given dream class table
|
|
1088
1236
|
*
|