This PR improves speed and memory efficiency across all operations with the Mongoose adapter. ### How? - Removes Mongoose layer from all database calls, instead uses MongoDB directly. (this doesn't remove building mongoose schema since it's still needed for indexes + users in theory can use it) - Replaces deep copying of read results using `JSON.parse(JSON.stringify(data))` with the `transform` `operation: 'read'` function which converts Date's, ObjectID's in relationships / joins to strings. As before, it also handles transformations for write operations. - Faster `hasNearConstraint` for potentially large `where`'s - `traverseFields` now can accept `flattenedFields` which we use in `transform`. Less recursive calls with tabs/rows/collapsible Additional fixes - Uses current transaction for querying nested relationships properties in `buildQuery`, previously it wasn't used which could've led to wrong results - Allows to clear not required point fields with passing `null` from the Local API. Previously it didn't work in both, MongoDB and Postgres Benchmarks using this file https://github.com/payloadcms/payload/blob/chore/db-benchmark/test/_community/int.spec.ts ### Small Dataset Performance | Metric | Before Optimization | After Optimization | Improvement (%) | |---------------------------|---------------------|--------------------|-----------------| | Average FULL (ms) | 1170 | 844 | 27.86% | | `payload.db.create` (ms) | 1413 | 691 | 51.12% | | `payload.db.find` (ms) | 2856 | 2204 | 22.83% | | `payload.db.deleteMany` (ms) | 15206 | 8439 | 44.53% | | `payload.db.updateOne` (ms) | 21444 | 12162 | 43.30% | | `payload.db.findOne` (ms) | 159 | 112 | 29.56% | | `payload.db.deleteOne` (ms) | 3729 | 2578 | 30.89% | | DB small FULL (ms) | 64473 | 46451 | 27.93% | --- ### Medium Dataset Performance | Metric | Before Optimization | After Optimization | Improvement (%) | |---------------------------|---------------------|--------------------|-----------------| | Average FULL (ms) | 9407 | 6210 | 33.99% | | `payload.db.create` (ms) | 10270 | 4321 | 57.93% | | `payload.db.find` (ms) | 20814 | 16036 | 22.93% | | `payload.db.deleteMany` (ms) | 126351 | 61789 | 51.11% | | `payload.db.updateOne` (ms) | 201782 | 99943 | 50.49% | | `payload.db.findOne` (ms) | 1081 | 817 | 24.43% | | `payload.db.deleteOne` (ms) | 28534 | 23363 | 18.12% | | DB medium FULL (ms) | 519518 | 342194 | 34.13% | --- ### Large Dataset Performance | Metric | Before Optimization | After Optimization | Improvement (%) | |---------------------------|---------------------|--------------------|-----------------| | Average FULL (ms) | 26575 | 17509 | 34.14% | | `payload.db.create` (ms) | 29085 | 12196 | 58.08% | | `payload.db.find` (ms) | 58497 | 43838 | 25.04% | | `payload.db.deleteMany` (ms) | 372195 | 173218 | 53.47% | | `payload.db.updateOne` (ms) | 544089 | 288350 | 47.00% | | `payload.db.findOne` (ms) | 3058 | 2197 | 28.14% | | `payload.db.deleteOne` (ms) | 82444 | 64730 | 21.49% | | DB large FULL (ms) | 1461097 | 969714 | 33.62% |
109 lines
2.9 KiB
TypeScript
109 lines
2.9 KiB
TypeScript
import type { Payload } from 'payload'
|
|
|
|
import path from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
|
import type { Collection1 } from './payload-types.js'
|
|
|
|
import { devUser } from '../credentials.js'
|
|
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
|
import { collection1Slug, versionedRelationshipFieldSlug } from './collectionSlugs.js'
|
|
|
|
let payload: Payload
|
|
let restClient: NextRESTClient
|
|
|
|
const { email, password } = devUser
|
|
|
|
const filename = fileURLToPath(import.meta.url)
|
|
const dirname = path.dirname(filename)
|
|
|
|
describe('Relationship Fields', () => {
|
|
beforeAll(async () => {
|
|
const initialized = await initPayloadInt(dirname)
|
|
;({ payload, restClient } = initialized)
|
|
|
|
await restClient.login({
|
|
slug: 'users',
|
|
credentials: {
|
|
email,
|
|
password,
|
|
},
|
|
})
|
|
})
|
|
|
|
afterAll(async () => {
|
|
if (typeof payload.db.destroy === 'function') {
|
|
await payload.db.destroy()
|
|
}
|
|
})
|
|
|
|
describe('Versioned Relationship Field', () => {
|
|
let version2ID: string
|
|
const relatedDocName = 'Related Doc'
|
|
beforeAll(async () => {
|
|
const relatedDoc = await payload.create({
|
|
collection: collection1Slug,
|
|
data: {
|
|
name: relatedDocName,
|
|
},
|
|
})
|
|
|
|
const version1 = await payload.create({
|
|
collection: versionedRelationshipFieldSlug,
|
|
data: {
|
|
title: 'Version 1 Title',
|
|
relationshipField: [
|
|
{
|
|
value: relatedDoc.id,
|
|
relationTo: collection1Slug,
|
|
},
|
|
],
|
|
},
|
|
})
|
|
|
|
const version2 = await payload.update({
|
|
collection: versionedRelationshipFieldSlug,
|
|
id: version1.id,
|
|
data: {
|
|
title: 'Version 2 Title',
|
|
},
|
|
})
|
|
|
|
const versions = await payload.findVersions({
|
|
collection: versionedRelationshipFieldSlug,
|
|
where: {
|
|
parent: {
|
|
equals: version2.id,
|
|
},
|
|
},
|
|
sort: '-updatedAt',
|
|
limit: 1,
|
|
})
|
|
|
|
version2ID = versions.docs[0].id
|
|
})
|
|
it('should return the correct versioned relationship field via REST', async () => {
|
|
const version2Data = await restClient
|
|
.GET(`/${versionedRelationshipFieldSlug}/versions/${version2ID}?locale=all`)
|
|
.then((res) => res.json())
|
|
|
|
expect(version2Data.version.title).toEqual('Version 2 Title')
|
|
expect(version2Data.version.relationshipField[0].value.name).toEqual(relatedDocName)
|
|
})
|
|
|
|
it('should return the correct versioned relationship field via LocalAPI', async () => {
|
|
const version2Data = await payload.findVersionByID({
|
|
collection: versionedRelationshipFieldSlug,
|
|
id: version2ID,
|
|
locale: 'all',
|
|
})
|
|
|
|
expect(version2Data.version.title).toEqual('Version 2 Title')
|
|
expect((version2Data.version.relationshipField[0].value as Collection1).name).toEqual(
|
|
relatedDocName,
|
|
)
|
|
})
|
|
})
|
|
})
|