Skip to content

Commit

Permalink
improve field deletion in collection (parse-community#6823)
Browse files Browse the repository at this point in the history
* added filter to updateMany when deleting field

* added test cases

* added changelog entry
  • Loading branch information
mtrezza authored and therealjmj committed Aug 20, 2020
1 parent 7aed392 commit 29fb2cc
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### master
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.3.0...master)
- IMPROVE: Optimized deletion of class field from schema by using an index if available to do an index scan instead of a collection scan. [#6815](https://github.com/parse-community/parse-server/issues/6815). Thanks to [Manuel Trezza](https://github.com/mtrezza).

### 4.3.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.2.0...4.3.0)
Expand Down
37 changes: 37 additions & 0 deletions spec/MongoStorageAdapter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,43 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH');
});

it('should delete field without index', async () => {
const database = Config.get(Parse.applicationId).database;
const obj = new Parse.Object('MyObject');
obj.set("test", 1);
await obj.save();
const schemaBeforeDeletion = await new Parse.Schema('MyObject').get();
await database.adapter.deleteFields(
"MyObject",
schemaBeforeDeletion,
["test"]
);
const schemaAfterDeletion = await new Parse.Schema('MyObject').get();
expect(schemaBeforeDeletion.fields.test).toBeDefined();
expect(schemaAfterDeletion.fields.test).toBeUndefined();
});

it('should delete field with index', async () => {
const database = Config.get(Parse.applicationId).database;
const obj = new Parse.Object('MyObject');
obj.set("test", 1);
await obj.save();
const schemaBeforeDeletion = await new Parse.Schema('MyObject').get();
await database.adapter.ensureIndex(
'MyObject',
schemaBeforeDeletion,
['test']
);
await database.adapter.deleteFields(
"MyObject",
schemaBeforeDeletion,
["test"]
);
const schemaAfterDeletion = await new Parse.Schema('MyObject').get();
expect(schemaBeforeDeletion.fields.test).toBeDefined();
expect(schemaAfterDeletion.fields.test).toBeUndefined();
});

if (
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
Expand Down
7 changes: 6 additions & 1 deletion src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,14 +433,19 @@ export class MongoStorageAdapter implements StorageAdapter {
collectionUpdate['$unset'][name] = null;
});

const collectionFilter = { $or: [] };
mongoFormatNames.forEach(name => {
collectionFilter['$or'].push({ [name]: { $exists: true } });
});

const schemaUpdate = { $unset: {} };
fieldNames.forEach((name) => {
schemaUpdate['$unset'][name] = null;
schemaUpdate['$unset'][`_metadata.fields_options.${name}`] = null;
});

return this._adaptiveCollection(className)
.then((collection) => collection.updateMany({}, collectionUpdate))
.then((collection) => collection.updateMany(collectionFilter, collectionUpdate))
.then(() => this._schemaCollection())
.then((schemaCollection) =>
schemaCollection.updateSchema(className, schemaUpdate)
Expand Down

0 comments on commit 29fb2cc

Please sign in to comment.