Skip to content

Commit 87d6d91

Browse files
committed
Inflate included pointers in-place
I'd like to parallelize the fetching of included documents, but it would be tricky to do in the world where these methods are cloning the response. Instead of trying to do tricky processing on the responses to merge all the pointers, we can mutate the response in-place. This kind of in-place mutation can be viewed as more dangerous, particularly with multiple promises trying to run this kind of replacement in a row. However, we have some nice guarantees here: - Each path passed is unique; we don't need to worry about multiple calls each trying to replace the same documents. - When we start doing a replacement, all the path prefixes have already been replaced. In the current implementation this is known because we sort the include paths by path length, so if a path `a.b.c` is being processed, then the prefix paths `a` and `a.b` must have already been processed. This in-place mutation might also have a nice benefit in terms of memory usage; queries with lots of includes would have been rebuilding the result tree multiple times, causing extra allocations. The in-place method avoids this, and so might reduce memory usage for those queries.
1 parent e8bbde4 commit 87d6d91

File tree

1 file changed

+18
-25
lines changed

1 file changed

+18
-25
lines changed

src/RestQuery.js

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -760,17 +760,14 @@ RestQuery.prototype.handleInclude = function() {
760760
this.restOptions
761761
);
762762
if (pathResponse.then) {
763-
return pathResponse.then(newResponse => {
764-
this.response = newResponse;
763+
return pathResponse.then(() => {
765764
this.include = this.include.slice(1);
766765
return this.handleInclude();
767766
});
768767
} else if (this.include.length > 0) {
769768
this.include = this.include.slice(1);
770769
return this.handleInclude();
771770
}
772-
773-
return pathResponse;
774771
};
775772

776773
//Returns a promise of a processed set of results
@@ -821,7 +818,10 @@ RestQuery.prototype.runAfterFindTrigger = function() {
821818

822819
// Adds included values to the response.
823820
// Path is a list of field names.
824-
// Returns a promise for an augmented response.
821+
//
822+
// Modifies the response in place by replacing pointers with fetched objects
823+
// Returns a promise that resolves when all includes have been fetched and
824+
// substituted into the response.
825825
function includePath(config, auth, response, path, restOptions = {}) {
826826
var pointers = findPointers(response.results, path);
827827
if (pointers.length == 0) {
@@ -905,19 +905,12 @@ function includePath(config, auth, response, path, restOptions = {}) {
905905
return replace;
906906
}, {});
907907

908-
var resp = {
909-
results: replacePointers(response.results, path, replace),
910-
};
911-
if (response.count) {
912-
resp.count = response.count;
913-
}
914-
return resp;
908+
replacePointers(response.results, path, replace);
915909
});
916910
}
917911

918912
// Object may be a list of REST-format object to find pointers in, or
919913
// it may be a single object.
920-
// If the path yields things that aren't pointers, this throws an error.
921914
// Path is a list of fields to search into.
922915
// Returns a list of pointers in REST format.
923916
function findPointers(object, path) {
@@ -951,8 +944,8 @@ function findPointers(object, path) {
951944
// in, or it may be a single object.
952945
// Path is a list of fields to search into.
953946
// replace is a map from object id -> object.
954-
// Returns something analogous to object, but with the appropriate
955-
// pointers inflated.
947+
//
948+
// Performs in-place replacements on object
956949
function replacePointers(object, path, replace) {
957950
if (object instanceof Array) {
958951
return object
@@ -964,27 +957,27 @@ function replacePointers(object, path, replace) {
964957
return object;
965958
}
966959

960+
// base case: we're returning a replacement
967961
if (path.length === 0) {
968962
if (object && object.__type === 'Pointer') {
969963
return replace[object.objectId];
970964
}
971965
return object;
972966
}
973967

968+
// penultimate case: we're looking at the object holding a reference
969+
// to be mutated
970+
else if (path.length === 1) {
971+
object[path[0]] = replacePointers(object[path[0]], [], replace);
972+
return;
973+
}
974+
974975
var subobject = object[path[0]];
975976
if (!subobject) {
976977
return object;
977978
}
978-
var newsub = replacePointers(subobject, path.slice(1), replace);
979-
var answer = {};
980-
for (var key in object) {
981-
if (key == path[0]) {
982-
answer[key] = newsub;
983-
} else {
984-
answer[key] = object[key];
985-
}
986-
}
987-
return answer;
979+
980+
return replacePointers(subobject, path.slice(1), replace);
988981
}
989982

990983
// Finds a subobject that has the given key, if there is one.

0 commit comments

Comments
 (0)