feat: add indexSortableField option to create indexes for sortable fields on all collections

This commit is contained in:
Dan Ribbens
2021-09-30 16:05:47 -04:00
parent d498e37083
commit ad097820bf
7 changed files with 15 additions and 2 deletions

View File

@@ -130,6 +130,7 @@ export default buildConfig({
defaultLocale: 'en', defaultLocale: 'en',
fallback: true, fallback: true,
}, },
// indexSortableFields: true,
hooks: { hooks: {
afterError: (err) => { afterError: (err) => {
console.error('global error config handler', err); console.error('global error config handler', err);

View File

@@ -29,6 +29,7 @@ Payload is a *config-based*, code-first CMS and application framework. The Paylo
| `csrf` | A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. [More](/docs/authentication/overview#csrf-protection) | | `csrf` | A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. [More](/docs/authentication/overview#csrf-protection) |
| `defaultDepth` | If a user does not specify `depth` while requesting a resource, this depth will be used. [More](/docs/getting-started/concepts#depth) | | `defaultDepth` | If a user does not specify `depth` while requesting a resource, this depth will be used. [More](/docs/getting-started/concepts#depth) |
| `maxDepth` | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. | | `maxDepth` | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. |
| `indexSortableFields`| Automatically create database an index on every sortable type of top-level field. |
| `upload` | Base Payload upload configuration. [More](/docs/upload/overview#payload-wide-upload-options). | | `upload` | Base Payload upload configuration. [More](/docs/upload/overview#payload-wide-upload-options). |
| `routes` | Control the routing structure that Payload binds itself to. Specify `admin`, `api`, `graphQL`, and `graphQLPlayground`. | | `routes` | Control the routing structure that Payload binds itself to. Specify `admin`, `api`, `graphQL`, and `graphQLPlayground`. |
| `email` | Base email settings to allow Payload to generate email such as Forgot Password requests and other requirements. [More](/docs/email/overview#configuration) | | `email` | Base email settings to allow Payload to generate email such as Forgot Password requests and other requirements. [More](/docs/email/overview#configuration) |

View File

@@ -72,6 +72,8 @@ If you are using a [persistent filesystem-based cloud host](#persistent-vs-ephem
Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and backups. Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and backups.
When using Azure Cosmos, MongoDB requires an index on any field being sorted on in a query. To make this easier, see the [indexSortableFields](/docs/configuration/overview) configuration option.
## File storage ## File storage
If you are using Payload to [manage file uploads](/docs/upload/overview), you need to consider where your uploaded files will be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app whatsoever. If you are using Payload to [manage file uploads](/docs/upload/overview), you need to consider where your uploaded files will be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app whatsoever.

View File

@@ -80,6 +80,7 @@ export default joi.object({
}), }),
local: joi.boolean(), local: joi.boolean(),
upload: joi.object(), upload: joi.object(),
indexSortableFields: joi.boolean(),
rateLimit: joi.object() rateLimit: joi.object()
.keys({ .keys({
window: joi.number(), window: joi.number(),

View File

@@ -120,6 +120,7 @@ export type Config = {
}, },
defaultDepth?: number; defaultDepth?: number;
maxDepth?: number; maxDepth?: number;
indexSortableFields?: boolean;
rateLimit?: { rateLimit?: {
window?: number; window?: number;
max?: number; max?: number;
@@ -143,7 +144,7 @@ export type Config = {
hooks?: { hooks?: {
afterError?: AfterErrorHook; afterError?: AfterErrorHook;
}; };
plugins?: Plugin[] plugins?: Plugin[];
}; };
export type SanitizedConfig = Omit<DeepRequired<Config>, 'collections' | 'globals'> & { export type SanitizedConfig = Omit<DeepRequired<Config>, 'collections' | 'globals'> & {

View File

@@ -9,7 +9,7 @@ const buildModel = (config: SanitizedConfig): mongoose.PaginateModel<any> | null
const Globals = mongoose.model('globals', globalsSchema); const Globals = mongoose.model('globals', globalsSchema);
Object.values(config.globals).forEach((globalConfig) => { Object.values(config.globals).forEach((globalConfig) => {
const globalSchema = buildSchema(config, globalConfig.fields, {}); const globalSchema = buildSchema(config, globalConfig.fields, { global: true });
Globals.discriminator(globalConfig.slug, globalSchema); Globals.discriminator(globalConfig.slug, globalSchema);
}); });

View File

@@ -2,11 +2,13 @@
import { Schema, SchemaDefinition, SchemaOptions } from 'mongoose'; import { Schema, SchemaDefinition, SchemaOptions } from 'mongoose';
import { SanitizedConfig } from '../config/types'; import { SanitizedConfig } from '../config/types';
import { ArrayField, Block, BlockField, Field, GroupField, RadioField, RelationshipField, RowField, SelectField, UploadField } from '../fields/config/types'; import { ArrayField, Block, BlockField, Field, GroupField, RadioField, RelationshipField, RowField, SelectField, UploadField } from '../fields/config/types';
import sortableFieldTypes from '../fields/sortableFieldTypes';
type BuildSchemaOptions = { type BuildSchemaOptions = {
options?: SchemaOptions options?: SchemaOptions
allowIDField?: boolean allowIDField?: boolean
disableRequired?: boolean disableRequired?: boolean
global?: boolean
} }
type FieldSchemaGenerator = (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions) => SchemaDefinition; type FieldSchemaGenerator = (field: Field, fields: SchemaDefinition, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions) => SchemaDefinition;
@@ -89,10 +91,15 @@ const buildSchema = (config: SanitizedConfig, configFields: Field[], buildSchema
if (fieldSchema) { if (fieldSchema) {
fields = fieldSchema(field, fields, config, buildSchemaOptions); fields = fieldSchema(field, fields, config, buildSchemaOptions);
} }
// geospatial field index must be created after the schema is created // geospatial field index must be created after the schema is created
if (fieldIndexMap[field.type]) { if (fieldIndexMap[field.type]) {
indexFields.push(...fieldIndexMap[field.type](field, config)); indexFields.push(...fieldIndexMap[field.type](field, config));
} }
if (config.indexSortableFields && !buildSchemaOptions.global && !field.index && !field.hidden && sortableFieldTypes.indexOf(field.type) > -1) {
indexFields.push({ [field.name]: 1 });
}
}); });
const schema = new Schema(fields, options); const schema = new Schema(fields, options);