Skip to content

Commit 09fee7d

Browse files
authored
Adds optimization for related relations (#4345)
* Adds optimization for related relations * Makes MongoStorageAdapter only able to sort on Join tables
1 parent 7223add commit 09fee7d

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

spec/ParseRelation.spec.js

+27
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,33 @@ describe('Parse.Relation testing', () => {
163163
});
164164
});
165165

166+
it("related at ordering optimizations", (done) => {
167+
var ChildObject = Parse.Object.extend("ChildObject");
168+
var childObjects = [];
169+
for (var i = 0; i < 10; i++) {
170+
childObjects.push(new ChildObject({x: i}));
171+
}
172+
173+
var parent;
174+
var relation;
175+
176+
Parse.Object.saveAll(childObjects).then(function() {
177+
var ParentObject = Parse.Object.extend('ParentObject');
178+
parent = new ParentObject();
179+
parent.set('x', 4);
180+
relation = parent.relation('child');
181+
relation.add(childObjects);
182+
return parent.save();
183+
}).then(function() {
184+
const query = relation.query();
185+
query.descending('createdAt');
186+
query.skip(1);
187+
query.limit(3);
188+
return query.find();
189+
}).then(function(list) {
190+
expect(list.length).toBe(3);
191+
}).then(done, done.fail);
192+
});
166193

167194
it_exclude_dbs(['postgres'])("queries with relations", (done) => {
168195

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export class MongoStorageAdapter {
8686
// Public
8787
connectionPromise;
8888
database;
89-
89+
canSortOnJoinTables;
9090
constructor({
9191
uri = defaults.DefaultMongoURI,
9292
collectionPrefix = '',
@@ -98,6 +98,7 @@ export class MongoStorageAdapter {
9898

9999
// MaxTimeMS is not a global MongoDB client option, it is applied per operation.
100100
this._maxTimeMS = mongoOptions.maxTimeMS;
101+
this.canSortOnJoinTables = true;
101102
delete mongoOptions.maxTimeMS;
102103
}
103104

src/Controllers/DatabaseController.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,16 @@ DatabaseController.prototype.deleteEverything = function() {
599599

600600
// Returns a promise for a list of related ids given an owning id.
601601
// className here is the owning className.
602-
DatabaseController.prototype.relatedIds = function(className, key, owningId) {
603-
return this.adapter.find(joinTableName(className, key), relationSchema, { owningId }, {})
602+
DatabaseController.prototype.relatedIds = function(className, key, owningId, queryOptions) {
603+
const { skip, limit, sort } = queryOptions;
604+
const findOptions = {};
605+
if (sort && sort.createdAt && this.adapter.canSortOnJoinTables) {
606+
findOptions.sort = { '_id' : sort.createdAt };
607+
findOptions.limit = limit;
608+
findOptions.skip = skip;
609+
queryOptions.skip = 0;
610+
}
611+
return this.adapter.find(joinTableName(className, key), relationSchema, { owningId }, findOptions)
604612
.then(results => results.map(result => result.relatedId));
605613
};
606614

@@ -693,11 +701,11 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
693701

694702
// Modifies query so that it no longer has $relatedTo
695703
// Returns a promise that resolves when query is mutated
696-
DatabaseController.prototype.reduceRelationKeys = function(className, query) {
704+
DatabaseController.prototype.reduceRelationKeys = function(className, query, queryOptions) {
697705

698706
if (query['$or']) {
699707
return Promise.all(query['$or'].map((aQuery) => {
700-
return this.reduceRelationKeys(className, aQuery);
708+
return this.reduceRelationKeys(className, aQuery, queryOptions);
701709
}));
702710
}
703711

@@ -706,11 +714,12 @@ DatabaseController.prototype.reduceRelationKeys = function(className, query) {
706714
return this.relatedIds(
707715
relatedTo.object.className,
708716
relatedTo.key,
709-
relatedTo.object.objectId)
717+
relatedTo.object.objectId,
718+
queryOptions)
710719
.then((ids) => {
711720
delete query['$relatedTo'];
712721
this.addInObjectIdsIds(ids, query);
713-
return this.reduceRelationKeys(className, query);
722+
return this.reduceRelationKeys(className, query, queryOptions);
714723
});
715724
}
716725
};
@@ -831,8 +840,9 @@ DatabaseController.prototype.find = function(className, query, {
831840
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
832841
}
833842
});
843+
const queryOptions = { skip, limit, sort, keys, readPreference };
834844
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op))
835-
.then(() => this.reduceRelationKeys(className, query))
845+
.then(() => this.reduceRelationKeys(className, query, queryOptions))
836846
.then(() => this.reduceInRelation(className, query, schemaController))
837847
.then(() => {
838848
if (!isMaster) {
@@ -871,7 +881,7 @@ DatabaseController.prototype.find = function(className, query, {
871881
if (!classExists) {
872882
return [];
873883
} else {
874-
return this.adapter.find(className, schema, query, { skip, limit, sort, keys, readPreference })
884+
return this.adapter.find(className, schema, query, queryOptions)
875885
.then(objects => objects.map(object => {
876886
object = untransformObjectACL(object);
877887
return filterSensitiveData(isMaster, aclGroup, className, object)

0 commit comments

Comments
 (0)