@rvoh/dream 1.10.0 → 1.11.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/Dream.js +43 -42
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +0 -32
- package/dist/cjs/src/dream/DreamInstanceTransactionBuilder.js +23 -0
- package/dist/cjs/src/dream/Query.js +75 -4
- package/dist/cjs/src/helpers/cli/SchemaBuilder.js +10 -12
- package/dist/esm/src/Dream.js +43 -42
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +0 -32
- package/dist/esm/src/dream/DreamInstanceTransactionBuilder.js +23 -0
- package/dist/esm/src/dream/Query.js +75 -4
- package/dist/esm/src/helpers/cli/SchemaBuilder.js +10 -12
- package/dist/types/src/Dream.d.ts +115 -22
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +1 -31
- package/dist/types/src/dream/DreamInstanceTransactionBuilder.d.ts +113 -7
- package/dist/types/src/dream/Query.d.ts +32 -1
- package/dist/types/src/helpers/cli/SchemaBuilder.d.ts +1 -1
- package/dist/types/src/types/associations/shared.d.ts +1 -0
- package/dist/types/src/types/associations/shared.ts +8 -0
- package/dist/types/src/types/dream.d.ts +11 -3
- package/dist/types/src/types/dream.ts +60 -3
- package/dist/types/src/types/query.d.ts +20 -0
- package/dist/types/src/types/query.ts +24 -0
- package/dist/types/src/types/variadic.d.ts +1 -2
- package/dist/types/src/types/variadic.ts +2 -18
- package/docs/assets/search.js +1 -1
- package/docs/classes/Benchmark.html +2 -2
- package/docs/classes/CalendarDate.html +2 -2
- package/docs/classes/CheckConstraintViolation.html +3 -3
- package/docs/classes/CliFileWriter.html +2 -2
- package/docs/classes/ColumnOverflow.html +3 -3
- package/docs/classes/CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/Decorators.html +19 -19
- package/docs/classes/Dream.html +244 -227
- package/docs/classes/DreamApp.html +4 -4
- package/docs/classes/DreamBin.html +2 -2
- package/docs/classes/DreamCLI.html +4 -4
- package/docs/classes/DreamImporter.html +2 -2
- package/docs/classes/DreamLogos.html +2 -2
- package/docs/classes/DreamMigrationHelpers.html +9 -9
- package/docs/classes/DreamSerializerBuilder.html +8 -8
- package/docs/classes/DreamTransaction.html +2 -2
- package/docs/classes/Encrypt.html +2 -2
- package/docs/classes/Env.html +2 -2
- package/docs/classes/GlobalNameNotSet.html +3 -3
- package/docs/classes/KyselyQueryDriver.html +30 -30
- package/docs/classes/MissingSerializersDefinition.html +3 -3
- package/docs/classes/NonLoadedAssociation.html +3 -3
- package/docs/classes/NotNullViolation.html +3 -3
- package/docs/classes/ObjectSerializerBuilder.html +8 -8
- package/docs/classes/PostgresQueryDriver.html +31 -31
- package/docs/classes/Query.html +75 -65
- package/docs/classes/QueryDriverBase.html +29 -29
- package/docs/classes/Range.html +2 -2
- package/docs/classes/RecordNotFound.html +3 -3
- package/docs/classes/ValidationError.html +3 -3
- package/docs/functions/DreamSerializer.html +1 -1
- package/docs/functions/ObjectSerializer.html +1 -1
- package/docs/functions/ReplicaSafe.html +1 -1
- package/docs/functions/STI.html +1 -1
- package/docs/functions/SoftDelete.html +1 -1
- package/docs/functions/camelize.html +1 -1
- package/docs/functions/capitalize.html +1 -1
- package/docs/functions/cloneDeepSafe.html +1 -1
- package/docs/functions/closeAllDbConnections.html +1 -1
- package/docs/functions/compact.html +1 -1
- package/docs/functions/dreamDbConnections.html +1 -1
- package/docs/functions/dreamPath.html +1 -1
- package/docs/functions/expandStiClasses.html +1 -1
- package/docs/functions/generateDream.html +1 -1
- package/docs/functions/globalClassNameFromFullyQualifiedModelName.html +1 -1
- package/docs/functions/groupBy.html +1 -1
- package/docs/functions/hyphenize.html +1 -1
- package/docs/functions/inferSerializerFromDreamOrViewModel.html +1 -1
- package/docs/functions/inferSerializersFromDreamClassOrViewModelClass.html +1 -1
- package/docs/functions/intersection.html +1 -1
- package/docs/functions/isDreamSerializer.html +1 -1
- package/docs/functions/isEmpty.html +1 -1
- package/docs/functions/loadRepl.html +1 -1
- package/docs/functions/lookupClassByGlobalName.html +1 -1
- package/docs/functions/normalizeUnicode.html +1 -1
- package/docs/functions/pascalize.html +1 -1
- package/docs/functions/percent.html +1 -1
- package/docs/functions/pgErrorType.html +1 -1
- package/docs/functions/range-1.html +1 -1
- package/docs/functions/relativeDreamPath.html +1 -1
- package/docs/functions/round.html +1 -1
- package/docs/functions/serializerNameFromFullyQualifiedModelName.html +1 -1
- package/docs/functions/sharedPathPrefix.html +1 -1
- package/docs/functions/snakeify.html +1 -1
- package/docs/functions/sort.html +1 -1
- package/docs/functions/sortBy.html +1 -1
- package/docs/functions/sortObjectByKey.html +1 -1
- package/docs/functions/sortObjectByValue.html +1 -1
- package/docs/functions/standardizeFullyQualifiedModelName.html +1 -1
- package/docs/functions/uncapitalize.html +1 -1
- package/docs/functions/uniq.html +1 -1
- package/docs/functions/untypedDb.html +1 -1
- package/docs/functions/validateColumn.html +1 -1
- package/docs/functions/validateTable.html +1 -1
- package/docs/interfaces/BelongsToStatement.html +2 -2
- package/docs/interfaces/DecoratorContext.html +2 -2
- package/docs/interfaces/DreamAppInitOptions.html +2 -2
- package/docs/interfaces/DreamAppOpts.html +2 -2
- package/docs/interfaces/EncryptOptions.html +2 -2
- package/docs/interfaces/InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/OpenapiDescription.html +2 -2
- package/docs/interfaces/OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/SerializerRendererOpts.html +2 -2
- package/docs/types/Camelized.html +1 -1
- package/docs/types/CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/DateTime.html +1 -1
- package/docs/types/DbConnectionType.html +1 -1
- package/docs/types/DbTypes.html +1 -1
- package/docs/types/DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/DreamAssociationMetadata.html +1 -1
- package/docs/types/DreamAttributes.html +1 -1
- package/docs/types/DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/DreamClassColumn.html +1 -1
- package/docs/types/DreamColumn.html +1 -1
- package/docs/types/DreamColumnNames.html +1 -1
- package/docs/types/DreamLogLevel.html +1 -1
- package/docs/types/DreamLogger.html +1 -1
- package/docs/types/DreamModelSerializerType.html +1 -1
- package/docs/types/DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/DreamParamSafeAttributes.html +1 -1
- package/docs/types/DreamParamSafeColumnNames.html +1 -1
- package/docs/types/DreamSerializable.html +1 -1
- package/docs/types/DreamSerializableArray.html +1 -1
- package/docs/types/DreamSerializerKey.html +1 -1
- package/docs/types/DreamSerializers.html +1 -1
- package/docs/types/DreamVirtualColumns.html +1 -1
- package/docs/types/EncryptAlgorithm.html +1 -1
- package/docs/types/HasManyStatement.html +1 -1
- package/docs/types/HasOneStatement.html +1 -1
- package/docs/types/Hyphenized.html +1 -1
- package/docs/types/OpenapiAllTypes.html +1 -1
- package/docs/types/OpenapiFormats.html +1 -1
- package/docs/types/OpenapiNumberFormats.html +1 -1
- package/docs/types/OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/OpenapiSchemaArray.html +1 -1
- package/docs/types/OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/OpenapiSchemaBase.html +1 -1
- package/docs/types/OpenapiSchemaBody.html +1 -1
- package/docs/types/OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/OpenapiSchemaExpressionAllOf.html +1 -1
- package/docs/types/OpenapiSchemaExpressionAnyOf.html +1 -1
- package/docs/types/OpenapiSchemaExpressionOneOf.html +1 -1
- package/docs/types/OpenapiSchemaExpressionRef.html +1 -1
- package/docs/types/OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
- package/docs/types/OpenapiSchemaInteger.html +1 -1
- package/docs/types/OpenapiSchemaNull.html +1 -1
- package/docs/types/OpenapiSchemaNumber.html +1 -1
- package/docs/types/OpenapiSchemaObject.html +1 -1
- package/docs/types/OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/OpenapiSchemaShorthandExpressionAllOf.html +1 -1
- package/docs/types/OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
- package/docs/types/OpenapiSchemaShorthandExpressionOneOf.html +1 -1
- package/docs/types/OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
- package/docs/types/OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
- package/docs/types/OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/OpenapiSchemaString.html +1 -1
- package/docs/types/OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/OpenapiTypeField.html +1 -1
- package/docs/types/Pascalized.html +1 -1
- package/docs/types/RoundingPrecision.html +1 -1
- package/docs/types/SerializerCasing.html +1 -1
- package/docs/types/SimpleObjectSerializerType.html +1 -1
- package/docs/types/Snakeified.html +1 -1
- package/docs/types/UpdateableAssociationProperties.html +1 -1
- package/docs/types/UpdateableProperties.html +1 -1
- package/docs/types/ValidationType.html +1 -1
- package/docs/types/ViewModel.html +1 -1
- package/docs/types/ViewModelClass.html +1 -1
- package/docs/types/WhereStatementForDream.html +1 -1
- package/docs/types/WhereStatementForDreamClass.html +1 -1
- package/docs/variables/DateTime-1.html +1 -1
- package/docs/variables/DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/DreamConst.html +1 -1
- package/docs/variables/TRIGRAM_OPERATORS.html +1 -1
- package/docs/variables/openapiPrimitiveTypes-1.html +1 -1
- package/docs/variables/openapiShorthandPrimitiveTypes-1.html +1 -1
- package/docs/variables/ops.html +1 -1
- package/docs/variables/primaryKeyTypes.html +1 -1
- package/package.json +3 -3
package/dist/cjs/src/Dream.js
CHANGED
|
@@ -909,6 +909,37 @@ class Dream {
|
|
|
909
909
|
static async paginate(opts) {
|
|
910
910
|
return await this.query().paginate(opts);
|
|
911
911
|
}
|
|
912
|
+
/**
|
|
913
|
+
* Fast paginates the results of your query using cursor-based pagination,
|
|
914
|
+
* accepting a pageSize and cursor argument. This method
|
|
915
|
+
* provides better performance for large datasets by using cursor-based
|
|
916
|
+
* pagination instead of offset-based pagination.
|
|
917
|
+
*
|
|
918
|
+
* ```ts
|
|
919
|
+
* // First page
|
|
920
|
+
* const firstPage = await User.order('email').fastPaginate({ pageSize: 100 })
|
|
921
|
+
* firstPage.results
|
|
922
|
+
* // [ { User{id: 1}, User{id: 2}, ...}]
|
|
923
|
+
*
|
|
924
|
+
* firstPage.cursor
|
|
925
|
+
* // "100" (or null if no more pages)
|
|
926
|
+
*
|
|
927
|
+
* // Next page using cursor from previous result
|
|
928
|
+
* const nextPage = await User.order('email').fastPaginate({
|
|
929
|
+
* pageSize: 100,
|
|
930
|
+
* cursor: firstPage.cursor
|
|
931
|
+
* })
|
|
932
|
+
* ```
|
|
933
|
+
*
|
|
934
|
+
* @param opts - Fast pagination options
|
|
935
|
+
* @param opts.pageSize - the number of results per page (optional)
|
|
936
|
+
* @param opts.cursor - identifier of where to start the next page; undefined to start from the beginning; null when no more pages
|
|
937
|
+
* @returns results.cursor - identifier for the next page, or null if no more pages
|
|
938
|
+
* @returns results.results - An array of records for the current page
|
|
939
|
+
*/
|
|
940
|
+
static async fastPaginate(opts) {
|
|
941
|
+
return await this.query().fastPaginate(opts);
|
|
942
|
+
}
|
|
912
943
|
/**
|
|
913
944
|
* Forces use of a database connection (e.g. 'primary') during the query.
|
|
914
945
|
*
|
|
@@ -3221,61 +3252,31 @@ class Dream {
|
|
|
3221
3252
|
load(...args) {
|
|
3222
3253
|
return new LoadBuilder_js_1.default(this).load(...args);
|
|
3223
3254
|
}
|
|
3224
|
-
|
|
3225
|
-
* If the association is already loaded on the instance, it returns the loaded value
|
|
3226
|
-
* immediately without making a database query. If the association is not loaded,
|
|
3227
|
-
* it performs a database query to fetch the association.
|
|
3228
|
-
*
|
|
3229
|
-
* If a query is performed, the association is set on the model so that future calls to
|
|
3230
|
-
* access this association can access the already loaded value.
|
|
3231
|
-
*
|
|
3232
|
-
* ```ts
|
|
3233
|
-
* // return the already loaded user if it exists or fetch it from the database otherwise
|
|
3234
|
-
* const user = await post.associationOrFail('user')
|
|
3235
|
-
* ```
|
|
3236
|
-
*
|
|
3237
|
-
* @param associationName - The name of the BelongsTo or HasOne association
|
|
3238
|
-
* @returns The associated model instance or null
|
|
3239
|
-
*/
|
|
3240
|
-
async association(associationName) {
|
|
3255
|
+
async association(associationName, options) {
|
|
3241
3256
|
if (!this.loaded(associationName)) {
|
|
3242
3257
|
const association = this.getAssociationMetadata(associationName);
|
|
3258
|
+
const associationQueryOptions = options?.required && { and: options.required };
|
|
3259
|
+
let scope = this.associationQuery(associationName, associationQueryOptions);
|
|
3260
|
+
if (options?.passthrough)
|
|
3261
|
+
scope = scope.passthrough(options.passthrough);
|
|
3243
3262
|
if (association?.type === 'HasMany') {
|
|
3244
|
-
this[associationName] = (await
|
|
3263
|
+
this[associationName] = (await scope.all());
|
|
3245
3264
|
}
|
|
3246
3265
|
else {
|
|
3247
|
-
this[associationName] = (await
|
|
3266
|
+
this[associationName] = (await scope.first());
|
|
3248
3267
|
}
|
|
3249
3268
|
}
|
|
3250
3269
|
return this[associationName];
|
|
3251
3270
|
}
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
* immediately without making a database query. If the association is not loaded,
|
|
3255
|
-
* it performs a database query to fetch the association.
|
|
3256
|
-
*
|
|
3257
|
-
* Unlike `association`, this method throws an exception if no associated
|
|
3258
|
-
* record is found, guaranteeing a non-null result.
|
|
3259
|
-
*
|
|
3260
|
-
* If a query is performed, the association is set on the model so that future calls to
|
|
3261
|
-
* access this association can access the already loaded value.
|
|
3262
|
-
*
|
|
3263
|
-
* ```ts
|
|
3264
|
-
* // return the already loaded user if it exists or fetch it from the database otherwise,
|
|
3265
|
-
* // throwing RecordNotFound either way if the associated model does not exist
|
|
3266
|
-
* const user = await post.associationOrFail('user')
|
|
3267
|
-
* ```
|
|
3268
|
-
*
|
|
3269
|
-
* @param associationName - The name of the BelongsTo or HasOne association
|
|
3270
|
-
* @returns The associated model instance (never null)
|
|
3271
|
-
* @throws RecordNotFound if no associated record exists
|
|
3272
|
-
*/
|
|
3273
|
-
async associationOrFail(associationName) {
|
|
3274
|
-
const response = await this.association(associationName);
|
|
3271
|
+
async associationOrFail(associationName, options) {
|
|
3272
|
+
const response = await this.association(associationName, options);
|
|
3275
3273
|
if (this[associationName] === null)
|
|
3276
3274
|
throw new RecordNotFound_js_1.default(this['sanitizedConstructorName']);
|
|
3277
3275
|
return response;
|
|
3278
3276
|
}
|
|
3277
|
+
///////////////////
|
|
3278
|
+
// end:associationOrFail
|
|
3279
|
+
///////////////////
|
|
3279
3280
|
/**
|
|
3280
3281
|
* Recursively loads all Dream associations referenced by `rendersOne` and `rendersMany`
|
|
3281
3282
|
* in a DreamSerializer. This traverses the entire content tree of serializers to automatically
|
|
@@ -38,38 +38,6 @@ class DreamClassTransactionBuilder {
|
|
|
38
38
|
async all(options = {}) {
|
|
39
39
|
return this.queryInstance().all(options);
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Paginates the results of your query, accepting a pageSize and page argument,
|
|
43
|
-
* which it uses to segment your query into pages, leveraging limit and offset
|
|
44
|
-
* to deliver your query to you in pages.
|
|
45
|
-
*
|
|
46
|
-
* ```ts
|
|
47
|
-
* await ApplicationModel.transaction(async txn => {
|
|
48
|
-
* const paginated = await User.txn(txn).paginate({ pageSize: 100, page: 2 })
|
|
49
|
-
* paginated.results
|
|
50
|
-
* // [ { User{id: 101}, User{id: 102}, ...}]
|
|
51
|
-
*
|
|
52
|
-
* paginated.recordCount
|
|
53
|
-
* // 350
|
|
54
|
-
*
|
|
55
|
-
* paginated.pageCount
|
|
56
|
-
* // 4
|
|
57
|
-
*
|
|
58
|
-
* paginated.currentPage
|
|
59
|
-
* // 2
|
|
60
|
-
* })
|
|
61
|
-
* ```
|
|
62
|
-
*
|
|
63
|
-
* @param opts.page - the page number that you want to fetch results for
|
|
64
|
-
* @param opts.pageSize - the number of results per page (optional)
|
|
65
|
-
* @returns results.recordCount - A number representing the total number of records matching your query
|
|
66
|
-
* @returns results.pageCount - The number of pages needed to encapsulate all the matching records
|
|
67
|
-
* @returns results.currentPage - The current page (same as what is provided in the paginate args)
|
|
68
|
-
* @returns results.results - An array of records matching the current record
|
|
69
|
-
*/
|
|
70
|
-
async paginate(opts) {
|
|
71
|
-
return await this.queryInstance().paginate(opts);
|
|
72
|
-
}
|
|
73
41
|
/**
|
|
74
42
|
* Retrieves the number of records corresponding
|
|
75
43
|
* to this model.
|
|
@@ -4,6 +4,7 @@ const CannotAssociationQueryOnUnpersistedDream_js_1 = require("../errors/associa
|
|
|
4
4
|
const CannotCreateAssociationOnUnpersistedDream_js_1 = require("../errors/associations/CannotCreateAssociationOnUnpersistedDream.js");
|
|
5
5
|
const CannotDestroyAssociationOnUnpersistedDream_js_1 = require("../errors/associations/CannotDestroyAssociationOnUnpersistedDream.js");
|
|
6
6
|
const CannotUpdateAssociationOnUnpersistedDream_js_1 = require("../errors/associations/CannotUpdateAssociationOnUnpersistedDream.js");
|
|
7
|
+
const RecordNotFound_js_1 = require("../errors/RecordNotFound.js");
|
|
7
8
|
const associationQuery_js_1 = require("./internal/associations/associationQuery.js");
|
|
8
9
|
const associationUpdateQuery_js_1 = require("./internal/associations/associationUpdateQuery.js");
|
|
9
10
|
const createAssociation_js_1 = require("./internal/associations/createAssociation.js");
|
|
@@ -398,6 +399,28 @@ class DreamInstanceTransactionBuilder {
|
|
|
398
399
|
async save({ skipHooks } = {}) {
|
|
399
400
|
await (0, saveDream_js_1.default)(this.dreamInstance, this.dreamTransaction, skipHooks ? { skipHooks } : undefined);
|
|
400
401
|
}
|
|
402
|
+
async association(associationName, options) {
|
|
403
|
+
if (!this.dreamInstance.loaded(associationName)) {
|
|
404
|
+
const association = this.dreamInstance['getAssociationMetadata'](associationName);
|
|
405
|
+
const associationQueryOptions = options?.required && { and: options.required };
|
|
406
|
+
let scope = this.associationQuery(associationName, associationQueryOptions);
|
|
407
|
+
if (options?.passthrough)
|
|
408
|
+
scope = scope.passthrough(options.passthrough);
|
|
409
|
+
if (association?.type === 'HasMany') {
|
|
410
|
+
this.dreamInstance[associationName] = (await scope.all());
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
this.dreamInstance[associationName] = (await scope.first());
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return this.dreamInstance[associationName];
|
|
417
|
+
}
|
|
418
|
+
async associationOrFail(associationName, options) {
|
|
419
|
+
const response = await this.association(associationName, options);
|
|
420
|
+
if (this.dreamInstance[associationName] === null)
|
|
421
|
+
throw new RecordNotFound_js_1.default(this.dreamInstance['sanitizedConstructorName']);
|
|
422
|
+
return response;
|
|
423
|
+
}
|
|
401
424
|
/**
|
|
402
425
|
* Returns a Query instance for the specified
|
|
403
426
|
* association on the current instance.
|
|
@@ -809,7 +809,8 @@ class Query {
|
|
|
809
809
|
* @returns A cloned Query with the passthrough data
|
|
810
810
|
*/
|
|
811
811
|
passthrough(passthroughOnStatement) {
|
|
812
|
-
|
|
812
|
+
const baseSelectQuery = this.baseSelectQuery && this.baseSelectQuery.clone({ passthroughOnStatement: passthroughOnStatement });
|
|
813
|
+
return this.clone({ passthroughOnStatement: passthroughOnStatement, baseSelectQuery });
|
|
813
814
|
}
|
|
814
815
|
/**
|
|
815
816
|
* Applies a where statement to the Query instance
|
|
@@ -1209,20 +1210,21 @@ class Query {
|
|
|
1209
1210
|
* @returns results.results - An array of records matching the current record
|
|
1210
1211
|
*/
|
|
1211
1212
|
async paginate(opts) {
|
|
1213
|
+
const options = opts;
|
|
1212
1214
|
if (this.limitStatement)
|
|
1213
1215
|
throw new CannotPaginateWithLimit_js_1.default();
|
|
1214
1216
|
if (this.offsetStatement)
|
|
1215
1217
|
throw new CannotPaginateWithOffset_js_1.default();
|
|
1216
|
-
const page = (0, computedPaginatePage_js_1.default)(
|
|
1218
|
+
const page = (0, computedPaginatePage_js_1.default)(options.page);
|
|
1217
1219
|
const recordCount = await this.count();
|
|
1218
|
-
const pageSize =
|
|
1220
|
+
const pageSize = Math.max(0, options.pageSize ?? 0) || index_js_1.default.getOrFail().paginationPageSize;
|
|
1219
1221
|
const pageCount = Math.ceil(recordCount / pageSize);
|
|
1220
1222
|
const query = this.orderStatements.length
|
|
1221
1223
|
? this
|
|
1222
1224
|
: this.order({ [this.namespacedPrimaryKey]: 'asc' });
|
|
1223
1225
|
const results = await query
|
|
1224
1226
|
.limit(pageSize)
|
|
1225
|
-
.offset((page - 1) * pageSize)
|
|
1227
|
+
.offset(((page - 1) * pageSize))
|
|
1226
1228
|
.all();
|
|
1227
1229
|
return {
|
|
1228
1230
|
recordCount,
|
|
@@ -1231,6 +1233,75 @@ class Query {
|
|
|
1231
1233
|
results,
|
|
1232
1234
|
};
|
|
1233
1235
|
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Fast paginates the results of your query using cursor-based pagination,
|
|
1238
|
+
* accepting a pageSize and cursor argument. This method
|
|
1239
|
+
* provides better performance for large datasets by using cursor-based
|
|
1240
|
+
* pagination instead of offset-based pagination.
|
|
1241
|
+
*
|
|
1242
|
+
* ```ts
|
|
1243
|
+
* // First page
|
|
1244
|
+
* const firstPage = await User.order('email').fastPaginate({ pageSize: 100 })
|
|
1245
|
+
* firstPage.results
|
|
1246
|
+
* // [ { User{id: 1}, User{id: 2}, ...}]
|
|
1247
|
+
*
|
|
1248
|
+
* firstPage.cursor
|
|
1249
|
+
* // "100" (or null if no more pages)
|
|
1250
|
+
*
|
|
1251
|
+
* // Next page using cursor from previous result
|
|
1252
|
+
* const nextPage = await User.order('email').fastPaginate({
|
|
1253
|
+
* pageSize: 100,
|
|
1254
|
+
* cursor: firstPage.cursor
|
|
1255
|
+
* })
|
|
1256
|
+
* ```
|
|
1257
|
+
*
|
|
1258
|
+
* @param opts - Fast pagination options
|
|
1259
|
+
* @param opts.pageSize - the number of results per page (optional)
|
|
1260
|
+
* @param opts.cursor - identifier of where to start the next page; undefined to start from the beginning; null when no more pages
|
|
1261
|
+
* @returns results.cursor - identifier for the next page, or null if no more pages
|
|
1262
|
+
* @returns results.results - An array of records for the current page
|
|
1263
|
+
*/
|
|
1264
|
+
async fastPaginate(opts) {
|
|
1265
|
+
const options = opts;
|
|
1266
|
+
const pageSize = Math.max(0, options.pageSize ?? 0) || index_js_1.default.getOrFail().paginationPageSize;
|
|
1267
|
+
if (options === null)
|
|
1268
|
+
return { cursor: null, results: [] };
|
|
1269
|
+
if (this.limitStatement)
|
|
1270
|
+
throw new CannotPaginateWithLimit_js_1.default();
|
|
1271
|
+
if (this.offsetStatement)
|
|
1272
|
+
throw new CannotPaginateWithOffset_js_1.default();
|
|
1273
|
+
const explicitOrdering = !!this.orderStatements.length;
|
|
1274
|
+
let query = explicitOrdering ? this : this.order({ [this.namespacedPrimaryKey]: 'asc' });
|
|
1275
|
+
if (options.cursor) {
|
|
1276
|
+
if (explicitOrdering) {
|
|
1277
|
+
const lastItem = await query
|
|
1278
|
+
.where({ [this.dreamClass.primaryKey]: options.cursor })
|
|
1279
|
+
.firstOrFail();
|
|
1280
|
+
this.orderStatements.forEach(orderStatement => {
|
|
1281
|
+
const column = orderStatement.column;
|
|
1282
|
+
const direction = orderStatement.direction;
|
|
1283
|
+
switch (direction) {
|
|
1284
|
+
case 'asc':
|
|
1285
|
+
query = query.where({ [column]: index_js_2.default.greaterThan(lastItem[column]) });
|
|
1286
|
+
break;
|
|
1287
|
+
case 'desc':
|
|
1288
|
+
query = query.where({ [column]: index_js_2.default.lessThan(lastItem[column]) });
|
|
1289
|
+
break;
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
query = query.where({
|
|
1295
|
+
[this.dreamClass.primaryKey]: index_js_2.default.greaterThan(options.cursor),
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const results = await query.limit(pageSize).all();
|
|
1300
|
+
return {
|
|
1301
|
+
cursor: (results.length === pageSize && results.at(-1)?.primaryKeyValue().toString()) || null,
|
|
1302
|
+
results,
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1234
1305
|
/**
|
|
1235
1306
|
* Forces use of a database connection (e.g. 'primary') during the query.
|
|
1236
1307
|
*
|
|
@@ -132,24 +132,22 @@ ${tableName}: {
|
|
|
132
132
|
const associationMetadata = tableData.associations[associationName];
|
|
133
133
|
if (associationMetadata === undefined)
|
|
134
134
|
return '';
|
|
135
|
-
const
|
|
136
|
-
const
|
|
135
|
+
const andStatement = associationMetadata.and;
|
|
136
|
+
const requiredAndClauses = andStatement === null
|
|
137
137
|
? []
|
|
138
|
-
: Object.keys(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
...passthroughColumns,
|
|
144
|
-
...Object.keys(whereStatement).filter(column => whereStatement[column] === constants_js_1.DreamConst.passthrough),
|
|
145
|
-
];
|
|
138
|
+
: Object.keys(andStatement).filter(column => andStatement[column] === constants_js_1.DreamConst.required);
|
|
139
|
+
const passthroughAndClauses = andStatement === null
|
|
140
|
+
? []
|
|
141
|
+
: Object.keys(andStatement).filter(column => andStatement[column] === constants_js_1.DreamConst.passthrough);
|
|
142
|
+
passthroughColumns = [...passthroughColumns, ...passthroughAndClauses];
|
|
146
143
|
return `${associationName}: {
|
|
147
144
|
type: '${associationMetadata.type}',
|
|
148
145
|
foreignKey: ${associationMetadata.foreignKey ? `'${associationMetadata.foreignKey}'` : 'null'},
|
|
149
146
|
foreignKeyTypeColumn: ${associationMetadata.foreignKeyTypeColumn ? `'${associationMetadata.foreignKeyTypeColumn}'` : 'null'},
|
|
150
147
|
tables: ${stringifyArray(associationMetadata.tables)},
|
|
151
148
|
optional: ${associationMetadata.optional},
|
|
152
|
-
|
|
149
|
+
requiredAndClauses: ${requiredAndClauses.length === 0 ? 'null' : stringifyArray(requiredAndClauses)},
|
|
150
|
+
passthroughAndClauses: ${passthroughAndClauses.length === 0 ? 'null' : stringifyArray(passthroughAndClauses)},
|
|
153
151
|
},`;
|
|
154
152
|
})
|
|
155
153
|
.join('\n ')}
|
|
@@ -276,7 +274,7 @@ may need to update the table getter in the corresponding Dream.
|
|
|
276
274
|
? associationMetaData?.foreignKeyTypeField?.() || null
|
|
277
275
|
: null,
|
|
278
276
|
optional,
|
|
279
|
-
where,
|
|
277
|
+
and: where,
|
|
280
278
|
};
|
|
281
279
|
if (foreignKey)
|
|
282
280
|
tableAssociationData[associationName]['foreignKey'] = foreignKey;
|
package/dist/esm/src/Dream.js
CHANGED
|
@@ -907,6 +907,37 @@ export default class Dream {
|
|
|
907
907
|
static async paginate(opts) {
|
|
908
908
|
return await this.query().paginate(opts);
|
|
909
909
|
}
|
|
910
|
+
/**
|
|
911
|
+
* Fast paginates the results of your query using cursor-based pagination,
|
|
912
|
+
* accepting a pageSize and cursor argument. This method
|
|
913
|
+
* provides better performance for large datasets by using cursor-based
|
|
914
|
+
* pagination instead of offset-based pagination.
|
|
915
|
+
*
|
|
916
|
+
* ```ts
|
|
917
|
+
* // First page
|
|
918
|
+
* const firstPage = await User.order('email').fastPaginate({ pageSize: 100 })
|
|
919
|
+
* firstPage.results
|
|
920
|
+
* // [ { User{id: 1}, User{id: 2}, ...}]
|
|
921
|
+
*
|
|
922
|
+
* firstPage.cursor
|
|
923
|
+
* // "100" (or null if no more pages)
|
|
924
|
+
*
|
|
925
|
+
* // Next page using cursor from previous result
|
|
926
|
+
* const nextPage = await User.order('email').fastPaginate({
|
|
927
|
+
* pageSize: 100,
|
|
928
|
+
* cursor: firstPage.cursor
|
|
929
|
+
* })
|
|
930
|
+
* ```
|
|
931
|
+
*
|
|
932
|
+
* @param opts - Fast pagination options
|
|
933
|
+
* @param opts.pageSize - the number of results per page (optional)
|
|
934
|
+
* @param opts.cursor - identifier of where to start the next page; undefined to start from the beginning; null when no more pages
|
|
935
|
+
* @returns results.cursor - identifier for the next page, or null if no more pages
|
|
936
|
+
* @returns results.results - An array of records for the current page
|
|
937
|
+
*/
|
|
938
|
+
static async fastPaginate(opts) {
|
|
939
|
+
return await this.query().fastPaginate(opts);
|
|
940
|
+
}
|
|
910
941
|
/**
|
|
911
942
|
* Forces use of a database connection (e.g. 'primary') during the query.
|
|
912
943
|
*
|
|
@@ -3219,61 +3250,31 @@ export default class Dream {
|
|
|
3219
3250
|
load(...args) {
|
|
3220
3251
|
return new LoadBuilder(this).load(...args);
|
|
3221
3252
|
}
|
|
3222
|
-
|
|
3223
|
-
* If the association is already loaded on the instance, it returns the loaded value
|
|
3224
|
-
* immediately without making a database query. If the association is not loaded,
|
|
3225
|
-
* it performs a database query to fetch the association.
|
|
3226
|
-
*
|
|
3227
|
-
* If a query is performed, the association is set on the model so that future calls to
|
|
3228
|
-
* access this association can access the already loaded value.
|
|
3229
|
-
*
|
|
3230
|
-
* ```ts
|
|
3231
|
-
* // return the already loaded user if it exists or fetch it from the database otherwise
|
|
3232
|
-
* const user = await post.associationOrFail('user')
|
|
3233
|
-
* ```
|
|
3234
|
-
*
|
|
3235
|
-
* @param associationName - The name of the BelongsTo or HasOne association
|
|
3236
|
-
* @returns The associated model instance or null
|
|
3237
|
-
*/
|
|
3238
|
-
async association(associationName) {
|
|
3253
|
+
async association(associationName, options) {
|
|
3239
3254
|
if (!this.loaded(associationName)) {
|
|
3240
3255
|
const association = this.getAssociationMetadata(associationName);
|
|
3256
|
+
const associationQueryOptions = options?.required && { and: options.required };
|
|
3257
|
+
let scope = this.associationQuery(associationName, associationQueryOptions);
|
|
3258
|
+
if (options?.passthrough)
|
|
3259
|
+
scope = scope.passthrough(options.passthrough);
|
|
3241
3260
|
if (association?.type === 'HasMany') {
|
|
3242
|
-
this[associationName] = (await
|
|
3261
|
+
this[associationName] = (await scope.all());
|
|
3243
3262
|
}
|
|
3244
3263
|
else {
|
|
3245
|
-
this[associationName] = (await
|
|
3264
|
+
this[associationName] = (await scope.first());
|
|
3246
3265
|
}
|
|
3247
3266
|
}
|
|
3248
3267
|
return this[associationName];
|
|
3249
3268
|
}
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
* immediately without making a database query. If the association is not loaded,
|
|
3253
|
-
* it performs a database query to fetch the association.
|
|
3254
|
-
*
|
|
3255
|
-
* Unlike `association`, this method throws an exception if no associated
|
|
3256
|
-
* record is found, guaranteeing a non-null result.
|
|
3257
|
-
*
|
|
3258
|
-
* If a query is performed, the association is set on the model so that future calls to
|
|
3259
|
-
* access this association can access the already loaded value.
|
|
3260
|
-
*
|
|
3261
|
-
* ```ts
|
|
3262
|
-
* // return the already loaded user if it exists or fetch it from the database otherwise,
|
|
3263
|
-
* // throwing RecordNotFound either way if the associated model does not exist
|
|
3264
|
-
* const user = await post.associationOrFail('user')
|
|
3265
|
-
* ```
|
|
3266
|
-
*
|
|
3267
|
-
* @param associationName - The name of the BelongsTo or HasOne association
|
|
3268
|
-
* @returns The associated model instance (never null)
|
|
3269
|
-
* @throws RecordNotFound if no associated record exists
|
|
3270
|
-
*/
|
|
3271
|
-
async associationOrFail(associationName) {
|
|
3272
|
-
const response = await this.association(associationName);
|
|
3269
|
+
async associationOrFail(associationName, options) {
|
|
3270
|
+
const response = await this.association(associationName, options);
|
|
3273
3271
|
if (this[associationName] === null)
|
|
3274
3272
|
throw new RecordNotFound(this['sanitizedConstructorName']);
|
|
3275
3273
|
return response;
|
|
3276
3274
|
}
|
|
3275
|
+
///////////////////
|
|
3276
|
+
// end:associationOrFail
|
|
3277
|
+
///////////////////
|
|
3277
3278
|
/**
|
|
3278
3279
|
* Recursively loads all Dream associations referenced by `rendersOne` and `rendersMany`
|
|
3279
3280
|
* in a DreamSerializer. This traverses the entire content tree of serializers to automatically
|
|
@@ -36,38 +36,6 @@ export default class DreamClassTransactionBuilder {
|
|
|
36
36
|
async all(options = {}) {
|
|
37
37
|
return this.queryInstance().all(options);
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Paginates the results of your query, accepting a pageSize and page argument,
|
|
41
|
-
* which it uses to segment your query into pages, leveraging limit and offset
|
|
42
|
-
* to deliver your query to you in pages.
|
|
43
|
-
*
|
|
44
|
-
* ```ts
|
|
45
|
-
* await ApplicationModel.transaction(async txn => {
|
|
46
|
-
* const paginated = await User.txn(txn).paginate({ pageSize: 100, page: 2 })
|
|
47
|
-
* paginated.results
|
|
48
|
-
* // [ { User{id: 101}, User{id: 102}, ...}]
|
|
49
|
-
*
|
|
50
|
-
* paginated.recordCount
|
|
51
|
-
* // 350
|
|
52
|
-
*
|
|
53
|
-
* paginated.pageCount
|
|
54
|
-
* // 4
|
|
55
|
-
*
|
|
56
|
-
* paginated.currentPage
|
|
57
|
-
* // 2
|
|
58
|
-
* })
|
|
59
|
-
* ```
|
|
60
|
-
*
|
|
61
|
-
* @param opts.page - the page number that you want to fetch results for
|
|
62
|
-
* @param opts.pageSize - the number of results per page (optional)
|
|
63
|
-
* @returns results.recordCount - A number representing the total number of records matching your query
|
|
64
|
-
* @returns results.pageCount - The number of pages needed to encapsulate all the matching records
|
|
65
|
-
* @returns results.currentPage - The current page (same as what is provided in the paginate args)
|
|
66
|
-
* @returns results.results - An array of records matching the current record
|
|
67
|
-
*/
|
|
68
|
-
async paginate(opts) {
|
|
69
|
-
return await this.queryInstance().paginate(opts);
|
|
70
|
-
}
|
|
71
39
|
/**
|
|
72
40
|
* Retrieves the number of records corresponding
|
|
73
41
|
* to this model.
|
|
@@ -2,6 +2,7 @@ import CannotAssociationQueryOnUnpersistedDream from '../errors/associations/Can
|
|
|
2
2
|
import CannotCreateAssociationOnUnpersistedDream from '../errors/associations/CannotCreateAssociationOnUnpersistedDream.js';
|
|
3
3
|
import CannotDestroyAssociationOnUnpersistedDream from '../errors/associations/CannotDestroyAssociationOnUnpersistedDream.js';
|
|
4
4
|
import CannotUpdateAssociationOnUnpersistedDream from '../errors/associations/CannotUpdateAssociationOnUnpersistedDream.js';
|
|
5
|
+
import RecordNotFound from '../errors/RecordNotFound.js';
|
|
5
6
|
import associationQuery from './internal/associations/associationQuery.js';
|
|
6
7
|
import associationUpdateQuery from './internal/associations/associationUpdateQuery.js';
|
|
7
8
|
import createAssociation from './internal/associations/createAssociation.js';
|
|
@@ -396,6 +397,28 @@ export default class DreamInstanceTransactionBuilder {
|
|
|
396
397
|
async save({ skipHooks } = {}) {
|
|
397
398
|
await saveDream(this.dreamInstance, this.dreamTransaction, skipHooks ? { skipHooks } : undefined);
|
|
398
399
|
}
|
|
400
|
+
async association(associationName, options) {
|
|
401
|
+
if (!this.dreamInstance.loaded(associationName)) {
|
|
402
|
+
const association = this.dreamInstance['getAssociationMetadata'](associationName);
|
|
403
|
+
const associationQueryOptions = options?.required && { and: options.required };
|
|
404
|
+
let scope = this.associationQuery(associationName, associationQueryOptions);
|
|
405
|
+
if (options?.passthrough)
|
|
406
|
+
scope = scope.passthrough(options.passthrough);
|
|
407
|
+
if (association?.type === 'HasMany') {
|
|
408
|
+
this.dreamInstance[associationName] = (await scope.all());
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
this.dreamInstance[associationName] = (await scope.first());
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return this.dreamInstance[associationName];
|
|
415
|
+
}
|
|
416
|
+
async associationOrFail(associationName, options) {
|
|
417
|
+
const response = await this.association(associationName, options);
|
|
418
|
+
if (this.dreamInstance[associationName] === null)
|
|
419
|
+
throw new RecordNotFound(this.dreamInstance['sanitizedConstructorName']);
|
|
420
|
+
return response;
|
|
421
|
+
}
|
|
399
422
|
/**
|
|
400
423
|
* Returns a Query instance for the specified
|
|
401
424
|
* association on the current instance.
|
|
@@ -807,7 +807,8 @@ export default class Query {
|
|
|
807
807
|
* @returns A cloned Query with the passthrough data
|
|
808
808
|
*/
|
|
809
809
|
passthrough(passthroughOnStatement) {
|
|
810
|
-
|
|
810
|
+
const baseSelectQuery = this.baseSelectQuery && this.baseSelectQuery.clone({ passthroughOnStatement: passthroughOnStatement });
|
|
811
|
+
return this.clone({ passthroughOnStatement: passthroughOnStatement, baseSelectQuery });
|
|
811
812
|
}
|
|
812
813
|
/**
|
|
813
814
|
* Applies a where statement to the Query instance
|
|
@@ -1207,20 +1208,21 @@ export default class Query {
|
|
|
1207
1208
|
* @returns results.results - An array of records matching the current record
|
|
1208
1209
|
*/
|
|
1209
1210
|
async paginate(opts) {
|
|
1211
|
+
const options = opts;
|
|
1210
1212
|
if (this.limitStatement)
|
|
1211
1213
|
throw new CannotPaginateWithLimit();
|
|
1212
1214
|
if (this.offsetStatement)
|
|
1213
1215
|
throw new CannotPaginateWithOffset();
|
|
1214
|
-
const page = computedPaginatePage(
|
|
1216
|
+
const page = computedPaginatePage(options.page);
|
|
1215
1217
|
const recordCount = await this.count();
|
|
1216
|
-
const pageSize =
|
|
1218
|
+
const pageSize = Math.max(0, options.pageSize ?? 0) || DreamApp.getOrFail().paginationPageSize;
|
|
1217
1219
|
const pageCount = Math.ceil(recordCount / pageSize);
|
|
1218
1220
|
const query = this.orderStatements.length
|
|
1219
1221
|
? this
|
|
1220
1222
|
: this.order({ [this.namespacedPrimaryKey]: 'asc' });
|
|
1221
1223
|
const results = await query
|
|
1222
1224
|
.limit(pageSize)
|
|
1223
|
-
.offset((page - 1) * pageSize)
|
|
1225
|
+
.offset(((page - 1) * pageSize))
|
|
1224
1226
|
.all();
|
|
1225
1227
|
return {
|
|
1226
1228
|
recordCount,
|
|
@@ -1229,6 +1231,75 @@ export default class Query {
|
|
|
1229
1231
|
results,
|
|
1230
1232
|
};
|
|
1231
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Fast paginates the results of your query using cursor-based pagination,
|
|
1236
|
+
* accepting a pageSize and cursor argument. This method
|
|
1237
|
+
* provides better performance for large datasets by using cursor-based
|
|
1238
|
+
* pagination instead of offset-based pagination.
|
|
1239
|
+
*
|
|
1240
|
+
* ```ts
|
|
1241
|
+
* // First page
|
|
1242
|
+
* const firstPage = await User.order('email').fastPaginate({ pageSize: 100 })
|
|
1243
|
+
* firstPage.results
|
|
1244
|
+
* // [ { User{id: 1}, User{id: 2}, ...}]
|
|
1245
|
+
*
|
|
1246
|
+
* firstPage.cursor
|
|
1247
|
+
* // "100" (or null if no more pages)
|
|
1248
|
+
*
|
|
1249
|
+
* // Next page using cursor from previous result
|
|
1250
|
+
* const nextPage = await User.order('email').fastPaginate({
|
|
1251
|
+
* pageSize: 100,
|
|
1252
|
+
* cursor: firstPage.cursor
|
|
1253
|
+
* })
|
|
1254
|
+
* ```
|
|
1255
|
+
*
|
|
1256
|
+
* @param opts - Fast pagination options
|
|
1257
|
+
* @param opts.pageSize - the number of results per page (optional)
|
|
1258
|
+
* @param opts.cursor - identifier of where to start the next page; undefined to start from the beginning; null when no more pages
|
|
1259
|
+
* @returns results.cursor - identifier for the next page, or null if no more pages
|
|
1260
|
+
* @returns results.results - An array of records for the current page
|
|
1261
|
+
*/
|
|
1262
|
+
async fastPaginate(opts) {
|
|
1263
|
+
const options = opts;
|
|
1264
|
+
const pageSize = Math.max(0, options.pageSize ?? 0) || DreamApp.getOrFail().paginationPageSize;
|
|
1265
|
+
if (options === null)
|
|
1266
|
+
return { cursor: null, results: [] };
|
|
1267
|
+
if (this.limitStatement)
|
|
1268
|
+
throw new CannotPaginateWithLimit();
|
|
1269
|
+
if (this.offsetStatement)
|
|
1270
|
+
throw new CannotPaginateWithOffset();
|
|
1271
|
+
const explicitOrdering = !!this.orderStatements.length;
|
|
1272
|
+
let query = explicitOrdering ? this : this.order({ [this.namespacedPrimaryKey]: 'asc' });
|
|
1273
|
+
if (options.cursor) {
|
|
1274
|
+
if (explicitOrdering) {
|
|
1275
|
+
const lastItem = await query
|
|
1276
|
+
.where({ [this.dreamClass.primaryKey]: options.cursor })
|
|
1277
|
+
.firstOrFail();
|
|
1278
|
+
this.orderStatements.forEach(orderStatement => {
|
|
1279
|
+
const column = orderStatement.column;
|
|
1280
|
+
const direction = orderStatement.direction;
|
|
1281
|
+
switch (direction) {
|
|
1282
|
+
case 'asc':
|
|
1283
|
+
query = query.where({ [column]: ops.greaterThan(lastItem[column]) });
|
|
1284
|
+
break;
|
|
1285
|
+
case 'desc':
|
|
1286
|
+
query = query.where({ [column]: ops.lessThan(lastItem[column]) });
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
else {
|
|
1292
|
+
query = query.where({
|
|
1293
|
+
[this.dreamClass.primaryKey]: ops.greaterThan(options.cursor),
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
const results = await query.limit(pageSize).all();
|
|
1298
|
+
return {
|
|
1299
|
+
cursor: (results.length === pageSize && results.at(-1)?.primaryKeyValue().toString()) || null,
|
|
1300
|
+
results,
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1232
1303
|
/**
|
|
1233
1304
|
* Forces use of a database connection (e.g. 'primary') during the query.
|
|
1234
1305
|
*
|