Files
payload/test/sort/int.spec.ts
Anders Semb Hermansen 4d44c378ed feat: sort by multiple fields (#8799)
This change adds support for sort with multiple fields in local API and
REST API. Related discussion #2089

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2024-10-24 15:46:30 -04:00

472 lines
13 KiB
TypeScript

import type { CollectionSlug, Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
let payload: Payload
let restClient: NextRESTClient
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Sort', () => {
beforeAll(async () => {
const initialized = await initPayloadInt(dirname)
;({ payload, restClient } = initialized)
})
afterAll(async () => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
})
describe('Local API', () => {
beforeAll(async () => {
await createData('posts', [
{ text: 'Post 1', number: 1, number2: 10, group: { number: 100 } },
{ text: 'Post 2', number: 2, number2: 10, group: { number: 200 } },
{ text: 'Post 3', number: 3, number2: 5, group: { number: 150 } },
{ text: 'Post 10', number: 10, number2: 5, group: { number: 200 } },
{ text: 'Post 11', number: 11, number2: 20, group: { number: 150 } },
{ text: 'Post 12', number: 12, number2: 20, group: { number: 100 } },
])
await createData('default-sort', [
{ text: 'Post default-5 b', number: 5 },
{ text: 'Post default-10', number: 10 },
{ text: 'Post default-5 a', number: 5 },
{ text: 'Post default-1', number: 1 },
])
})
afterAll(async () => {
await payload.delete({ collection: 'posts', where: {} })
await payload.delete({ collection: 'default-sort', where: {} })
})
describe('Default sort', () => {
it('should sort posts by default definition in collection', async () => {
const posts = await payload.find({
collection: 'default-sort', // 'number,-text'
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post default-1',
'Post default-5 b',
'Post default-5 a',
'Post default-10',
])
})
})
describe('Sinlge sort field', () => {
it('should sort posts by text field', async () => {
const posts = await payload.find({
collection: 'posts',
sort: 'text',
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 1',
'Post 10',
'Post 11',
'Post 12',
'Post 2',
'Post 3',
])
})
it('should sort posts by text field desc', async () => {
const posts = await payload.find({
collection: 'posts',
sort: '-text',
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 3',
'Post 2',
'Post 12',
'Post 11',
'Post 10',
'Post 1',
])
})
it('should sort posts by number field', async () => {
const posts = await payload.find({
collection: 'posts',
sort: 'number',
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 1',
'Post 2',
'Post 3',
'Post 10',
'Post 11',
'Post 12',
])
})
it('should sort posts by number field desc', async () => {
const posts = await payload.find({
collection: 'posts',
sort: '-number',
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 12',
'Post 11',
'Post 10',
'Post 3',
'Post 2',
'Post 1',
])
})
})
describe('Sort by multiple fields', () => {
it('should sort posts by multiple fields', async () => {
const posts = await payload.find({
collection: 'posts',
sort: ['number2', 'number'],
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 3', // 5, 3
'Post 10', // 5, 10
'Post 1', // 10, 1
'Post 2', // 10, 2
'Post 11', // 20, 11
'Post 12', // 20, 12
])
})
it('should sort posts by multiple fields asc and desc', async () => {
const posts = await payload.find({
collection: 'posts',
sort: ['number2', '-number'],
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 10', // 5, 10
'Post 3', // 5, 3
'Post 2', // 10, 2
'Post 1', // 10, 1
'Post 12', // 20, 12
'Post 11', // 20, 11
])
})
it('should sort posts by multiple fields with group', async () => {
const posts = await payload.find({
collection: 'posts',
sort: ['-group.number', '-number'],
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 10', // 200, 10
'Post 2', // 200, 2
'Post 11', // 150, 11
'Post 3', // 150, 3
'Post 12', // 100, 12
'Post 1', // 100, 1
])
})
})
describe('Sort with drafts', () => {
beforeAll(async () => {
const testData1 = await payload.create({
collection: 'drafts',
data: { text: 'Post 1 draft', number: 10 },
draft: true,
})
await payload.update({
collection: 'drafts',
id: testData1.id,
data: { text: 'Post 1 draft updated', number: 20 },
draft: true,
})
await payload.update({
collection: 'drafts',
id: testData1.id,
data: { text: 'Post 1 draft updated', number: 30 },
draft: true,
})
await payload.update({
collection: 'drafts',
id: testData1.id,
data: { text: 'Post 1 published', number: 15 },
draft: false,
})
const testData2 = await payload.create({
collection: 'drafts',
data: { text: 'Post 2 draft', number: 1 },
draft: true,
})
await payload.update({
collection: 'drafts',
id: testData2.id,
data: { text: 'Post 2 published', number: 2 },
draft: false,
})
await payload.update({
collection: 'drafts',
id: testData2.id,
data: { text: 'Post 2 newdraft', number: 100 },
draft: true,
})
await payload.create({
collection: 'drafts',
data: { text: 'Post 3 draft', number: 3 },
draft: true,
})
})
it('should sort latest without draft', async () => {
const posts = await payload.find({
collection: 'drafts',
sort: 'number',
draft: false,
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 2 published', // 2
'Post 3 draft', // 3
'Post 1 published', // 15
])
})
it('should sort latest with draft', async () => {
const posts = await payload.find({
collection: 'drafts',
sort: 'number',
draft: true,
})
expect(posts.docs.map((post) => post.text)).toEqual([
'Post 3 draft', // 3
'Post 1 published', // 15
'Post 2 newdraft', // 100
])
})
it('should sort versions', async () => {
const posts = await payload.findVersions({
collection: 'drafts',
sort: 'version.number',
draft: false,
})
expect(posts.docs.map((post) => post.version.text)).toEqual([
'Post 2 draft', // 1
'Post 2 published', // 2
'Post 3 draft', // 3
'Post 1 draft', // 10
'Post 1 published', // 15
'Post 1 draft updated', // 20
'Post 1 draft updated', // 30
'Post 2 newdraft', // 100
])
})
})
describe('Localized sort', () => {
beforeAll(async () => {
const testData1 = await payload.create({
collection: 'localized',
data: { text: 'Post 1 english', number: 10 },
locale: 'en',
})
await payload.update({
collection: 'localized',
id: testData1.id,
data: { text: 'Post 1 norsk', number: 20 },
locale: 'nb',
})
const testData2 = await payload.create({
collection: 'localized',
data: { text: 'Post 2 english', number: 25 },
locale: 'en',
})
await payload.update({
collection: 'localized',
id: testData2.id,
data: { text: 'Post 2 norsk', number: 5 },
locale: 'nb',
})
})
it('should sort localized field', async () => {
const englishPosts = await payload.find({
collection: 'localized',
sort: 'number',
locale: 'en',
})
expect(englishPosts.docs.map((post) => post.text)).toEqual([
'Post 1 english', // 10
'Post 2 english', // 20
])
const norwegianPosts = await payload.find({
collection: 'localized',
sort: 'number',
locale: 'nb',
})
expect(norwegianPosts.docs.map((post) => post.text)).toEqual([
'Post 2 norsk', // 5
'Post 1 norsk', // 25
])
})
})
})
describe('REST API', () => {
beforeAll(async () => {
await createData('posts', [
{ text: 'Post 1', number: 1, number2: 10 },
{ text: 'Post 2', number: 2, number2: 10 },
{ text: 'Post 3', number: 3, number2: 5 },
{ text: 'Post 10', number: 10, number2: 5 },
{ text: 'Post 11', number: 11, number2: 20 },
{ text: 'Post 12', number: 12, number2: 20 },
])
})
afterAll(async () => {
await payload.delete({ collection: 'posts', where: {} })
})
describe('Sinlge sort field', () => {
it('should sort posts by text field', async () => {
const res = await restClient
.GET(`/posts`, {
query: {
sort: 'text',
},
})
.then((res) => res.json())
expect(res.docs.map((post) => post.text)).toEqual([
'Post 1',
'Post 10',
'Post 11',
'Post 12',
'Post 2',
'Post 3',
])
})
it('should sort posts by text field desc', async () => {
const res = await restClient
.GET(`/posts`, {
query: {
sort: '-text',
},
})
.then((res) => res.json())
expect(res.docs.map((post) => post.text)).toEqual([
'Post 3',
'Post 2',
'Post 12',
'Post 11',
'Post 10',
'Post 1',
])
})
it('should sort posts by number field', async () => {
const res = await restClient
.GET(`/posts`, {
query: {
sort: 'number',
},
})
.then((res) => res.json())
expect(res.docs.map((post) => post.text)).toEqual([
'Post 1',
'Post 2',
'Post 3',
'Post 10',
'Post 11',
'Post 12',
])
})
it('should sort posts by number field desc', async () => {
const res = await restClient
.GET(`/posts`, {
query: {
sort: '-number',
},
})
.then((res) => res.json())
expect(res.docs.map((post) => post.text)).toEqual([
'Post 12',
'Post 11',
'Post 10',
'Post 3',
'Post 2',
'Post 1',
])
})
})
describe('Sort by multiple fields', () => {
it('should sort posts by multiple fields', async () => {
const res = await restClient
.GET(`/posts`, {
query: {
sort: 'number2,number',
},
})
.then((res) => res.json())
expect(res.docs.map((post) => post.text)).toEqual([
'Post 3', // 5, 3
'Post 10', // 5, 10
'Post 1', // 10, 1
'Post 2', // 10, 2
'Post 11', // 20, 11
'Post 12', // 20, 12
])
})
it('should sort posts by multiple fields asc and desc', async () => {
const res = await restClient
.GET(`/posts`, {
query: {
sort: 'number2,-number',
},
})
.then((res) => res.json())
expect(res.docs.map((post) => post.text)).toEqual([
'Post 10', // 5, 10
'Post 3', // 5, 3
'Post 2', // 10, 2
'Post 1', // 10, 1
'Post 12', // 20, 12
'Post 11', // 20, 11
])
})
})
})
})
async function createData(collection: CollectionSlug, data: Record<string, any>[]) {
for (const item of data) {
await payload.create({ collection, data: item })
}
}