Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new schema collection type and replace all usages of direct mongo collection for schema operations. #943

Merged
merged 2 commits into from
Mar 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions src/Adapters/Storage/Mongo/MongoCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,6 @@ export default class MongoCollection {
return this._mongoCollection.updateMany(query, update);
}

// Atomically find and delete an object based on query.
// The result is the promise with an object that was in the database before deleting.
// Postgres Note: Translates directly to `DELETE * FROM ... RETURNING *`, which will return data after delete is done.
findOneAndDelete(query) {
// arguments: query, sort
return this._mongoCollection.findAndRemove(query, []).then(document => {
// Value is the object where mongo returns multiple fields.
return document.value;
});
}

deleteOne(query) {
return this._mongoCollection.deleteOne(query);
}
Expand Down
58 changes: 58 additions & 0 deletions src/Adapters/Storage/Mongo/MongoSchemaCollection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

import MongoCollection from './MongoCollection';

function _mongoSchemaQueryFromNameQuery(name: string, query) {
return _mongoSchemaObjectFromNameFields(name, query);
}

function _mongoSchemaObjectFromNameFields(name: string, fields) {
let object = { _id: name };
if (fields) {
Object.keys(fields).forEach(key => {
object[key] = fields[key];
});
}
return object;
}

export default class MongoSchemaCollection {
_collection: MongoCollection;

constructor(collection: MongoCollection) {
this._collection = collection;
}

getAllSchemas() {
return this._collection._rawFind({});
}

findSchema(name: string) {
return this._collection._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 }).then(results => {
return results[0];
});
}

// Atomically find and delete an object based on query.
// The result is the promise with an object that was in the database before deleting.
// Postgres Note: Translates directly to `DELETE * FROM ... RETURNING *`, which will return data after delete is done.
findAndDeleteSchema(name: string) {
// arguments: query, sort
return this._collection._mongoCollection.findAndRemove(_mongoSchemaQueryFromNameQuery(name), []).then(document => {
// Value is the object where mongo returns multiple fields.
return document.value;
});
}

addSchema(name: string, fields) {
let mongoObject = _mongoSchemaObjectFromNameFields(name, fields);
return this._collection.insertOne(mongoObject);
}

updateSchema(name: string, update) {
return this._collection.updateOne(_mongoSchemaQueryFromNameQuery(name), update);
}

upsertSchema(name: string, query: string, update) {
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
}
}
9 changes: 9 additions & 0 deletions src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@

import MongoCollection from './MongoCollection';
import MongoSchemaCollection from './MongoSchemaCollection';

let mongodb = require('mongodb');
let MongoClient = mongodb.MongoClient;

const MongoSchemaCollectionName = '_SCHEMA';

