Skip to content

Commit c4499d2

Browse files
committed
Merge pull request #1888 from drew-gross/finish-moving-query-logic
Finish moving query logic
2 parents 4864518 + 0896f33 commit c4499d2

File tree

7 files changed

+85
-96
lines changed

7 files changed

+85
-96
lines changed

spec/InstallationsRouter.spec.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,9 @@ describe('InstallationsRouter', () => {
123123

124124
var router = new InstallationsRouter();
125125
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
126-
.then(() => {
127-
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
128-
}).then(() => {
129-
return router.handleFind(request);
130-
}).then((res) => {
126+
.then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest))
127+
.then(() => router.handleFind(request))
128+
.then((res) => {
131129
var response = res.response;
132130
expect(response.results.length).toEqual(2);
133131
expect(response.count).toEqual(2);

spec/MongoTransform.spec.js

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ var dummySchema = {
1717
}
1818
return;
1919
},
20-
getRelationFields: function() {
21-
return {}
22-
}
2320
};
2421

2522

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

7067
it('basic ACL', (done) => {
7168
var input = {ACL: {'0123': {'read': true, 'write': true}}};
72-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
69+
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
7370
// This just checks that it doesn't crash, but it should check format.
7471
done();
7572
});
@@ -124,15 +121,17 @@ describe('transformWhere', () => {
124121
describe('mongoObjectToParseObject', () => {
125122
it('built-in timestamps', (done) => {
126123
var input = {createdAt: new Date(), updatedAt: new Date()};
127-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
124+
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
128125
expect(typeof output.createdAt).toEqual('string');
129126
expect(typeof output.updatedAt).toEqual('string');
130127
done();
131128
});
132129

133130
it('pointer', (done) => {
134131
var input = {_p_userPointer: '_User$123'};
135-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
132+
var output = transform.mongoObjectToParseObject(null, input, {
133+
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
134+
});
136135
expect(typeof output.userPointer).toEqual('object');
137136
expect(output.userPointer).toEqual(
138137
{__type: 'Pointer', className: '_User', objectId: '123'}
@@ -142,22 +141,28 @@ describe('mongoObjectToParseObject', () => {
142141

143142
it('null pointer', (done) => {
144143
var input = {_p_userPointer: null};
145-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
144+
var output = transform.mongoObjectToParseObject(null, input, {
145+
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
146+
});
146147
expect(output.userPointer).toBeUndefined();
147148
done();
148149
});
149150

150151
it('file', (done) => {
151152
var input = {picture: 'pic.jpg'};
152-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
153+
var output = transform.mongoObjectToParseObject(null, input, {
154+
fields: { picture: { type: 'File' }},
155+
});
153156
expect(typeof output.picture).toEqual('object');
154157
expect(output.picture).toEqual({__type: 'File', name: 'pic.jpg'});
155158
done();
156159
});
157160

158161
it('geopoint', (done) => {
159162
var input = {location: [180, -180]};
160-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
163+
var output = transform.mongoObjectToParseObject(null, input, {
164+
fields: { location: { type: 'GeoPoint' }},
165+
});
161166
expect(typeof output.location).toEqual('object');
162167
expect(output.location).toEqual(
163168
{__type: 'GeoPoint', longitude: 180, latitude: -180}
@@ -167,7 +172,9 @@ describe('mongoObjectToParseObject', () => {
167172

168173
it('nested array', (done) => {
169174
var input = {arr: [{_testKey: 'testValue' }]};
170-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
175+
var output = transform.mongoObjectToParseObject(null, input, {
176+
fields: { arr: { type: 'Array' } },
177+
});
171178
expect(Array.isArray(output.arr)).toEqual(true);
172179
expect(output.arr).toEqual([{ _testKey: 'testValue'}]);
173180
done();
@@ -185,7 +192,9 @@ describe('mongoObjectToParseObject', () => {
185192
},
186193
regularKey: "some data",
187194
}]}
188-
let output = transform.mongoObjectToParseObject(dummySchema, null, input);
195+
let output = transform.mongoObjectToParseObject(null, input, {
196+
fields: { array: { type: 'Array' }},
197+
});
189198
expect(dd(output, input)).toEqual(undefined);
190199
done();
191200
});
@@ -224,7 +233,7 @@ describe('transform schema key changes', () => {
224233
"Kevin": { "write": true }
225234
}
226235
};
227-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
236+
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
228237
expect(typeof output._rperm).toEqual('object');
229238
expect(typeof output._wperm).toEqual('object');
230239
expect(output.ACL).toBeUndefined();
@@ -241,7 +250,7 @@ describe('transform schema key changes', () => {
241250
}
242251
};
243252

244-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
253+
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
245254
expect(typeof output._acl).toEqual('object');
246255
expect(output._acl["Kevin"].w).toBeTruthy();
247256
expect(output._acl["Kevin"].r).toBeUndefined();
@@ -253,7 +262,7 @@ describe('transform schema key changes', () => {
253262
_rperm: ["*"],
254263
_wperm: ["Kevin"]
255264
};
256-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
265+
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
257266
expect(typeof output.ACL).toEqual('object');
258267
expect(output._rperm).toBeUndefined();
259268
expect(output._wperm).toBeUndefined();
@@ -267,7 +276,12 @@ describe('transform schema key changes', () => {
267276
long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER),
268277
double: new mongodb.Double(Number.MAX_VALUE)
269278
}
270-
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
279+
var output = transform.mongoObjectToParseObject(null, input, {
280+
fields: {
281+
long: { type: 'Number' },
282+
double: { type: 'Number' },
283+
},
284+
});
271285
expect(output.long).toBe(Number.MAX_SAFE_INTEGER);
272286
expect(output.double).toBe(Number.MAX_VALUE);
273287
done();

spec/ParseAPI.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ describe('miscellaneous', function() {
11211121
})
11221122
});
11231123

1124-
it('does not change inner object key names _auth_data_something', done => {
1124+
it('does not change inner object keys named _auth_data_something', done => {
11251125
new Parse.Object('O').save({ innerObj: {_auth_data_facebook: 7}})
11261126
.then(object => new Parse.Query('O').get(object.id))
11271127
.then(object => {

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,18 @@ export class MongoStorageAdapter {
198198
}
199199

200200
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
201-
// Accepts the schemaController for legacy reasons.
202-
find(className, query, { skip, limit, sort }, schemaController) {
201+
find(className, query, schema, { skip, limit, sort }) {
202+
let mongoWhere = this.transform.transformWhere(className, query, schema);
203+
let mongoSort = _.mapKeys(sort, (value, fieldName) => transform.transformKey(className, fieldName, schema));
203204
return this.adaptiveCollection(className)
204-
.then(collection => collection.find(query, { skip, limit, sort }))
205-
.then(objects => objects.map(object => transform.mongoObjectToParseObject(schemaController, className, object)));
205+
.then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort }))
206+
.then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema)));
207+
}
208+
209+
// Executs a count.
210+
count(className, query, schema) {
211+
return this.adaptiveCollection(className)
212+
.then(collection => collection.count(transform.transformWhere(className, query, schema)));
206213
}
207214

208215
get transform() {

src/Adapters/Storage/Mongo/MongoTransform.js

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,7 @@ const nestedMongoObjectToNestedParseObject = mongoObject => {
755755

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

831831
if (key.indexOf('_p_') == 0) {
832832
var newKey = key.substring(3);
833-
var expected;
834-
if (schema && schema.getExpectedType) {
835-
expected = schema.getExpectedType(className, newKey);
836-
}
837-
if (!expected) {
838-
log.info('transform.js',
839-
'Found a pointer column not in the schema, dropping it.',
840-
className, newKey);
833+
if (!schema.fields[newKey]) {
834+
log.info('transform.js', 'Found a pointer column not in the schema, dropping it.', className, newKey);
841835
break;
842836
}
843-
if (expected && expected.type !== 'Pointer') {
837+
if (schema.fields[newKey].type !== 'Pointer') {
844838
log.info('transform.js', 'Found a pointer in a non-pointer column, dropping it.', className, key);
845839
break;
846840
}
847841
if (mongoObject[key] === null) {
848842
break;
849843
}
850844
var objData = mongoObject[key].split('$');
851-
var newClass = (expected ? expected.targetClass : objData[0]);
852-
if (objData[0] !== newClass) {
845+
if (objData[0] !== schema.fields[newKey].targetClass) {
853846
throw 'pointer to incorrect className';
854847
}
855848
restObject[newKey] = {
@@ -861,13 +854,12 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
861854
} else if (key[0] == '_' && key != '__type') {
862855
throw ('bad key in untransform: ' + key);
863856
} else {
864-
var expectedType = schema.getExpectedType(className, key);
865857
var value = mongoObject[key];
866-
if (expectedType && expectedType.type === 'File' && FileCoder.isValidDatabaseObject(value)) {
858+
if (schema.fields[key] && schema.fields[key].type === 'File' && FileCoder.isValidDatabaseObject(value)) {
867859
restObject[key] = FileCoder.databaseToJSON(value);
868860
break;
869861
}
870-
if (expectedType && expectedType.type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
862+
if (schema.fields[key] && schema.fields[key].type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
871863
restObject[key] = GeoPointCoder.databaseToJSON(value);
872864
break;
873865
}
@@ -876,7 +868,16 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
876868
}
877869
}
878870

879-
return { ...restObject, ...schema.getRelationFields(className) };
871+
const relationFieldNames = Object.keys(schema.fields).filter(fieldName => schema.fields[fieldName].type === 'Relation');
872+
let relationFields = {};
873+
relationFieldNames.forEach(relationFieldName => {
874+
relationFields[relationFieldName] = {
875+
__type: 'Relation',
876+
className: schema.fields[relationFieldName].targetClass,
877+
}
878+
});
879+
880+
return { ...restObject, ...relationFields };
880881
default:
881882
throw 'unknown js type';
882883
}

src/Controllers/DatabaseController.js

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -628,16 +628,9 @@ DatabaseController.prototype.find = function(className, query, {
628628
skip,
629629
limit,
630630
acl,
631-
sort,
631+
sort = {},
632632
count,
633633
} = {}) {
634-
let mongoOptions = {};
635-
if (skip) {
636-
mongoOptions.skip = skip;
637-
}
638-
if (limit) {
639-
mongoOptions.limit = limit;
640-
}
641634
let isMaster = acl === undefined;
642635
let aclGroup = acl || [];
643636
let op = typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find';
@@ -653,34 +646,29 @@ DatabaseController.prototype.find = function(className, query, {
653646
throw error;
654647
})
655648
.then(schema => {
656-
if (sort) {
657-
mongoOptions.sort = {};
658-
for (let fieldName in sort) {
659-
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
660-
// so duplicate that behaviour here.
661-
if (fieldName === '_created_at') {
662-
fieldName = 'createdAt';
663-
sort['createdAt'] = sort['_created_at'];
664-
} else if (fieldName === '_updated_at') {
665-
fieldName = 'updatedAt';
666-
sort['updatedAt'] = sort['_updated_at'];
667-
}
668-
669-
if (!SchemaController.fieldNameIsValid(fieldName)) {
670-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
671-
}
672-
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
673-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
674-
}
675-
const mongoKey = this.transform.transformKey(className, fieldName, schema);
676-
mongoOptions.sort[mongoKey] = sort[fieldName];
677-
}
649+
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
650+
// so duplicate that behaviour here. If both are specified, the corrent behaviour to match Parse.com is to
651+
// use the one that appears first in the sort list.
652+
if (sort._created_at) {
653+
sort.createdAt = sort._created_at;
654+
delete sort._created_at;
655+
}
656+
if (sort._updated_at) {
657+
sort.updatedAt = sort._updated_at;
658+
delete sort._updated_at;
678659
}
660+
Object.keys(sort).forEach(fieldName => {
661+
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
662+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
663+
}
664+
if (!SchemaController.fieldNameIsValid(fieldName)) {
665+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
666+
}
667+
});
679668
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op))
680669
.then(() => this.reduceRelationKeys(className, query))
681670
.then(() => this.reduceInRelation(className, query, schemaController))
682-
.then(() => this.adapter.adaptiveCollection(className))
683-
.then(collection => {
671+
.then(() => {
684672
if (!isMaster) {
685673
query = this.addPointerPermissions(schemaController, className, op, query, aclGroup);
686674
}
@@ -696,12 +684,10 @@ DatabaseController.prototype.find = function(className, query, {
696684
query = addReadACL(query, aclGroup);
697685
}
698686
validateQuery(query);
699-
let mongoWhere = this.transform.transformWhere(className, query, schema);
700687
if (count) {
701-
delete mongoOptions.limit;
702-
return collection.count(mongoWhere, mongoOptions);
688+
return this.adapter.count(className, query, schema);
703689
} else {
704-
return this.adapter.find(className, mongoWhere, mongoOptions, schemaController)
690+
return this.adapter.find(className, query, schema, { skip, limit, sort })
705691
.then(objects => objects.map(object => filterSensitiveData(isMaster, aclGroup, className, object)));
706692
}
707693
});

src/Controllers/SchemaController.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -686,23 +686,6 @@ class SchemaController {
686686
hasClass(className) {
687687
return this.reloadData().then(() => !!(this.data[className]));
688688
}
689-
690-
getRelationFields(className) {
691-
if (this.data && this.data[className]) {
692-
let classData = this.data[className];
693-
return Object.keys(classData).filter((field) => {
694-
return classData[field].type === 'Relation';
695-
}).reduce((memo, field) => {
696-
let type = classData[field];
697-
memo[field] = {
698-
__type: 'Relation',
699-
className: type.targetClass
700-
};
701-
return memo;
702-
}, {});
703-
}
704-
return {};
705-
}
706689
}
707690

708691
// Returns a promise for a new Schema.

0 commit comments

Comments
 (0)