perf(db-mongodb): improve performance of all operations, up to 50% faster (#9594)

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% |
This commit is contained in:
Sasha
2024-12-19 20:20:39 +02:00
committed by GitHub
parent 034b442699
commit e468292039
46 changed files with 1338 additions and 940 deletions

View File

@@ -53,10 +53,12 @@ describe('Relationship Fields', () => {
collection: versionedRelationshipFieldSlug,
data: {
title: 'Version 1 Title',
relationshipField: {
value: relatedDoc.id,
relationTo: collection1Slug,
},
relationshipField: [
{
value: relatedDoc.id,
relationTo: collection1Slug,
},
],
},
})

View File

@@ -1134,6 +1134,30 @@ describe('Fields', () => {
expect(doc.localized).toEqual(localized)
expect(doc.group).toMatchObject(group)
})
it('should clear a point field', async () => {
if (payload.db.name === 'sqlite') {
return
}
const doc = await payload.create({
collection: 'point-fields',
data: {
point: [7, -7],
group: {
point: [7, -7],
},
},
})
const res = await payload.update({
collection: 'point-fields',
id: doc.id,
data: { group: { point: null } },
})
expect(res.group.point).toBeFalsy()
})
})
describe('unique indexes', () => {

View File

@@ -154,7 +154,10 @@ describe('Joins Field', () => {
collection: categoriesSlug,
})
expect(Object.keys(categoryWithPosts)).toStrictEqual(['id', 'group'])
expect(categoryWithPosts).toStrictEqual({
id: categoryWithPosts.id,
group: categoryWithPosts.group,
})
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('id')

View File

@@ -1633,7 +1633,10 @@ describe('Select', () => {
},
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
expect(res).toStrictEqual({
id: res.id,
text: res.text,
})
})
it('should apply select with updateByID', async () => {
@@ -1646,13 +1649,18 @@ describe('Select', () => {
select: { text: true },
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
expect(res).toStrictEqual({
id: res.id,
text: res.text,
})
})
it('should apply select with updateBulk', async () => {
const post = await createPost()
const res = await payload.update({
const {
docs: [res],
} = await payload.update({
collection: 'posts',
where: {
id: {
@@ -1663,7 +1671,10 @@ describe('Select', () => {
select: { text: true },
})
expect(Object.keys(res.docs[0])).toStrictEqual(['id', 'text'])
expect(res).toStrictEqual({
id: res.id,
text: res.text,
})
})
it('should apply select with deleteByID', async () => {
@@ -1675,13 +1686,18 @@ describe('Select', () => {
select: { text: true },
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
expect(res).toStrictEqual({
id: res.id,
text: res.text,
})
})
it('should apply select with deleteBulk', async () => {
const post = await createPost()
const res = await payload.delete({
const {
docs: [res],
} = await payload.delete({
collection: 'posts',
where: {
id: {
@@ -1691,7 +1707,10 @@ describe('Select', () => {
select: { text: true },
})
expect(Object.keys(res.docs[0])).toStrictEqual(['id', 'text'])
expect(res).toStrictEqual({
id: res.id,
text: res.text,
})
})
it('should apply select with duplicate', async () => {
@@ -1703,7 +1722,10 @@ describe('Select', () => {
select: { text: true },
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
expect(res).toStrictEqual({
id: res.id,
text: res.text,
})
})
})