diff --git a/spec/DefinedSchemas.spec.js b/spec/DefinedSchemas.spec.js index e3d6fd51fe..b2cae864c1 100644 --- a/spec/DefinedSchemas.spec.js +++ b/spec/DefinedSchemas.spec.js @@ -371,7 +371,7 @@ describe('DefinedSchemas', () => { expect(schema.indexes).toEqual(indexes); }); - it('should delete removed indexes', async () => { + it('should delete unknown indexes when keepUnknownIndexes is not set', async () => { const server = await reconfigureServer(); let indexes = { complex: { createdAt: 1, updatedAt: 1 } }; @@ -393,6 +393,53 @@ describe('DefinedSchemas', () => { cleanUpIndexes(schema); expect(schema.indexes).toBeUndefined(); }); + + it('should delete unknown indexes when keepUnknownIndexes is set to false', async () => { + const server = await reconfigureServer(); + + let indexes = { complex: { createdAt: 1, updatedAt: 1 } }; + + let schemas = { definitions: [{ className: 'Test', indexes }], keepUnknownIndexes: false }; + await new DefinedSchemas(schemas, server.config).execute(); + + indexes = {}; + schemas = { definitions: [{ className: 'Test', indexes }], keepUnknownIndexes: false }; + // Change indexes + await new DefinedSchemas(schemas, server.config).execute(); + let schema = await new Parse.Schema('Test').get(); + cleanUpIndexes(schema); + expect(schema.indexes).toBeUndefined(); + + // Update + await new DefinedSchemas(schemas, server.config).execute(); + schema = await new Parse.Schema('Test').get(); + cleanUpIndexes(schema); + expect(schema.indexes).toBeUndefined(); + }); + + it('should not delete unknown indexes when keepUnknownIndexes is set to true', async () => { + const server = await reconfigureServer(); + + const indexes = { complex: { createdAt: 1, updatedAt: 1 } }; + + let schemas = { definitions: [{ className: 'Test', indexes }], keepUnknownIndexes: true }; + await new DefinedSchemas(schemas, server.config).execute(); + + schemas = { definitions: [{ className: 'Test', indexes: {} }], keepUnknownIndexes: true }; + + // Change indexes + await new DefinedSchemas(schemas, server.config).execute(); + let schema = await new Parse.Schema('Test').get(); + cleanUpIndexes(schema); + expect(schema.indexes).toEqual({ complex: { createdAt: 1, updatedAt: 1 } }); + + // Update + await new DefinedSchemas(schemas, server.config).execute(); + schema = await new Parse.Schema('Test').get(); + cleanUpIndexes(schema); + expect(schema.indexes).toEqual(indexes); + }); + xit('should keep protected indexes', async () => { const server = await reconfigureServer(); diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index a23a0de3e5..ab25b1017d 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -28,6 +28,13 @@ module.exports.SchemaOptions = { action: parsers.booleanParser, default: false, }, + keepUnknownIndexes: { + env: 'PARSE_SERVER_SCHEMA_KEEP_UNKNOWN_INDEXES', + help: + "(Optional) Keep indexes that are present in the database but not defined in the schema. Set this to `true` if you are adding indexes manually, so that they won't be removed when running schema migration. Default is `false`.", + action: parsers.booleanParser, + default: false, + }, lockSchemas: { env: 'PARSE_SERVER_SCHEMA_LOCK_SCHEMAS', help: diff --git a/src/Options/docs.js b/src/Options/docs.js index bfba129bb2..0a8df6d137 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -4,6 +4,7 @@ * @property {Function} beforeMigration Execute a callback before running schema migrations. * @property {Any} definitions Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema * @property {Boolean} deleteExtraFields Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development. + * @property {Boolean} keepUnknownIndexes (Optional) Keep indexes that are present in the database but not defined in the schema. Set this to `true` if you are adding indexes manually, so that they won't be removed when running schema migration. Default is `false`. * @property {Boolean} lockSchemas Is true if Parse Server will reject any attempts to modify the schema while the server is running. * @property {Boolean} recreateModifiedFields Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development. * @property {Boolean} strict Is true if Parse Server should exit if schema update fail. diff --git a/src/Options/index.js b/src/Options/index.js index b1827d808a..9f70700345 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -25,6 +25,9 @@ export interface SchemaOptions { /* Is true if Parse Server will reject any attempts to modify the schema while the server is running. :DEFAULT: false */ lockSchemas: ?boolean; + /* (Optional) Keep indexes that are present in the database but not defined in the schema. Set this to `true` if you are adding indexes manually, so that they won't be removed when running schema migration. Default is `false`. + :DEFAULT: false */ + keepUnknownIndexes: ?boolean; /* Execute a callback before running schema migrations. */ beforeMigration: ?() => void | Promise; /* Execute a callback after running schema migrations. */ diff --git a/src/SchemaMigrations/DefinedSchemas.js b/src/SchemaMigrations/DefinedSchemas.js index cf2b1761f4..0a94a5e4ad 100644 --- a/src/SchemaMigrations/DefinedSchemas.js +++ b/src/SchemaMigrations/DefinedSchemas.js @@ -349,7 +349,10 @@ export class DefinedSchemas { Object.keys(cloudSchema.indexes).forEach(indexName => { if (!this.isProtectedIndex(localSchema.className, indexName)) { if (!localSchema.indexes || !localSchema.indexes[indexName]) { - newLocalSchema.deleteIndex(indexName); + // If keepUnknownIndex is falsy, then delete all unknown indexes from the db. + if(!this.schemaOptions.keepUnknownIndexes){ + newLocalSchema.deleteIndex(indexName); + } } else if ( !this.paramsAreEquals(localSchema.indexes[indexName], cloudSchema.indexes[indexName]) ) { diff --git a/src/SchemaMigrations/Migrations.js b/src/SchemaMigrations/Migrations.js index 8768911189..23499bdba7 100644 --- a/src/SchemaMigrations/Migrations.js +++ b/src/SchemaMigrations/Migrations.js @@ -6,6 +6,7 @@ export interface SchemaOptions { deleteExtraFields: ?boolean; recreateModifiedFields: ?boolean; lockSchemas: ?boolean; + keepUnknownIndexes: ?boolean; beforeMigration: ?() => void | Promise; afterMigration: ?() => void | Promise; }