From 6b39e3f19ba0944b38c8b2822945dc7ca847731c Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 14 Mar 2024 11:40:19 +0100 Subject: [PATCH 1/4] potential optimization - bail out of `executeSubSelectedArray` calls --- .changeset/mean-singers-cheer.md | 5 +++++ src/cache/inmemory/readFromStore.ts | 21 ++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 .changeset/mean-singers-cheer.md diff --git a/.changeset/mean-singers-cheer.md b/.changeset/mean-singers-cheer.md new file mode 100644 index 0000000000..b2cb9cd098 --- /dev/null +++ b/.changeset/mean-singers-cheer.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Bail out of `executeSubSelectedArray` calls if the array has 0 elements. diff --git a/src/cache/inmemory/readFromStore.ts b/src/cache/inmemory/readFromStore.ts index cc1ec9f0f6..d0e0d6b249 100644 --- a/src/cache/inmemory/readFromStore.ts +++ b/src/cache/inmemory/readFromStore.ts @@ -403,15 +403,18 @@ export class StoreReader { }); } } else if (isArray(fieldValue)) { - fieldValue = handleMissing( - this.executeSubSelectedArray({ - field: selection, - array: fieldValue, - enclosingRef, - context, - }), - resultName - ); + fieldValue = + fieldValue.length === 0 ? + fieldValue + : handleMissing( + this.executeSubSelectedArray({ + field: selection, + array: fieldValue, + enclosingRef, + context, + }), + resultName + ); } else if (!selection.selectionSet) { // If the field does not have a selection set, then we handle it // as a scalar value. To keep this.canon from canonicalizing From f2bc6c30679f7c27ede6f0c3da108968c0ba8c77 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Mon, 25 Mar 2024 12:09:39 +0100 Subject: [PATCH 2/4] simplify logic, add test --- src/cache/inmemory/__tests__/readFromStore.ts | 63 +++++++++++++++++++ src/cache/inmemory/readFromStore.ts | 23 ++++--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/cache/inmemory/__tests__/readFromStore.ts b/src/cache/inmemory/__tests__/readFromStore.ts index 2af1138cef..d936ad7373 100644 --- a/src/cache/inmemory/__tests__/readFromStore.ts +++ b/src/cache/inmemory/__tests__/readFromStore.ts @@ -560,6 +560,69 @@ describe("reading from the store", () => { }); }); + it("runs a nested query - skips iterating into an empty array", () => { + const reader = new StoreReader({ + cache: new InMemoryCache(), + }); + + const result = { + id: "abcd", + stringField: "This is a string!", + numberField: 5, + nullField: null, + nestedArray: [ + { + id: "abcde", + stringField: "This is a string also!", + numberField: 7, + nullField: null, + }, + ], + emptyArray: [], + } satisfies StoreObject; + + const store = defaultNormalizedCacheFactory({ + ROOT_QUERY: { ...result, nestedArray: [makeReference("abcde")] }, + abcde: result.nestedArray[0], + }); + + expect(reader["executeSubSelectedArray"].size).toBe(0); + + // assumption: cache size does not increase for empty array + readQueryFromStore(reader, { + store, + query: gql` + { + stringField + numberField + emptyArray { + id + stringField + numberField + } + } + `, + }); + expect(reader["executeSubSelectedArray"].size).toBe(0); + + // assumption: cache size increases for array with content + readQueryFromStore(reader, { + store, + query: gql` + { + stringField + numberField + nestedArray { + id + stringField + numberField + } + } + `, + }); + expect(reader["executeSubSelectedArray"].size).toBe(1); + }); + it("throws on a missing field", () => { const result = { id: "abcd", diff --git a/src/cache/inmemory/readFromStore.ts b/src/cache/inmemory/readFromStore.ts index d0e0d6b249..d89876d85c 100644 --- a/src/cache/inmemory/readFromStore.ts +++ b/src/cache/inmemory/readFromStore.ts @@ -403,18 +403,17 @@ export class StoreReader { }); } } else if (isArray(fieldValue)) { - fieldValue = - fieldValue.length === 0 ? - fieldValue - : handleMissing( - this.executeSubSelectedArray({ - field: selection, - array: fieldValue, - enclosingRef, - context, - }), - resultName - ); + if (fieldValue.length > 0) { + fieldValue = handleMissing( + this.executeSubSelectedArray({ + field: selection, + array: fieldValue, + enclosingRef, + context, + }), + resultName + ); + } } else if (!selection.selectionSet) { // If the field does not have a selection set, then we handle it // as a scalar value. To keep this.canon from canonicalizing From 8c6860cd336f1a82aeb4ee5e627dcff7d11e4493 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Mon, 25 Mar 2024 12:13:39 +0100 Subject: [PATCH 3/4] update size-limits --- .size-limits.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.size-limits.json b/.size-limits.json index cdae2c9cd4..119cdb06f2 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 39247, - "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32630 + "dist/apollo-client.min.cjs": 39254, + "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32635 } From aa388af1bf632ed8d33cd110ed9a7488e4badabc Mon Sep 17 00:00:00 2001 From: phryneas Date: Mon, 25 Mar 2024 11:16:25 +0000 Subject: [PATCH 4/4] Clean up Prettier, Size-limit, and Api-Extractor --- .size-limits.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.size-limits.json b/.size-limits.json index 119cdb06f2..f964b98787 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 39254, - "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32635 + "dist/apollo-client.min.cjs": 39325, + "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32634 }