Skip to content

Commit 1dc748d

Browse files
authored
perf(db-mongodb): remove JSON.parse(JSON.stringify) copying of results (#11293)
Improves performance and optimizes memory usage for mongodb adapter by cutting down copying of results via `JSON.parse(JSON.stringify())`. Instead, `transform` does necessary transformations (`ObjectID` -> `string,` `Date` -> `string`) without any copying
1 parent c7c5018 commit 1dc748d

23 files changed

+579
-421
lines changed

packages/db-mongodb/src/create.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { MongooseAdapter } from './index.js'
55

66
import { getSession } from './utilities/getSession.js'
77
import { handleError } from './utilities/handleError.js'
8-
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
8+
import { transform } from './utilities/transform.js'
99

1010
export const create: Create = async function create(
1111
this: MongooseAdapter,
@@ -18,31 +18,31 @@ export const create: Create = async function create(
1818

1919
let doc
2020

21-
const sanitizedData = sanitizeRelationshipIDs({
22-
config: this.payload.config,
21+
transform({
22+
adapter: this,
2323
data,
2424
fields: this.payload.collections[collection].config.fields,
25+
operation: 'write',
2526
})
2627

2728
if (this.payload.collections[collection].customIDType) {
28-
sanitizedData._id = sanitizedData.id
29+
data._id = data.id
2930
}
3031

3132
try {
32-
;[doc] = await Model.create([sanitizedData], options)
33+
;[doc] = await Model.create([data], options)
3334
} catch (error) {
3435
handleError({ collection, error, req })
3536
}
3637

37-
// doc.toJSON does not do stuff like converting ObjectIds to string, or date strings to date objects. That's why we use JSON.parse/stringify here
38-
const result: Document = JSON.parse(JSON.stringify(doc))
39-
const verificationToken = doc._verificationToken
38+
doc = doc.toObject()
4039

41-
// custom id type reset
42-
result.id = result._id
43-
if (verificationToken) {
44-
result._verificationToken = verificationToken
45-
}
40+
transform({
41+
adapter: this,
42+
data: doc,
43+
fields: this.payload.collections[collection].config.fields,
44+
operation: 'read',
45+
})
4646

47-
return result
47+
return doc
4848
}

packages/db-mongodb/src/createGlobal.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,36 @@ import type { CreateGlobal } from 'payload'
44
import type { MongooseAdapter } from './index.js'
55

66
import { getSession } from './utilities/getSession.js'
7-
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
8-
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
7+
import { transform } from './utilities/transform.js'
98

109
export const createGlobal: CreateGlobal = async function createGlobal(
1110
this: MongooseAdapter,
1211
{ slug, data, req },
1312
) {
1413
const Model = this.globals
1514

16-
const global = sanitizeRelationshipIDs({
17-
config: this.payload.config,
18-
data: {
19-
globalType: slug,
20-
...data,
21-
},
15+
transform({
16+
adapter: this,
17+
data,
2218
fields: this.payload.config.globals.find((globalConfig) => globalConfig.slug === slug).fields,
19+
globalSlug: slug,
20+
operation: 'write',
2321
})
2422

2523
const options: CreateOptions = {
2624
session: await getSession(this, req),
2725
}
2826

29-
let [result] = (await Model.create([global], options)) as any
27+
let [result] = (await Model.create([data], options)) as any
3028

31-
result = JSON.parse(JSON.stringify(result))
29+
result = result.toObject()
3230

33-
// custom id type reset
34-
result.id = result._id
35-
result = sanitizeInternalFields(result)
31+
transform({
32+
adapter: this,
33+
data: result,
34+
fields: this.payload.config.globals.find((globalConfig) => globalConfig.slug === slug).fields,
35+
operation: 'read',
36+
})
3637

3738
return result
3839
}

packages/db-mongodb/src/createGlobalVersion.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { CreateOptions } from 'mongoose'
22

3-
import { buildVersionGlobalFields, type CreateGlobalVersion, type Document } from 'payload'
3+
import { buildVersionGlobalFields, type CreateGlobalVersion } from 'payload'
44

55
import type { MongooseAdapter } from './index.js'
66

77
import { getSession } from './utilities/getSession.js'
8-
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
8+
import { transform } from './utilities/transform.js'
99

1010
export const createGlobalVersion: CreateGlobalVersion = async function createGlobalVersion(
1111
this: MongooseAdapter,
@@ -26,25 +26,30 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
2626
session: await getSession(this, req),
2727
}
2828

29-
const data = sanitizeRelationshipIDs({
30-
config: this.payload.config,
31-
data: {
32-
autosave,
33-
createdAt,
34-
latest: true,
35-
parent,
36-
publishedLocale,
37-
snapshot,
38-
updatedAt,
39-
version: versionData,
40-
},
41-
fields: buildVersionGlobalFields(
42-
this.payload.config,
43-
this.payload.config.globals.find((global) => global.slug === globalSlug),
44-
),
29+
const data = {
30+
autosave,
31+
createdAt,
32+
latest: true,
33+
parent,
34+
publishedLocale,
35+
snapshot,
36+
updatedAt,
37+
version: versionData,
38+
}
39+
40+
const fields = buildVersionGlobalFields(
41+
this.payload.config,
42+
this.payload.config.globals.find((global) => global.slug === globalSlug),
43+
)
44+
45+
transform({
46+
adapter: this,
47+
data,
48+
fields,
49+
operation: 'write',
4550
})
4651

47-
const [doc] = await VersionModel.create([data], options, req)
52+
let [doc] = await VersionModel.create([data], options, req)
4853

4954
await VersionModel.updateMany(
5055
{
@@ -70,13 +75,14 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
7075
options,
7176
)
7277

73-
const result: Document = JSON.parse(JSON.stringify(doc))
74-
const verificationToken = doc._verificationToken
78+
doc = doc.toObject()
7579

76-
// custom id type reset
77-
result.id = result._id
78-
if (verificationToken) {
79-
result._verificationToken = verificationToken
80-
}
81-
return result
80+
transform({
81+
adapter: this,
82+
data: doc,
83+
fields,
84+
operation: 'read',
85+
})
86+
87+
return doc
8288
}
Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import type { CreateOptions } from 'mongoose'
22

3-
import { Types } from 'mongoose'
4-
import { buildVersionCollectionFields, type CreateVersion, type Document } from 'payload'
3+
import { buildVersionCollectionFields, type CreateVersion } from 'payload'
54

65
import type { MongooseAdapter } from './index.js'
76

87
import { getSession } from './utilities/getSession.js'
9-
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
8+
import { transform } from './utilities/transform.js'
109

1110
export const createVersion: CreateVersion = async function createVersion(
1211
this: MongooseAdapter,
@@ -27,25 +26,30 @@ export const createVersion: CreateVersion = async function createVersion(
2726
session: await getSession(this, req),
2827
}
2928

30-
const data = sanitizeRelationshipIDs({
31-
config: this.payload.config,
32-
data: {
33-
autosave,
34-
createdAt,
35-
latest: true,
36-
parent,
37-
publishedLocale,
38-
snapshot,
39-
updatedAt,
40-
version: versionData,
41-
},
42-
fields: buildVersionCollectionFields(
43-
this.payload.config,
44-
this.payload.collections[collectionSlug].config,
45-
),
29+
const data = {
30+
autosave,
31+
createdAt,
32+
latest: true,
33+
parent,
34+
publishedLocale,
35+
snapshot,
36+
updatedAt,
37+
version: versionData,
38+
}
39+
40+
const fields = buildVersionCollectionFields(
41+
this.payload.config,
42+
this.payload.collections[collectionSlug].config,
43+
)
44+
45+
transform({
46+
adapter: this,
47+
data,
48+
fields,
49+
operation: 'write',
4650
})
4751

48-
const [doc] = await VersionModel.create([data], options, req)
52+
let [doc] = await VersionModel.create([data], options, req)
4953

5054
const parentQuery = {
5155
$or: [
@@ -56,13 +60,6 @@ export const createVersion: CreateVersion = async function createVersion(
5660
},
5761
],
5862
}
59-
if (data.parent instanceof Types.ObjectId) {
60-
parentQuery.$or.push({
61-
parent: {
62-
$eq: data.parent.toString(),
63-
},
64-
})
65-
}
6663

6764
await VersionModel.updateMany(
6865
{
@@ -89,13 +86,14 @@ export const createVersion: CreateVersion = async function createVersion(
8986
options,
9087
)
9188

92-
const result: Document = JSON.parse(JSON.stringify(doc))
93-
const verificationToken = doc._verificationToken
89+
doc = doc.toObject()
9490

95-
// custom id type reset
96-
result.id = result._id
97-
if (verificationToken) {
98-
result._verificationToken = verificationToken
99-
}
100-
return result
91+
transform({
92+
adapter: this,
93+
data: doc,
94+
fields,
95+
operation: 'read',
96+
})
97+
98+
return doc
10199
}

packages/db-mongodb/src/deleteOne.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { QueryOptions } from 'mongoose'
2-
import type { DeleteOne, Document } from 'payload'
2+
import type { DeleteOne } from 'payload'
33

44
import type { MongooseAdapter } from './index.js'
55

66
import { buildQuery } from './queries/buildQuery.js'
77
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
88
import { getSession } from './utilities/getSession.js'
9-
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
9+
import { transform } from './utilities/transform.js'
1010

1111
export const deleteOne: DeleteOne = async function deleteOne(
1212
this: MongooseAdapter,
@@ -35,11 +35,12 @@ export const deleteOne: DeleteOne = async function deleteOne(
3535
return null
3636
}
3737

38-
let result: Document = JSON.parse(JSON.stringify(doc))
39-
40-
// custom id type reset
41-
result.id = result._id
42-
result = sanitizeInternalFields(result)
38+
transform({
39+
adapter: this,
40+
data: doc,
41+
fields: this.payload.collections[collection].config.fields,
42+
operation: 'read',
43+
})
4344

44-
return result
45+
return doc
4546
}

packages/db-mongodb/src/find.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { buildSortParam } from './queries/buildSortParam.js'
1010
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
1111
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
1212
import { getSession } from './utilities/getSession.js'
13-
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
13+
import { transform } from './utilities/transform.js'
1414

1515
export const find: Find = async function find(
1616
this: MongooseAdapter,
@@ -133,13 +133,12 @@ export const find: Find = async function find(
133133
result = await Model.paginate(query, paginationOptions)
134134
}
135135

136-
const docs = JSON.parse(JSON.stringify(result.docs))
136+
transform({
137+
adapter: this,
138+
data: result.docs,
139+
fields: this.payload.collections[collection].config.fields,
140+
operation: 'read',
141+
})
137142

138-
return {
139-
...result,
140-
docs: docs.map((doc) => {
141-
doc.id = doc._id
142-
return sanitizeInternalFields(doc)
143-
}),
144-
}
143+
return result
145144
}

packages/db-mongodb/src/findGlobal.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import type { MongooseAdapter } from './index.js'
88
import { buildQuery } from './queries/buildQuery.js'
99
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
1010
import { getSession } from './utilities/getSession.js'
11-
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
11+
import { transform } from './utilities/transform.js'
1212

1313
export const findGlobal: FindGlobal = async function findGlobal(
1414
this: MongooseAdapter,
1515
{ slug, locale, req, select, where },
1616
) {
1717
const Model = this.globals
18-
const fields = this.payload.globals.config.find((each) => each.slug === slug).flattenedFields
18+
const globalConfig = this.payload.globals.config.find((each) => each.slug === slug)
19+
const fields = globalConfig.flattenedFields
1920
const options: QueryOptions = {
2021
lean: true,
2122
select: buildProjectionFromSelect({
@@ -34,18 +35,18 @@ export const findGlobal: FindGlobal = async function findGlobal(
3435
where: combineQueries({ globalType: { equals: slug } }, where),
3536
})
3637

37-
let doc = (await Model.findOne(query, {}, options)) as any
38+
const doc = (await Model.findOne(query, {}, options)) as any
3839

3940
if (!doc) {
4041
return null
4142
}
42-
if (doc._id) {
43-
doc.id = doc._id
44-
delete doc._id
45-
}
4643

47-
doc = JSON.parse(JSON.stringify(doc))
48-
doc = sanitizeInternalFields(doc)
44+
transform({
45+
adapter: this,
46+
data: doc,
47+
fields: globalConfig.fields,
48+
operation: 'read',
49+
})
4950

5051
return doc
5152
}

0 commit comments

Comments
 (0)