Skip to content

Commit 36683fd

Browse files
committed
enhance: Hoist getEntityCaches computation to MemoCache init
1 parent 1ded1d8 commit 36683fd

File tree

9 files changed

+110
-75
lines changed

9 files changed

+110
-75
lines changed

packages/normalizr/src/memo/MemoCache.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import WeakDependencyMap from './WeakDependencyMap.js';
33
import buildQueryKey from '../buildQueryKey.js';
44
import { getDependency, type BaseDelegate } from './BaseDelegate.js';
55
import { PlainDelegate } from './Delegate.js';
6+
import { GetEntityCache, getEntityCaches } from './entitiesCache.js';
67
import { getEntities } from '../denormalize/getEntities.js';
78
import getUnvisit from '../denormalize/unvisit.js';
89
import type {
@@ -12,7 +13,7 @@ import type {
1213
Schema,
1314
} from '../interface.js';
1415
import type { DenormalizeNullable, NormalizeNullable } from '../types.js';
15-
import { EndpointsCache, EntityCache } from './types.js';
16+
import { EndpointsCache } from './types.js';
1617
import type { INVALID } from '../denormalize/symbol.js';
1718

1819
type DelegateClass = new (v: { entities: any; indexes: any }) => BaseDelegate;
@@ -22,15 +23,17 @@ type DelegateClass = new (v: { entities: any; indexes: any }) => BaseDelegate;
2223
/** Singleton to store the memoization cache for denormalization methods */
2324
export default class MemoCache {
2425
/** Cache for every entity based on its dependencies and its own input */
25-
protected entities: EntityCache = new Map();
26+
declare protected _getCache: GetEntityCache;
2627
/** Caches the final denormalized form based on input, entities */
2728
protected endpoints: EndpointsCache = new WeakDependencyMap<EntityPath>();
2829
/** Caches the queryKey based on schema, args, and any used entities or indexes */
2930
protected queryKeys: Map<string, WeakDependencyMap<QueryPath>> = new Map();
31+
3032
declare protected Delegate: DelegateClass;
3133

3234
constructor(D: DelegateClass = PlainDelegate) {
3335
this.Delegate = D;
36+
this._getCache = getEntityCaches(new Map());
3437
}
3538

3639
/** Compute denormalized form maintaining referential equality for same inputs */
@@ -58,7 +61,7 @@ export default class MemoCache {
5861

5962
return getUnvisit(
6063
getEntity,
61-
new GlobalCache(getEntity, this.entities, this.endpoints),
64+
new GlobalCache(getEntity, this._getCache, this.endpoints),
6265
args,
6366
)(schema, input);
6467
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { EntityInterface, EntityPath } from '../interface.js';
2+
import { EntityCache } from './types.js';
3+
import WeakDependencyMap from './WeakDependencyMap.js';
4+
5+
export type GetEntityCache = (
6+
pk: string,
7+
schema: EntityInterface,
8+
) => WeakDependencyMap<EntityPath, object, any>;
9+
10+
export const getEntityCaches = (entityCache: EntityCache): GetEntityCache => {
11+
return (pk: string, schema: EntityInterface) => {
12+
const key = schema.key;
13+
// collections should use the entities they collect over
14+
// TODO: this should be based on a public interface
15+
const entityInstance: EntityInterface = (schema.cacheWith as any) ?? schema;
16+
17+
if (!entityCache.has(key)) {
18+
entityCache.set(key, new Map());
19+
}
20+
const entityCacheKey = entityCache.get(key)!;
21+
if (!entityCacheKey.get(pk))
22+
entityCacheKey.set(
23+
pk,
24+
new WeakMap<
25+
EntityInterface,
26+
WeakDependencyMap<EntityPath, object, any>
27+
>(),
28+
);
29+
30+
const entityCachePk = entityCacheKey.get(pk) as WeakMap<
31+
EntityInterface,
32+
WeakDependencyMap<EntityPath, object, any>
33+
>;
34+
let wem = entityCachePk.get(entityInstance) as WeakDependencyMap<
35+
EntityPath,
36+
object,
37+
any
38+
>;
39+
if (!wem) {
40+
wem = new WeakDependencyMap<EntityPath, object, any>();
41+
entityCachePk.set(entityInstance, wem);
42+
}
43+
44+
return wem;
45+
};
46+
};

packages/normalizr/src/memo/globalCache.ts

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
1-
import { EndpointsCache, EntityCache } from './types.js';
1+
import { EndpointsCache } from './types.js';
22
import WeakDependencyMap, { type Dep } from './WeakDependencyMap.js';
33
import type Cache from '../denormalize/cache.js';
44
import type { GetEntity } from '../denormalize/getEntities.js';
55
import type { INVALID } from '../denormalize/symbol.js';
66
import type { EntityInterface, EntityPath } from '../interface.js';
7+
import type { GetEntityCache } from './entitiesCache.js';
78

89
export default class GlobalCache implements Cache {
910
private dependencies: Dep<EntityPath>[] = [];
1011
private cycleCache: Map<string, Map<string, number>> = new Map();
1112
private cycleIndex = -1;
1213
private localCache: Map<string, Map<string, any>> = new Map();
1314

14-
declare private getCache: (
15-
pk: string,
16-
schema: EntityInterface,
17-
) => WeakDependencyMap<EntityPath, object, any>;
15+
declare private _getCache: GetEntityCache;
1816

1917
declare private _getEntity: GetEntity;
20-
declare private resultCache: EndpointsCache;
18+
declare private _resultCache: EndpointsCache;
2119

2220
constructor(
2321
getEntity: GetEntity,
24-
entityCache: EntityCache,
22+
getCache: GetEntityCache,
2523
resultCache: EndpointsCache,
2624
) {
2725
this._getEntity = getEntity;
28-
this.getCache = getEntityCaches(entityCache);
29-
this.resultCache = resultCache;
26+
this._getCache = getCache;
27+
this._resultCache = resultCache;
3028
}
3129

3230
getEntity(
@@ -43,7 +41,7 @@ export default class GlobalCache implements Cache {
4341
EntityPath,
4442
object,
4543
EntityCacheValue
46-
> = this.getCache(pk, schema);
44+
> = this._getCache(pk, schema);
4745
const [cacheValue, cachePath] = globalCache.get(entity, this._getEntity);
4846
// TODO: what if this just returned the deps - then we don't need to store them
4947

@@ -118,15 +116,15 @@ export default class GlobalCache implements Cache {
118116
return { data: computeValue(), paths: this.paths() };
119117
}
120118

121-
let [data, paths] = this.resultCache.get(input, this._getEntity);
119+
let [data, paths] = this._resultCache.get(input, this._getEntity);
122120

123121
if (paths === undefined) {
124122
data = computeValue();
125123
// we want to do this before we add our 'input' entry
126124
paths = this.paths();
127125
// for the first entry, `path` is ignored so empty members is fine
128126
this.dependencies.unshift({ path: { key: '', pk: '' }, entity: input });
129-
this.resultCache.set(this.dependencies, data);
127+
this._resultCache.set(this.dependencies, data);
130128
} else {
131129
paths.shift();
132130
}
@@ -142,41 +140,3 @@ interface EntityCacheValue {
142140
dependencies: Dep<EntityPath>[];
143141
value: object | typeof INVALID | undefined;
144142
}
145-
146-
const getEntityCaches = (entityCache: EntityCache) => {
147-
return (pk: string, schema: EntityInterface) => {
148-
const key = schema.key;
149-
// collections should use the entities they collect over
150-
// TODO: this should be based on a public interface
151-
const entityInstance: EntityInterface = (schema.cacheWith as any) ?? schema;
152-
153-
if (!entityCache.has(key)) {
154-
entityCache.set(key, new Map());
155-
}
156-
const entityCacheKey = entityCache.get(key)!;
157-
if (!entityCacheKey.get(pk))
158-
entityCacheKey.set(
159-
pk,
160-
new WeakMap<
161-
EntityInterface,
162-
WeakDependencyMap<EntityPath, object, any>
163-
>(),
164-
);
165-
166-
const entityCachePk = entityCacheKey.get(pk) as WeakMap<
167-
EntityInterface,
168-
WeakDependencyMap<EntityPath, object, any>
169-
>;
170-
let wem = entityCachePk.get(entityInstance) as WeakDependencyMap<
171-
EntityPath,
172-
object,
173-
any
174-
>;
175-
if (!wem) {
176-
wem = new WeakDependencyMap<EntityPath, object, any>();
177-
entityCachePk.set(entityInstance, wem);
178-
}
179-
180-
return wem;
181-
};
182-
};

website/src/components/Playground/editor-types/@data-client/core.d.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,23 @@ interface NormalizedIndex {
4949
};
5050
};
5151
}
52-
type EntityPath = [key: string, pk: string];
52+
/** Used in denormalize. Lookup to find an entity in the store table */
53+
interface EntityPath {
54+
key: string;
55+
pk: string;
56+
}
5357
type IndexPath = [key: string, index: string, value: string];
5458
type EntitiesPath = [key: string];
55-
type QueryPath = IndexPath | EntityPath | EntitiesPath;
59+
type QueryPath = IndexPath | [key: string, pk: string] | EntitiesPath;
5660
/** Get all normalized entities of one type from store */
5761
interface GetEntities {
58-
(...path: EntitiesPath): {
62+
(key: string): {
5963
readonly [pk: string]: any;
6064
} | undefined;
6165
}
6266
/** Get normalized Entity from store */
6367
interface GetEntity {
64-
(...path: EntityPath): any;
68+
(key: string, pk: string): any;
6569
}
6670
/** Get PK using an Entity Index */
6771
interface GetIndex {
@@ -226,7 +230,7 @@ declare abstract class BaseDelegate {
226230
indexes: any;
227231
});
228232
abstract getEntities(...path: EntitiesPath): object | undefined;
229-
abstract getEntity(...path: EntityPath): object | undefined;
233+
abstract getEntity(key: string, pk: string): object | undefined;
230234
abstract getIndex(...path: IndexPath): object | undefined;
231235
abstract getIndexEnd(entity: any, value: string): string | undefined;
232236
tracked(schema: any): [delegate: IQueryDelegate, dependencies: Dep<QueryPath>[]];
@@ -249,6 +253,7 @@ declare class MemoCache {
249253
/** Caches the queryKey based on schema, args, and any used entities or indexes */
250254
protected queryKeys: Map<string, WeakDependencyMap<QueryPath>>;
251255
protected Delegate: DelegateClass;
256+
private _getCache;
252257
constructor(D?: DelegateClass);
253258
/** Compute denormalized form maintaining referential equality for same inputs */
254259
denormalize<S extends Schema>(schema: S | undefined, input: unknown, entities: any, args?: readonly any[]): {

website/src/components/Playground/editor-types/@data-client/endpoint.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,11 @@ interface Visit {
253253
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
254254
creating?: boolean;
255255
}
256-
type EntityPath = [key: string, pk: string];
256+
/** Used in denormalize. Lookup to find an entity in the store table */
257+
interface EntityPath {
258+
key: string;
259+
pk: string;
260+
}
257261
type IndexPath = [key: string, index: string, value: string];
258262
type EntitiesPath = [key: string];
259263
/** Returns true if a circular reference is found */
@@ -262,13 +266,13 @@ interface CheckLoop {
262266
}
263267
/** Get all normalized entities of one type from store */
264268
interface GetEntities {
265-
(...path: EntitiesPath): {
269+
(key: string): {
266270
readonly [pk: string]: any;
267271
} | undefined;
268272
}
269273
/** Get normalized Entity from store */
270274
interface GetEntity {
271-
(...path: EntityPath): any;
275+
(key: string, pk: string): any;
272276
}
273277
/** Get PK using an Entity Index */
274278
interface GetIndex {

website/src/components/Playground/editor-types/@data-client/graphql.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,11 @@ interface Visit {
253253
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
254254
creating?: boolean;
255255
}
256-
type EntityPath = [key: string, pk: string];
256+
/** Used in denormalize. Lookup to find an entity in the store table */
257+
interface EntityPath {
258+
key: string;
259+
pk: string;
260+
}
257261
type IndexPath = [key: string, index: string, value: string];
258262
type EntitiesPath = [key: string];
259263
/** Returns true if a circular reference is found */
@@ -262,13 +266,13 @@ interface CheckLoop {
262266
}
263267
/** Get all normalized entities of one type from store */
264268
interface GetEntities {
265-
(...path: EntitiesPath): {
269+
(key: string): {
266270
readonly [pk: string]: any;
267271
} | undefined;
268272
}
269273
/** Get normalized Entity from store */
270274
interface GetEntity {
271-
(...path: EntityPath): any;
275+
(key: string, pk: string): any;
272276
}
273277
/** Get PK using an Entity Index */
274278
interface GetIndex {

website/src/components/Playground/editor-types/@data-client/normalizr.d.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,27 @@ interface EntityTable {
5858
interface Visit {
5959
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
6060
}
61-
type EntityPath = [key: string, pk: string];
61+
/** Used in denormalize. Lookup to find an entity in the store table */
62+
interface EntityPath {
63+
key: string;
64+
pk: string;
65+
}
6266
type IndexPath = [key: string, index: string, value: string];
6367
type EntitiesPath = [key: string];
64-
type QueryPath = IndexPath | EntityPath | EntitiesPath;
68+
type QueryPath = IndexPath | [key: string, pk: string] | EntitiesPath;
6569
/** Returns true if a circular reference is found */
6670
interface CheckLoop {
6771
(entityKey: string, pk: string, input: object): boolean;
6872
}
6973
/** Get all normalized entities of one type from store */
7074
interface GetEntities {
71-
(...path: EntitiesPath): {
75+
(key: string): {
7276
readonly [pk: string]: any;
7377
} | undefined;
7478
}
7579
/** Get normalized Entity from store */
7680
interface GetEntity {
77-
(...path: EntityPath): any;
81+
(key: string, pk: string): any;
7882
}
7983
/** Get PK using an Entity Index */
8084
interface GetIndex {
@@ -266,7 +270,7 @@ declare abstract class BaseDelegate {
266270
indexes: any;
267271
});
268272
abstract getEntities(...path: EntitiesPath): object | undefined;
269-
abstract getEntity(...path: EntityPath): object | undefined;
273+
abstract getEntity(key: string, pk: string): object | undefined;
270274
abstract getIndex(...path: IndexPath): object | undefined;
271275
abstract getIndexEnd(entity: any, value: string): string | undefined;
272276
tracked(schema: any): [delegate: IQueryDelegate, dependencies: Dep<QueryPath>[]];
@@ -289,6 +293,7 @@ declare class MemoCache {
289293
/** Caches the queryKey based on schema, args, and any used entities or indexes */
290294
protected queryKeys: Map<string, WeakDependencyMap<QueryPath>>;
291295
protected Delegate: DelegateClass;
296+
private _getCache;
292297
constructor(D?: DelegateClass);
293298
/** Compute denormalized form maintaining referential equality for same inputs */
294299
denormalize<S extends Schema>(schema: S | undefined, input: unknown, entities: any, args?: readonly any[]): {

website/src/components/Playground/editor-types/@data-client/rest.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,11 @@ interface Visit {
255255
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
256256
creating?: boolean;
257257
}
258-
type EntityPath = [key: string, pk: string];
258+
/** Used in denormalize. Lookup to find an entity in the store table */
259+
interface EntityPath {
260+
key: string;
261+
pk: string;
262+
}
259263
type IndexPath = [key: string, index: string, value: string];
260264
type EntitiesPath = [key: string];
261265
/** Returns true if a circular reference is found */
@@ -264,13 +268,13 @@ interface CheckLoop {
264268
}
265269
/** Get all normalized entities of one type from store */
266270
interface GetEntities {
267-
(...path: EntitiesPath): {
271+
(key: string): {
268272
readonly [pk: string]: any;
269273
} | undefined;
270274
}
271275
/** Get normalized Entity from store */
272276
interface GetEntity {
273-
(...path: EntityPath): any;
277+
(key: string, pk: string): any;
274278
}
275279
/** Get PK using an Entity Index */
276280
interface GetIndex {

0 commit comments

Comments
 (0)