Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: local / REST sort by multiple fields #3236

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions src/collections/operations/find.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -10,13 +10,14 @@ 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
where?: Where
page?: number
limit?: number
sort?: string
sort?: Sort
depth?: number
currentDepth?: number
req?: PayloadRequest
Expand Down Expand Up @@ -115,16 +116,25 @@ async function find<T extends TypeWithID & Record<string, unknown>>(

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 = buildObjectSortParam({
sort: args.sort,
config: payload.config,
fields: collectionConfig.fields,
locale,
});
} 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;
Expand Down
4 changes: 2 additions & 2 deletions src/collections/operations/local/find.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -22,7 +22,7 @@ export type Options<T extends keyof GeneratedTypes['collections']> = {
disableErrors?: boolean
showHiddenFields?: boolean
pagination?: boolean
sort?: string
sort?: Sort
where?: Where
draft?: boolean
req?: PayloadRequest
Expand Down
4 changes: 2 additions & 2 deletions src/collections/requestHandlers/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,7 +26,7 @@ export default async function findHandler<T extends TypeWithID = any>(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',
});
Expand Down
29 changes: 29 additions & 0 deletions src/mongoose/buildObjectSortParam.ts
Original file line number Diff line number Diff line change
@@ -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<SortObject>((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;
}, {});
};
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
38 changes: 38 additions & 0 deletions test/collections-rest/config.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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: [
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -357,5 +373,27 @@ export default buildConfigWithDefaults({
name: 'name',
},
});

const sortMultipleData = [
{
number: 10,
bool: true,
},
{
number: 30,
bool: false,
},
{
number: 5,
bool: true,
},
];

await mapAsync(sortMultipleData, async (data) => {
payload.create({
collection: sortMultipleSlug,
data,
});
});
},
});
13 changes: 12 additions & 1 deletion test/collections-rest/payload-types.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -14,6 +15,7 @@ export interface Config {
'custom-id': CustomId;
'custom-id-number': CustomIdNumber;
'error-on-hooks': ErrorOnHook;
'sort-multiple': SortMultiple;
users: User;
};
globals: {};
Expand Down Expand Up @@ -110,13 +112,22 @@ export interface ErrorOnHook {
updatedAt: string;
createdAt: string;
}
export interface SortMultiple {
id: string;
number?: number;
bool?: boolean;
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;
Expand Down