Skip to content

Finish moving query logic #1888

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
69d5a2f
Make find() in MongoStorageAdapter
drew-gross May 22, 2016
9f149e6
Don't mess with inner object keys called _auth_data_*
drew-gross May 22, 2016
c928dcc
Prevent untransforming inner object keys named _p_*
drew-gross May 22, 2016
74ee861
Fix inner keys named _rperm, _wperm
drew-gross May 22, 2016
fe81604
Revert changes to find
drew-gross May 23, 2016
474a893
Pass the Parse Schema into untransform
drew-gross May 23, 2016
00de555
remove one use of schemaController
drew-gross May 23, 2016
a55b2b6
remove another use of schemaController
drew-gross May 23, 2016
d944255
remove another use of schemaController
drew-gross May 23, 2016
f4b1f7b
Remove all dependencies on schemaController
drew-gross May 23, 2016
e440046
Remove getRelationFields
drew-gross May 24, 2016
7dca7e2
Remove schemaController parameter
drew-gross May 24, 2016
4052470
remove schemaController paramater
drew-gross May 24, 2016
1ae1d42
transformWhere in MongoAdapter
drew-gross May 24, 2016
14938bb
create + use adapter count instead of collection count
drew-gross May 24, 2016
cf0a4b2
remove adaptive collection call
drew-gross May 24, 2016
c9be5a3
Destructure mongo options
drew-gross May 24, 2016
aa072da
Remove limit from count
drew-gross May 24, 2016
e444ca8
Can't sort a count
drew-gross May 24, 2016
135b0e0
Remove options from count
drew-gross May 24, 2016
a763f7c
move transformWhere into mongo adapter
drew-gross May 24, 2016
d428041
Consistent parameter order
drew-gross May 24, 2016
05ae010
Kill mongoOptions
drew-gross May 24, 2016
3ed3c7b
Move more mongo specific stuff into mongo adapter
drew-gross May 24, 2016
0896f33
Remove unnecessary null check
drew-gross May 24, 2016
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
8 changes: 3 additions & 5 deletions spec/InstallationsRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,9 @@ describe('InstallationsRouter', () => {

var router = new InstallationsRouter();
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
.then(() => {
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
}).then(() => {
return router.handleFind(request);
}).then((res) => {
.then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest))
.then(() => router.handleFind(request))
.then((res) => {
var response = res.response;
expect(response.results.length).toEqual(2);
expect(response.count).toEqual(2);
Expand Down
48 changes: 31 additions & 17 deletions spec/MongoTransform.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ var dummySchema = {
}
return;
},
getRelationFields: function() {
return {}
}
};


Expand All @@ -39,7 +36,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
createdAt: "2015-10-06T21:24:50.332Z",
updatedAt: "2015-10-06T21:24:50.332Z"
};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
expect(output._created_at instanceof Date).toBe(true);
expect(output._updated_at instanceof Date).toBe(true);
done();
Expand All @@ -62,14 +59,14 @@ describe('parseObjectToMongoObjectForCreate', () => {
//have __op delete in a new object. Figure out what this should actually be testing.
notWorking('a delete op', (done) => {
var input = {deleteMe: {__op: 'Delete'}};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
jequal(output, {});
done();
});

it('basic ACL', (done) => {
var input = {ACL: {'0123': {'read': true, 'write': true}}};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
// This just checks that it doesn't crash, but it should check format.
done();
});
Expand Down Expand Up @@ -124,15 +121,17 @@ describe('transformWhere', () => {
describe('mongoObjectToParseObject', () => {
it('built-in timestamps', (done) => {
var input = {createdAt: new Date(), updatedAt: new Date()};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
expect(typeof output.createdAt).toEqual('string');
expect(typeof output.updatedAt).toEqual('string');
done();
});

it('pointer', (done) => {
var input = {_p_userPointer: '_User$123'};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, {
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
});
expect(typeof output.userPointer).toEqual('object');
expect(output.userPointer).toEqual(
{__type: 'Pointer', className: '_User', objectId: '123'}
Expand All @@ -142,22 +141,28 @@ describe('mongoObjectToParseObject', () => {

it('null pointer', (done) => {
var input = {_p_userPointer: null};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, {
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
});
expect(output.userPointer).toBeUndefined();
done();
});

it('file', (done) => {
var input = {picture: 'pic.jpg'};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, {
fields: { picture: { type: 'File' }},
});
expect(typeof output.picture).toEqual('object');
expect(output.picture).toEqual({__type: 'File', name: 'pic.jpg'});
done();
});

it('geopoint', (done) => {
var input = {location: [180, -180]};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, {
fields: { location: { type: 'GeoPoint' }},
});
expect(typeof output.location).toEqual('object');
expect(output.location).toEqual(
{__type: 'GeoPoint', longitude: 180, latitude: -180}
Expand All @@ -167,7 +172,9 @@ describe('mongoObjectToParseObject', () => {

it('nested array', (done) => {
var input = {arr: [{_testKey: 'testValue' }]};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, {
fields: { arr: { type: 'Array' } },
});
expect(Array.isArray(output.arr)).toEqual(true);
expect(output.arr).toEqual([{ _testKey: 'testValue'}]);
done();
Expand All @@ -185,7 +192,9 @@ describe('mongoObjectToParseObject', () => {
},
regularKey: "some data",
}]}
let output = transform.mongoObjectToParseObject(dummySchema, null, input);
let output = transform.mongoObjectToParseObject(null, input, {
fields: { array: { type: 'Array' }},
});
expect(dd(output, input)).toEqual(undefined);
done();
});
Expand Down Expand Up @@ -224,7 +233,7 @@ describe('transform schema key changes', () => {
"Kevin": { "write": true }
}
};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
expect(typeof output._rperm).toEqual('object');
expect(typeof output._wperm).toEqual('object');
expect(output.ACL).toBeUndefined();
Expand All @@ -241,7 +250,7 @@ describe('transform schema key changes', () => {
}
};

var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
expect(typeof output._acl).toEqual('object');
expect(output._acl["Kevin"].w).toBeTruthy();
expect(output._acl["Kevin"].r).toBeUndefined();
Expand All @@ -253,7 +262,7 @@ describe('transform schema key changes', () => {
_rperm: ["*"],
_wperm: ["Kevin"]
};
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
expect(typeof output.ACL).toEqual('object');
expect(output._rperm).toBeUndefined();
expect(output._wperm).toBeUndefined();
Expand All @@ -267,7 +276,12 @@ describe('transform schema key changes', () => {
long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER),
double: new mongodb.Double(Number.MAX_VALUE)
}
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
var output = transform.mongoObjectToParseObject(null, input, {
fields: {
long: { type: 'Number' },
double: { type: 'Number' },
},
});
expect(output.long).toBe(Number.MAX_SAFE_INTEGER);
expect(output.double).toBe(Number.MAX_VALUE);
done();
Expand Down
2 changes: 1 addition & 1 deletion spec/ParseAPI.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ describe('miscellaneous', function() {
})
});

it('does not change inner object key names _auth_data_something', done => {
it('does not change inner object keys named _auth_data_something', done => {
new Parse.Object('O').save({ innerObj: {_auth_data_facebook: 7}})
.then(object => new Parse.Query('O').get(object.id))
.then(object => {
Expand Down
15 changes: 11 additions & 4 deletions src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,18 @@ export class MongoStorageAdapter {
}

// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
// Accepts the schemaController for legacy reasons.
find(className, query, { skip, limit, sort }, schemaController) {
find(className, query, schema, { skip, limit, sort }) {
let mongoWhere = this.transform.transformWhere(className, query, schema);
let mongoSort = _.mapKeys(sort, (value, fieldName) => transform.transformKey(className, fieldName, schema));
return this.adaptiveCollection(className)
.then(collection => collection.find(query, { skip, limit, sort }))
.then(objects => objects.map(object => transform.mongoObjectToParseObject(schemaController, className, object)));
.then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort }))
.then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema)));
}

// Executs a count.
count(className, query, schema) {
return this.adaptiveCollection(className)
.then(collection => collection.count(transform.transformWhere(className, query, schema)));
}

get transform() {
Expand Down
33 changes: 17 additions & 16 deletions src/Adapters/Storage/Mongo/MongoTransform.js
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ const nestedMongoObjectToNestedParseObject = mongoObject => {

// Converts from a mongo-format object to a REST-format object.
// Does not strip out anything based on a lack of authentication.
const mongoObjectToParseObject = (schema, className, mongoObject) => {
const mongoObjectToParseObject = (className, mongoObject, schema) => {
switch(typeof mongoObject) {
case 'string':
case 'number':
Expand Down Expand Up @@ -830,26 +830,19 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {

if (key.indexOf('_p_') == 0) {
var newKey = key.substring(3);
var expected;
if (schema && schema.getExpectedType) {
expected = schema.getExpectedType(className, newKey);
}
if (!expected) {
log.info('transform.js',
'Found a pointer column not in the schema, dropping it.',
className, newKey);
if (!schema.fields[newKey]) {
log.info('transform.js', 'Found a pointer column not in the schema, dropping it.', className, newKey);
break;
}
if (expected && expected.type !== 'Pointer') {
if (schema.fields[newKey].type !== 'Pointer') {
log.info('transform.js', 'Found a pointer in a non-pointer column, dropping it.', className, key);
break;
}
if (mongoObject[key] === null) {
break;
}
var objData = mongoObject[key].split('$');
var newClass = (expected ? expected.targetClass : objData[0]);
if (objData[0] !== newClass) {
if (objData[0] !== schema.fields[newKey].targetClass) {
throw 'pointer to incorrect className';
}
restObject[newKey] = {
Expand All @@ -861,13 +854,12 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
} else if (key[0] == '_' && key != '__type') {
throw ('bad key in untransform: ' + key);
} else {
var expectedType = schema.getExpectedType(className, key);
var value = mongoObject[key];
if (expectedType && expectedType.type === 'File' && FileCoder.isValidDatabaseObject(value)) {
if (schema.fields[key] && schema.fields[key].type === 'File' && FileCoder.isValidDatabaseObject(value)) {
restObject[key] = FileCoder.databaseToJSON(value);
break;
}
if (expectedType && expectedType.type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
if (schema.fields[key] && schema.fields[key].type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
restObject[key] = GeoPointCoder.databaseToJSON(value);
break;
}
Expand All @@ -876,7 +868,16 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
}
}

return { ...restObject, ...schema.getRelationFields(className) };
const relationFieldNames = Object.keys(schema.fields).filter(fieldName => schema.fields[fieldName].type === 'Relation');
let relationFields = {};
relationFieldNames.forEach(relationFieldName => {
relationFields[relationFieldName] = {
__type: 'Relation',
className: schema.fields[relationFieldName].targetClass,
}
});

return { ...restObject, ...relationFields };
default:
throw 'unknown js type';
}
Expand Down
58 changes: 22 additions & 36 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -628,16 +628,9 @@ DatabaseController.prototype.find = function(className, query, {
skip,
limit,
acl,
sort,
sort = {},
count,
} = {}) {
let mongoOptions = {};
if (skip) {
mongoOptions.skip = skip;
}
if (limit) {
mongoOptions.limit = limit;
}
let isMaster = acl === undefined;
let aclGroup = acl || [];
let op = typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find';
Expand All @@ -653,34 +646,29 @@ DatabaseController.prototype.find = function(className, query, {
throw error;
})
.then(schema => {
if (sort) {
mongoOptions.sort = {};
for (let fieldName in sort) {
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
// so duplicate that behaviour here.
if (fieldName === '_created_at') {
fieldName = 'createdAt';
sort['createdAt'] = sort['_created_at'];
} else if (fieldName === '_updated_at') {
fieldName = 'updatedAt';
sort['updatedAt'] = sort['_updated_at'];
}

if (!SchemaController.fieldNameIsValid(fieldName)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
}
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
}
const mongoKey = this.transform.transformKey(className, fieldName, schema);
mongoOptions.sort[mongoKey] = sort[fieldName];
}
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
// so duplicate that behaviour here. If both are specified, the corrent behaviour to match Parse.com is to
// use the one that appears first in the sort list.
if (sort._created_at) {
sort.createdAt = sort._created_at;
delete sort._created_at;
}
if (sort._updated_at) {
sort.updatedAt = sort._updated_at;
delete sort._updated_at;
}
Object.keys(sort).forEach(fieldName => {
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
}
if (!SchemaController.fieldNameIsValid(fieldName)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
}
});
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op))
.then(() => this.reduceRelationKeys(className, query))
.then(() => this.reduceInRelation(className, query, schemaController))
.then(() => this.adapter.adaptiveCollection(className))
.then(collection => {
.then(() => {
if (!isMaster) {
query = this.addPointerPermissions(schemaController, className, op, query, aclGroup);
}
Expand All @@ -696,12 +684,10 @@ DatabaseController.prototype.find = function(className, query, {
query = addReadACL(query, aclGroup);
}
validateQuery(query);
let mongoWhere = this.transform.transformWhere(className, query, schema);
if (count) {
delete mongoOptions.limit;
return collection.count(mongoWhere, mongoOptions);
return this.adapter.count(className, query, schema);
} else {
return this.adapter.find(className, mongoWhere, mongoOptions, schemaController)
return this.adapter.find(className, query, schema, { skip, limit, sort })
.then(objects => objects.map(object => filterSensitiveData(isMaster, aclGroup, className, object)));
}
});
Expand Down
17 changes: 0 additions & 17 deletions src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,23 +686,6 @@ class SchemaController {
hasClass(className) {
return this.reloadData().then(() => !!(this.data[className]));
}

getRelationFields(className) {
if (this.data && this.data[className]) {
let classData = this.data[className];
return Object.keys(classData).filter((field) => {
return classData[field].type === 'Relation';
}).reduce((memo, field) => {
let type = classData[field];
memo[field] = {
__type: 'Relation',
className: type.targetClass
};
return memo;
}, {});
}
return {};
}
}

// Returns a promise for a new Schema.
Expand Down