Skip to content

Commit cc5c03b

Browse files
authored
bail out of executeSubSelectedArray calls for empty arrays (#11670)
* potential optimization - bail out of `executeSubSelectedArray` calls * simplify logic, add test * update size-limits * Clean up Prettier, Size-limit, and Api-Extractor --------- Co-authored-by: phryneas <[email protected]>
1 parent 6393537 commit cc5c03b

File tree

4 files changed

+81
-11
lines changed

4 files changed

+81
-11
lines changed

.changeset/mean-singers-cheer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Bail out of `executeSubSelectedArray` calls if the array has 0 elements.

.size-limits.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"dist/apollo-client.min.cjs": 39319,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32630
2+
"dist/apollo-client.min.cjs": 39325,
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32634
44
}

src/cache/inmemory/__tests__/readFromStore.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,69 @@ describe("reading from the store", () => {
560560
});
561561
});
562562

563+
it("runs a nested query - skips iterating into an empty array", () => {
564+
const reader = new StoreReader({
565+
cache: new InMemoryCache(),
566+
});
567+
568+
const result = {
569+
id: "abcd",
570+
stringField: "This is a string!",
571+
numberField: 5,
572+
nullField: null,
573+
nestedArray: [
574+
{
575+
id: "abcde",
576+
stringField: "This is a string also!",
577+
numberField: 7,
578+
nullField: null,
579+
},
580+
],
581+
emptyArray: [],
582+
} satisfies StoreObject;
583+
584+
const store = defaultNormalizedCacheFactory({
585+
ROOT_QUERY: { ...result, nestedArray: [makeReference("abcde")] },
586+
abcde: result.nestedArray[0],
587+
});
588+
589+
expect(reader["executeSubSelectedArray"].size).toBe(0);
590+
591+
// assumption: cache size does not increase for empty array
592+
readQueryFromStore(reader, {
593+
store,
594+
query: gql`
595+
{
596+
stringField
597+
numberField
598+
emptyArray {
599+
id
600+
stringField
601+
numberField
602+
}
603+
}
604+
`,
605+
});
606+
expect(reader["executeSubSelectedArray"].size).toBe(0);
607+
608+
// assumption: cache size increases for array with content
609+
readQueryFromStore(reader, {
610+
store,
611+
query: gql`
612+
{
613+
stringField
614+
numberField
615+
nestedArray {
616+
id
617+
stringField
618+
numberField
619+
}
620+
}
621+
`,
622+
});
623+
expect(reader["executeSubSelectedArray"].size).toBe(1);
624+
});
625+
563626
it("throws on a missing field", () => {
564627
const result = {
565628
id: "abcd",

src/cache/inmemory/readFromStore.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -403,15 +403,17 @@ export class StoreReader {
403403
});
404404
}
405405
} else if (isArray(fieldValue)) {
406-
fieldValue = handleMissing(
407-
this.executeSubSelectedArray({
408-
field: selection,
409-
array: fieldValue,
410-
enclosingRef,
411-
context,
412-
}),
413-
resultName
414-
);
406+
if (fieldValue.length > 0) {
407+
fieldValue = handleMissing(
408+
this.executeSubSelectedArray({
409+
field: selection,
410+
array: fieldValue,
411+
enclosingRef,
412+
context,
413+
}),
414+
resultName
415+
);
416+
}
415417
} else if (!selection.selectionSet) {
416418
// If the field does not have a selection set, then we handle it
417419
// as a scalar value. To keep this.canon from canonicalizing

0 commit comments

Comments
 (0)