export class MongoStorageAdapter {
// Private
_uri: string;
Expand Down Expand Up @@ -38,6 +41,12 @@ export class MongoStorageAdapter {
.then(rawCollection => new MongoCollection(rawCollection));
}

schemaCollection(collectionPrefix: string) {
return this.connect()
.then(() => this.adaptiveCollection(collectionPrefix + MongoSchemaCollectionName))
.then(collection => new MongoSchemaCollection(collection));
}

collectionExists(name: string) {
return this.connect().then(() => {
return this.database.listCollections({ name: name }).toArray();
Expand Down
8 changes: 6 additions & 2 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ DatabaseController.prototype.adaptiveCollection = function(className) {
return this.adapter.adaptiveCollection(this.collectionPrefix + className);
};

DatabaseController.prototype.schemaCollection = function() {
return this.adapter.schemaCollection(this.collectionPrefix);
};

DatabaseController.prototype.collectionExists = function(className) {
return this.adapter.collectionExists(this.collectionPrefix + className);
};
Expand All @@ -59,7 +63,7 @@ DatabaseController.prototype.validateClassName = function(className) {
DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) {

if (!this.schemaPromise) {
this.schemaPromise = this.adaptiveCollection('_SCHEMA').then(collection => {
this.schemaPromise = this.schemaCollection().then(collection => {
delete this.schemaPromise;
return Schema.load(collection);
});
Expand All @@ -70,7 +74,7 @@ DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) {
if (acceptor(schema)) {
return schema;
}
this.schemaPromise = this.adaptiveCollection('_SCHEMA').then(collection => {
this.schemaPromise = this.schemaCollection().then(collection => {
delete this.schemaPromise;
return Schema.load(collection);
});
Expand Down
21 changes: 10 additions & 11 deletions src/Routers/SchemasRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,22 @@ function classNameMismatchResponse(bodyClass, pathClass) {
}

function getAllSchemas(req) {
return req.config.database.adaptiveCollection('_SCHEMA')
.then(collection => collection.find({}))
return req.config.database.schemaCollection()
.then(collection => collection.getAllSchemas())
.then(schemas => schemas.map(Schema.mongoSchemaToSchemaAPIResponse))
.then(schemas => ({ response: { results: schemas } }));
}

function getOneSchema(req) {
const className = req.params.className;
return req.config.database.adaptiveCollection('_SCHEMA')
.then(collection => collection.find({ '_id': className }, { limit: 1 }))
.then(results => {
if (results.length != 1) {
return req.config.database.schemaCollection()
.then(collection => collection.findSchema(className))
.then(mongoSchema => {
if (!mongoSchema) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`);
}
return results[0];
})
.then(schema => ({ response: Schema.mongoSchemaToSchemaAPIResponse(schema) }));
return { response: Schema.mongoSchemaToSchemaAPIResponse(mongoSchema) };
});
}

function createSchema(req) {
Expand Down Expand Up @@ -142,8 +141,8 @@ function deleteSchema(req) {
.then(() => {
// We've dropped the collection now, so delete the item from _SCHEMA
// and clear the _Join collections
return req.config.database.adaptiveCollection('_SCHEMA')
.then(coll => coll.findOneAndDelete({ _id: req.params.className }))
return req.config.database.schemaCollection()
.then(coll => coll.findAndDeleteSchema(req.params.className))
.then(document => {
if (document === null) {
//tried to delete non-existent class
Expand Down
15 changes: 7 additions & 8 deletions src/Schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class Schema {
reloadData() {
this.data = {};
this.perms = {};
return this._collection.find({}).then(results => {
return this._collection.getAllSchemas().then(results => {
for (let obj of results) {
let className = null;
let classData = {};
Expand Down Expand Up @@ -231,7 +231,7 @@ class Schema {
return Promise.reject(mongoObject);
}

return this._collection.insertOne(mongoObject.result)
return this._collection.addSchema(className, mongoObject.result)
.then(result => result.ops[0])
.catch(error => {
if (error.code === 11000) { //Mongo's duplicate key error
Expand Down Expand Up @@ -268,7 +268,7 @@ class Schema {
'schema is frozen, cannot add: ' + className);
}
// We don't have this class. Update the schema
return this._collection.insertOne({ _id: className }).then(() => {
return this._collection.addSchema(className).then(() => {
// The schema update succeeded. Reload the schema
return this.reloadData();
}, () => {
Expand All @@ -288,14 +288,13 @@ class Schema {

// Sets the Class-level permissions for a given className, which must exist.
setPermissions(className, perms) {
var query = {_id: className};
var update = {
_metadata: {
class_permissions: perms
}
};
update = {'$set': update};
return this._collection.updateOne(query, update).then(() => {
return this._collection.updateSchema(className, update).then(() => {
// The update succeeded. Reload the schema
return this.reloadData();
});
Expand Down Expand Up @@ -353,12 +352,12 @@ class Schema {
// We don't have this field. Update the schema.
// Note that we use the $exists guard and $set to avoid race
// conditions in the database. This is important!
var query = { _id: className };
let query = {};
query[key] = { '$exists': false };
var update = {};
update[key] = type;
update = {'$set': update};
return this._collection.upsertOne(query, update).then(() => {
return this._collection.upsertSchema(className, query, update).then(() => {
// The update succeeded. Reload the schema
return this.reloadData();
}, () => {
Expand Down Expand Up @@ -428,7 +427,7 @@ class Schema {
});
})
// Save the _SCHEMA object
.then(() => this._collection.updateOne({ _id: className }, { $unset: { [fieldName]: null } }));
.then(() => this._collection.updateSchema(className, { $unset: { [fieldName]: null } }));
});
}

Expand Down