From 1c620df34775b0c1fc111d4d29b1767979b48a9c Mon Sep 17 00:00:00 2001 From: Ritsu Date: Sun, 27 Aug 2023 21:13:16 +0300 Subject: [PATCH 1/4] feat: local / rest sort by multiple fields --- src/collections/operations/find.ts | 27 +++++++++++++----------- src/collections/operations/local/find.ts | 4 ++-- src/collections/requestHandlers/find.ts | 4 ++-- src/types/index.ts | 2 ++ test/collections-rest/payload-types.ts | 13 +++++++++++- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/collections/operations/find.ts b/src/collections/operations/find.ts index ce7f847f5d3..460804c4f78 100644 --- a/src/collections/operations/find.ts +++ b/src/collections/operations/find.ts @@ -1,4 +1,4 @@ -import { Where } from '../../types'; +import { Sort, Where } from '../../types'; import { PayloadRequest } from '../../express/types'; import executeAccess from '../../auth/executeAccess'; import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'; @@ -16,7 +16,7 @@ export type Arguments = { where?: Where page?: number limit?: number - sort?: string + sort?: Sort depth?: number currentDepth?: number req?: PayloadRequest @@ -115,16 +115,19 @@ async function find>( let sort; if (!hasNearConstraint) { - const [sortProperty, sortOrder] = buildSortParam({ - sort: args.sort ?? collectionConfig.defaultSort, - config: payload.config, - fields: collectionConfig.fields, - timestamps: collectionConfig.timestamps, - locale, - }); - sort = { - [sortProperty]: sortOrder, - }; + if (typeof args.sort === 'object') sort = args.sort; + else { + const [sortProperty, sortOrder] = buildSortParam({ + sort: args.sort ?? collectionConfig.defaultSort, + config: payload.config, + fields: collectionConfig.fields, + timestamps: collectionConfig.timestamps, + locale, + }); + sort = { + [sortProperty]: sortOrder, + }; + } } const usePagination = pagination && limit !== 0; diff --git a/src/collections/operations/local/find.ts b/src/collections/operations/local/find.ts index 83cf156bf18..7bbd6f6f401 100644 --- a/src/collections/operations/local/find.ts +++ b/src/collections/operations/local/find.ts @@ -1,6 +1,6 @@ import { Config as GeneratedTypes } from 'payload/generated-types'; import { PaginatedDocs } from '../../../mongoose/types'; -import { Document, Where } from '../../../types'; +import { Document, Sort, Where } from '../../../types'; import { Payload } from '../../../payload'; import { PayloadRequest, RequestContext } from '../../../express/types'; import find from '../find'; @@ -22,7 +22,7 @@ export type Options = { disableErrors?: boolean showHiddenFields?: boolean pagination?: boolean - sort?: string + sort?: Sort where?: Where draft?: boolean req?: PayloadRequest diff --git a/src/collections/requestHandlers/find.ts b/src/collections/requestHandlers/find.ts index 9f94102a848..262e4e870fd 100644 --- a/src/collections/requestHandlers/find.ts +++ b/src/collections/requestHandlers/find.ts @@ -4,7 +4,7 @@ import { PayloadRequest } from '../../express/types'; import { TypeWithID } from '../config/types'; import { PaginatedDocs } from '../../mongoose/types'; import find from '../operations/find'; -import { Where } from '../../types'; +import { Sort, Where } from '../../types'; import { isNumber } from '../../utilities/isNumber'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -26,7 +26,7 @@ export default async function findHandler(req: Paylo where: req.query.where as Where, // This is a little shady page, limit: isNumber(req.query.limit) ? Number(req.query.limit) : undefined, - sort: req.query.sort as string, + sort: req.query.sort as Sort, depth: isNumber(req.query.depth) ? Number(req.query.depth) : undefined, draft: req.query.draft === 'true', }); diff --git a/src/types/index.ts b/src/types/index.ts index d05551577b8..9adf5460ee2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -17,6 +17,8 @@ export type Where = { and?: Where[]; }; +export type Sort = string | { [key: string]: -1 | 1 | 'asc' | 'desc' } + // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Document = any; diff --git a/test/collections-rest/payload-types.ts b/test/collections-rest/payload-types.ts index a1507d02f03..5359cb26712 100644 --- a/test/collections-rest/payload-types.ts +++ b/test/collections-rest/payload-types.ts @@ -1,4 +1,5 @@ /* tslint:disable */ +/* eslint-disable */ /** * This file was automatically generated by Payload. * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, @@ -14,6 +15,7 @@ export interface Config { 'custom-id': CustomId; 'custom-id-number': CustomIdNumber; 'error-on-hooks': ErrorOnHook; + 'sort-by-multiple': SortByMultiple; users: User; }; globals: {}; @@ -110,13 +112,22 @@ export interface ErrorOnHook { updatedAt: string; createdAt: string; } +export interface SortByMultiple { + id: string; + isFavorite?: boolean; + sortSecond?: number; + updatedAt: string; + createdAt: string; +} export interface User { id: string; updatedAt: string; createdAt: string; - email?: string; + email: string; resetPasswordToken?: string; resetPasswordExpiration?: string; + salt?: string; + hash?: string; loginAttempts?: number; lockUntil?: string; password?: string; From e15345c079370fcc2672a3dccc2ef856fcabef6d Mon Sep 17 00:00:00 2001 From: Ritsu Date: Sun, 27 Aug 2023 22:42:10 +0300 Subject: [PATCH 2/4] buildObjectSortParam for localized fields --- src/collections/operations/find.ts | 11 ++++-- src/mongoose/buildObjectSortParam.ts | 29 ++++++++++++++++ test/collections-rest/config.ts | 50 ++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/mongoose/buildObjectSortParam.ts diff --git a/src/collections/operations/find.ts b/src/collections/operations/find.ts index 460804c4f78..5ef4fc54c3f 100644 --- a/src/collections/operations/find.ts +++ b/src/collections/operations/find.ts @@ -10,6 +10,7 @@ import { AccessResult } from '../../config/types'; import { afterRead } from '../../fields/hooks/afterRead'; import { queryDrafts } from '../../versions/drafts/queryDrafts'; import { buildAfterOperation } from './utils'; +import { buildObjectSortParam } from '../../mongoose/buildObjectSortParam'; export type Arguments = { collection: Collection @@ -115,8 +116,14 @@ async function find>( let sort; if (!hasNearConstraint) { - if (typeof args.sort === 'object') sort = args.sort; - else { + if (typeof args.sort === 'object') { + sort = buildObjectSortParam({ + sort: args.sort, + config: payload.config, + fields: collectionConfig.fields, + locale, + }); + } else { const [sortProperty, sortOrder] = buildSortParam({ sort: args.sort ?? collectionConfig.defaultSort, config: payload.config, diff --git a/src/mongoose/buildObjectSortParam.ts b/src/mongoose/buildObjectSortParam.ts new file mode 100644 index 00000000000..0fc9be9e6d9 --- /dev/null +++ b/src/mongoose/buildObjectSortParam.ts @@ -0,0 +1,29 @@ +import { Config } from '../config/types'; +import { getLocalizedSortProperty } from './getLocalizedSortProperty'; +import { Field } from '../fields/config/types'; + +type SortObject = { [key: string]: 1 | -1 | 'asc' | 'desc' }; + +type Args = { + sort: SortObject + config: Config + fields: Field[] + locale: string +} + +export const buildObjectSortParam = ({ sort: incomingSort, config, fields, locale }: Args): SortObject => { + return Object.entries(incomingSort).reduce((acc, [property, order]) => { + if (property === 'id') { + acc._id = order; + return acc; + } + const localizedProperty = getLocalizedSortProperty({ + segments: property.split('.'), + config, + fields, + locale, + }); + acc[localizedProperty] = order; + return acc; + }, {}); +}; diff --git a/test/collections-rest/config.ts b/test/collections-rest/config.ts index 15dda6d89c5..4612a4c92c9 100644 --- a/test/collections-rest/config.ts +++ b/test/collections-rest/config.ts @@ -1,6 +1,7 @@ import type { CollectionConfig } from '../../src/collections/config/types'; import { devUser } from '../credentials'; import { buildConfigWithDefaults } from '../buildConfigWithDefaults'; +import { mapAsync } from '../../src/utilities/mapAsync'; export interface Relation { id: string; @@ -33,6 +34,7 @@ export const pointSlug = 'point'; export const customIdSlug = 'custom-id'; export const customIdNumberSlug = 'custom-id-number'; export const errorOnHookSlug = 'error-on-hooks'; +export const sortMultipleSlug = 'sort-multiple'; export default buildConfigWithDefaults({ endpoints: [ @@ -266,6 +268,20 @@ export default buildConfigWithDefaults({ }, ], }, + { + slug: sortMultipleSlug, + access: openAccess, + fields: [ + { + name: 'number', + type: 'number', + }, + { + name: 'bool', + type: 'checkbox', + }, + ], + }, ], onInit: async (payload) => { await payload.create({ @@ -357,5 +373,39 @@ export default buildConfigWithDefaults({ name: 'name', }, }); + + const sortMultipleData = [ + { + number: 10, + bool: true, + }, + { + number: 30, + bool: false, + }, + { + number: 5, + bool: true, + }, + { + number: 35, + bool: false, + }, + { + number: 1, + bool: true, + }, + { + number: 40, + bool: true, + }, + ]; + + await mapAsync(sortMultipleData, async (data) => { + payload.create({ + collection: sortMultipleSlug, + data, + }); + }); }, }); From be757cfc89a361be604ab3ecaa2a800cd02b842b Mon Sep 17 00:00:00 2001 From: Ritsu Date: Sun, 27 Aug 2023 22:59:16 +0300 Subject: [PATCH 3/4] chore: less data in sort multiple test config --- test/collections-rest/config.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/collections-rest/config.ts b/test/collections-rest/config.ts index 4612a4c92c9..78126a265e6 100644 --- a/test/collections-rest/config.ts +++ b/test/collections-rest/config.ts @@ -387,18 +387,6 @@ export default buildConfigWithDefaults({ number: 5, bool: true, }, - { - number: 35, - bool: false, - }, - { - number: 1, - bool: true, - }, - { - number: 40, - bool: true, - }, ]; await mapAsync(sortMultipleData, async (data) => { From c0f48175e824cd3fe622fa119c68aac106c2f518 Mon Sep 17 00:00:00 2001 From: Ritsu Date: Sun, 27 Aug 2023 23:12:40 +0300 Subject: [PATCH 4/4] chore: generate-types fix old collections-rest --- test/collections-rest/payload-types.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/collections-rest/payload-types.ts b/test/collections-rest/payload-types.ts index 5359cb26712..72d27d3c254 100644 --- a/test/collections-rest/payload-types.ts +++ b/test/collections-rest/payload-types.ts @@ -15,7 +15,7 @@ export interface Config { 'custom-id': CustomId; 'custom-id-number': CustomIdNumber; 'error-on-hooks': ErrorOnHook; - 'sort-by-multiple': SortByMultiple; + 'sort-multiple': SortMultiple; users: User; }; globals: {}; @@ -112,10 +112,10 @@ export interface ErrorOnHook { updatedAt: string; createdAt: string; } -export interface SortByMultiple { +export interface SortMultiple { id: string; - isFavorite?: boolean; - sortSecond?: number; + number?: number; + bool?: boolean; updatedAt: string; createdAt: string; }