Unique indexes (#1971)

* Add unique indexing

* Add unique indexing for username/email

* WIP

* Finish unique indexes

* Notes on how to upgrade to 2.3.0 safely

* index on unique-indexes: c454180 Revert "Log objects rather than JSON stringified objects (#1922)"

* reconfigure username/email tests

* Start dealing with test shittyness

* Remove tests for files that we are removing

* most tests passing

* fix failing test

* Make specific server config for tests async

* Fix more tests

* fix more tests

* Fix another test

* fix more tests

* Fix email validation

* move some stuff around

* Destroy server to ensure all connections are gone

* Fix broken cloud code

* Save callback to variable

* no need to delete non existant cloud

* undo

* Fix all tests where connections are left open after server closes.

* Fix issues caused by missing gridstore adapter

* Update guide for 2.3.0 and fix final tests

* use strict

* don't use features that won't work in node 4

* Fix syntax error

* Fix typos

* Add duplicate finding command

* Update 2.3.0.md
This commit is contained in:
Drew
2016-06-10 20:27:21 -07:00
committed by GitHub
parent 6415a35433
commit 7e868b2dcc
37 changed files with 1727 additions and 1517 deletions

View File

@@ -83,6 +83,18 @@ export default class MongoCollection {
return this._mongoCollection.deleteMany(query);
}
_ensureSparseUniqueIndexInBackground(indexRequest) {
return new Promise((resolve, reject) => {
this._mongoCollection.ensureIndex(indexRequest, { unique: true, background: true, sparse: true }, (error, indexName) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
drop() {
return this._mongoCollection.drop();
}

View File

@@ -148,7 +148,7 @@ class MongoSchemaCollection {
if (results.length === 1) {
return mongoSchemaToParseSchema(results[0]);
} else {
return Promise.reject();
throw undefined;
}
});
}
@@ -175,9 +175,9 @@ class MongoSchemaCollection {
.then(result => mongoSchemaToParseSchema(result.ops[0]))
.catch(error => {
if (error.code === 11000) { //Mongo's duplicate key error
return Promise.reject();
throw undefined;
}
return Promise.reject(error);
throw error;
});
}
@@ -207,17 +207,17 @@ class MongoSchemaCollection {
if (type.type === 'GeoPoint') {
// Make sure there are not other geopoint fields
if (Object.keys(schema.fields).some(existingField => schema.fields[existingField].type === 'GeoPoint')) {
return Promise.reject(new Parse.Error(Parse.Error.INCORRECT_TYPE, 'MongoDB only supports one GeoPoint field in a class.'));
throw new Parse.Error(Parse.Error.INCORRECT_TYPE, 'MongoDB only supports one GeoPoint field in a class.');
}
}
return Promise.resolve();
return;
}, error => {
// If error is undefined, the schema doesn't exist, and we can create the schema with the field.
// If some other error, reject with it.
if (error === undefined) {
return Promise.resolve();
return;
}
throw Promise.reject(error);
throw error;
})
.then(() => {
// We use $exists and $set to avoid overwriting the field type if it

View File

@@ -65,6 +65,7 @@ export class MongoStorageAdapter {
this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions).then(database => {
this.database = database;
});
return this.connectionPromise;
}
@@ -102,9 +103,9 @@ export class MongoStorageAdapter {
.catch(error => {
// 'ns not found' means collection was already gone. Ignore deletion attempt.
if (error.message == 'ns not found') {
return Promise.resolve();
return;
}
return Promise.reject(error);
throw error;
});
}
@@ -180,7 +181,7 @@ export class MongoStorageAdapter {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE,
'A duplicate value for a field with unique values was provided');
}
return Promise.reject(error);
throw error;
});
}
@@ -236,6 +237,28 @@ export class MongoStorageAdapter {
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
}
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
// currently know which fields are nullable and which aren't, we ignore that criteria.
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
// which is why we use sparse indexes.
ensureUniqueness(className, fieldNames, schema) {
let indexCreationRequest = {};
let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
mongoFieldNames.forEach(fieldName => {
indexCreationRequest[fieldName] = 1;
});
return this.adaptiveCollection(className)
.then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest))
.catch(error => {
if (error.code === 11000) {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.');
} else {
throw error;
}
});
}
// Used in tests
_rawFind(className, query) {
return this.adaptiveCollection(className).then(collection => collection.find(query));