Skip to content

Commit b08ff88

Browse files
authored
fix(db-mongodb): mongodb optimizations (#10120)
There are few issues introduced in #9594 that we need to look into with these changes.
1 parent 08eb13d commit b08ff88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+941
-1339
lines changed

packages/db-mongodb/src/count.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,52 @@
11
import type { CountOptions } from 'mongodb'
22
import type { Count } from 'payload'
33

4+
import { flattenWhereToOperators } from 'payload'
5+
46
import type { MongooseAdapter } from './index.js'
57

6-
import { getHasNearConstraint } from './utilities/getHasNearConstraint.js'
78
import { getSession } from './utilities/getSession.js'
89

910
export const count: Count = async function count(
1011
this: MongooseAdapter,
1112
{ collection, locale, req, where },
1213
) {
1314
const Model = this.collections[collection]
14-
const session = await getSession(this, req)
15+
const options: CountOptions = {
16+
session: await getSession(this, req),
17+
}
1518

16-
const hasNearConstraint = getHasNearConstraint(where)
19+
let hasNearConstraint = false
20+
21+
if (where) {
22+
const constraints = flattenWhereToOperators(where)
23+
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
24+
}
1725

1826
const query = await Model.buildQuery({
1927
locale,
2028
payload: this.payload,
21-
session,
2229
where,
2330
})
2431

2532
// useEstimatedCount is faster, but not accurate, as it ignores any filters. It is thus set to true if there are no filters.
2633
const useEstimatedCount = hasNearConstraint || !query || Object.keys(query).length === 0
2734

35+
if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) {
36+
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
37+
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
38+
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
39+
// the correct indexed field
40+
options.hint = {
41+
_id: 1,
42+
}
43+
}
44+
2845
let result: number
2946
if (useEstimatedCount) {
30-
result = await Model.collection.estimatedDocumentCount()
47+
result = await Model.estimatedDocumentCount({ session: options.session })
3148
} else {
32-
const options: CountOptions = { session }
33-
34-
if (this.disableIndexHints !== true) {
35-
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
36-
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
37-
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
38-
// the correct indexed field
39-
options.hint = {
40-
_id: 1,
41-
}
42-
}
43-
44-
result = await Model.collection.countDocuments(query, options)
49+
result = await Model.countDocuments(query, options)
4550
}
4651

4752
return {

packages/db-mongodb/src/countGlobalVersions.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,52 @@
11
import type { CountOptions } from 'mongodb'
22
import type { CountGlobalVersions } from 'payload'
33

4+
import { flattenWhereToOperators } from 'payload'
5+
46
import type { MongooseAdapter } from './index.js'
57

6-
import { getHasNearConstraint } from './utilities/getHasNearConstraint.js'
78
import { getSession } from './utilities/getSession.js'
89

910
export const countGlobalVersions: CountGlobalVersions = async function countGlobalVersions(
1011
this: MongooseAdapter,
1112
{ global, locale, req, where },
1213
) {
1314
const Model = this.versions[global]
14-
const session = await getSession(this, req)
15+
const options: CountOptions = {
16+
session: await getSession(this, req),
17+
}
1518

16-
const hasNearConstraint = getHasNearConstraint(where)
19+
let hasNearConstraint = false
20+
21+
if (where) {
22+
const constraints = flattenWhereToOperators(where)
23+
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
24+
}
1725

1826
const query = await Model.buildQuery({
1927
locale,
2028
payload: this.payload,
21-
session,
2229
where,
2330
})
2431

2532
// useEstimatedCount is faster, but not accurate, as it ignores any filters. It is thus set to true if there are no filters.
2633
const useEstimatedCount = hasNearConstraint || !query || Object.keys(query).length === 0
2734

35+
if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) {
36+
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
37+
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
38+
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
39+
// the correct indexed field
40+
options.hint = {
41+
_id: 1,
42+
}
43+
}
44+
2845
let result: number
2946
if (useEstimatedCount) {
30-
result = await Model.collection.estimatedDocumentCount()
47+
result = await Model.estimatedDocumentCount({ session: options.session })
3148
} else {
32-
const options: CountOptions = { session }
33-
34-
if (Object.keys(query).length === 0 && this.disableIndexHints !== true) {
35-
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
36-
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
37-
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
38-
// the correct indexed field
39-
options.hint = {
40-
_id: 1,
41-
}
42-
}
43-
44-
result = await Model.collection.countDocuments(query, options)
49+
result = await Model.countDocuments(query, options)
4550
}
4651

4752
return {

packages/db-mongodb/src/countVersions.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,52 @@
11
import type { CountOptions } from 'mongodb'
22
import type { CountVersions } from 'payload'
33

4+
import { flattenWhereToOperators } from 'payload'
5+
46
import type { MongooseAdapter } from './index.js'
57

6-
import { getHasNearConstraint } from './utilities/getHasNearConstraint.js'
78
import { getSession } from './utilities/getSession.js'
89

910
export const countVersions: CountVersions = async function countVersions(
1011
this: MongooseAdapter,
1112
{ collection, locale, req, where },
1213
) {
1314
const Model = this.versions[collection]
14-
const session = await getSession(this, req)
15+
const options: CountOptions = {
16+
session: await getSession(this, req),
17+
}
1518

16-
const hasNearConstraint = getHasNearConstraint(where)
19+
let hasNearConstraint = false
20+
21+
if (where) {
22+
const constraints = flattenWhereToOperators(where)
23+
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
24+
}
1725

1826
const query = await Model.buildQuery({
1927
locale,
2028
payload: this.payload,
21-
session,
2229
where,
2330
})
2431

2532
// useEstimatedCount is faster, but not accurate, as it ignores any filters. It is thus set to true if there are no filters.
2633
const useEstimatedCount = hasNearConstraint || !query || Object.keys(query).length === 0
2734

35+
if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) {
36+
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
37+
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
38+
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
39+
// the correct indexed field
40+
options.hint = {
41+
_id: 1,
42+
}
43+
}
44+
2845
let result: number
2946
if (useEstimatedCount) {
30-
result = await Model.collection.estimatedDocumentCount()
47+
result = await Model.estimatedDocumentCount({ session: options.session })
3148
} else {
32-
const options: CountOptions = { session }
33-
34-
if (this.disableIndexHints !== true) {
35-
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
36-
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
37-
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
38-
// the correct indexed field
39-
options.hint = {
40-
_id: 1,
41-
}
42-
}
43-
44-
result = await Model.collection.countDocuments(query, options)
49+
result = await Model.countDocuments(query, options)
4550
}
4651

4752
return {

packages/db-mongodb/src/create.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,48 @@
1-
import type { Create } from 'payload'
1+
import type { CreateOptions } from 'mongoose'
2+
import type { Create, Document } from 'payload'
23

34
import type { MongooseAdapter } from './index.js'
45

56
import { getSession } from './utilities/getSession.js'
67
import { handleError } from './utilities/handleError.js'
7-
import { transform } from './utilities/transform.js'
8+
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
89

910
export const create: Create = async function create(
1011
this: MongooseAdapter,
1112
{ collection, data, req },
1213
) {
1314
const Model = this.collections[collection]
14-
const session = await getSession(this, req)
15-
16-
const fields = this.payload.collections[collection].config.flattenedFields
17-
18-
if (this.payload.collections[collection].customIDType) {
19-
data._id = data.id
15+
const options: CreateOptions = {
16+
session: await getSession(this, req),
2017
}
2118

22-
transform({
23-
adapter: this,
19+
let doc
20+
21+
const sanitizedData = sanitizeRelationshipIDs({
22+
config: this.payload.config,
2423
data,
25-
fields,
26-
operation: 'create',
24+
fields: this.payload.collections[collection].config.fields,
2725
})
2826

29-
try {
30-
const { insertedId: insertedID } = await Model.collection.insertOne(data, { session })
31-
data._id = insertedID
32-
33-
transform({
34-
adapter: this,
35-
data,
36-
fields,
37-
operation: 'read',
38-
})
27+
if (this.payload.collections[collection].customIDType) {
28+
sanitizedData._id = sanitizedData.id
29+
}
3930

40-
return data
31+
try {
32+
;[doc] = await Model.create([sanitizedData], options)
4133
} catch (error) {
4234
handleError({ collection, error, req })
4335
}
36+
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
40+
41+
// custom id type reset
42+
result.id = result._id
43+
if (verificationToken) {
44+
result._verificationToken = verificationToken
45+
}
46+
47+
return result
4448
}
Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,38 @@
1+
import type { CreateOptions } from 'mongoose'
12
import type { CreateGlobal } from 'payload'
23

34
import type { MongooseAdapter } from './index.js'
45

56
import { getSession } from './utilities/getSession.js'
6-
import { transform } from './utilities/transform.js'
7+
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
8+
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
79

810
export const createGlobal: CreateGlobal = async function createGlobal(
911
this: MongooseAdapter,
1012
{ slug, data, req },
1113
) {
1214
const Model = this.globals
1315

14-
const fields = this.payload.config.globals.find(
15-
(globalConfig) => globalConfig.slug === slug,
16-
).flattenedFields
17-
18-
transform({
19-
adapter: this,
20-
data,
21-
fields,
22-
globalSlug: slug,
23-
operation: 'create',
16+
const global = sanitizeRelationshipIDs({
17+
config: this.payload.config,
18+
data: {
19+
globalType: slug,
20+
...data,
21+
},
22+
fields: this.payload.config.globals.find((globalConfig) => globalConfig.slug === slug).fields,
2423
})
2524

26-
const session = await getSession(this, req)
25+
const options: CreateOptions = {
26+
session: await getSession(this, req),
27+
}
2728

28-
const { insertedId: insertedID } = await Model.collection.insertOne(data, { session })
29-
;(data as any)._id = insertedID
29+
let [result] = (await Model.create([global], options)) as any
3030

31-
transform({
32-
adapter: this,
33-
data,
34-
fields,
35-
operation: 'read',
36-
})
31+
result = JSON.parse(JSON.stringify(result))
32+
33+
// custom id type reset
34+
result.id = result._id
35+
result = sanitizeInternalFields(result)
3736

38-
return data
37+
return result
3938
}

0 commit comments

Comments
 (0)