Skip to content

Commit 4ebe4e3

Browse files
committed
extract getEntityRelations and use it in findMany to fix hot-reloading
1 parent 2d3a39e commit 4ebe4e3

File tree

3 files changed

+62
-46
lines changed

3 files changed

+62
-46
lines changed
Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
import type { Any, AnyNoContext, Entity } from './types.js';
1+
import type { AnyNoContext, Entity } from './types.js';
2+
3+
export type DecodedEntitiesCacheEntry = {
4+
decoder: (data: unknown) => unknown;
5+
type: AnyNoContext; // TODO should be the type of the entity
6+
entities: Map<string, Entity<AnyNoContext>>; // holds all entities of this type
7+
queries: Map<
8+
string, // instead of serializedQueryKey as string we could also have the actual params
9+
{
10+
data: Array<Entity<AnyNoContext>>; // holds the decoded entities of this query and must be a stable reference and use the same reference for the `entities` array
11+
listeners: Array<() => void>; // listeners to this query
12+
}
13+
>;
14+
};
215

316
/*
417
/*
@@ -13,18 +26,7 @@ import type { Any, AnyNoContext, Entity } from './types.js';
1326
*/
1427
type DecodedEntitiesCache = Map<
1528
string, // type name
16-
{
17-
decoder: (data: unknown) => unknown;
18-
type: Any; // TODO should be the type of the entity
19-
entities: Map<string, Entity<AnyNoContext>>; // holds all entities of this type
20-
queries: Map<
21-
string, // instead of serializedQueryKey as string we could also have the actual params
22-
{
23-
data: Array<Entity<AnyNoContext>>; // holds the decoded entities of this query and must be a stable reference and use the same reference for the `entities` array
24-
listeners: Array<() => void>; // listeners to this query
25-
}
26-
>;
27-
}
29+
DecodedEntitiesCacheEntry
2830
>;
2931

3032
export const decodedEntitiesCache: DecodedEntitiesCache = new Map();

packages/hypergraph/src/entity/entity.ts

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import * as VariantSchema from '@effect/experimental/VariantSchema';
33
import * as Data from 'effect/Data';
44
import * as Schema from 'effect/Schema';
55
import { generateId } from '../utils/generateId.js';
6-
import { hasArrayField } from '../utils/hasArrayField.js';
76
import { decodedEntitiesCache } from './decodedEntitiesCache.js';
7+
import { getEntityRelations } from './getEntityRelations.js';
88
import type { AnyNoContext, Entity, Insert, Update } from './types.js';
99

1010
const {
@@ -98,34 +98,7 @@ export const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) =
9898
const cacheEntry = decodedEntitiesCache.get(typeName);
9999
if (!cacheEntry) continue;
100100

101-
const relations: Record<string, Entity<AnyNoContext>> = {};
102-
for (const [fieldName, field] of Object.entries(cacheEntry.type.fields)) {
103-
// check if the type exists in the cach and is a proper relation
104-
// TODO: what's the right way to get the name of the type?
105-
// @ts-expect-error name is defined
106-
const fieldCacheEntry = decodedEntitiesCache.get(field.name);
107-
if (!fieldCacheEntry) continue;
108-
109-
const relationEntities: Array<Entity<AnyNoContext>> = [];
110-
111-
if (hasArrayField(entity, fieldName)) {
112-
for (const relationEntityId of entity[fieldName]) {
113-
const relationEntity = doc.entities?.[relationEntityId];
114-
if (
115-
!relationEntity ||
116-
typeof relationEntity !== 'object' ||
117-
!('@@types@@' in relationEntity) ||
118-
!Array.isArray(relationEntity['@@types@@'])
119-
)
120-
continue;
121-
122-
relationEntities.push({ ...relationEntity, id: relationEntityId });
123-
}
124-
}
125-
126-
relations[fieldName] = relationEntities;
127-
}
128-
101+
const relations = getEntityRelations(entity, cacheEntry.type, doc);
129102
const decoded = cacheEntry.decoder({
130103
...entity,
131104
...relations,
@@ -294,16 +267,19 @@ export function findMany<const S extends AnyNoContext>(
294267
// @ts-expect-error name is defined
295268
const typeName = type.name;
296269

297-
// TODO: Instead of this insane filtering logic, we should be keeping track of the entities in
298-
// an index and store the decoded values instead of re-decoding over and over again.
299-
const entities = handle.docSync()?.entities ?? {};
270+
const doc = handle.docSync();
271+
if (!doc) {
272+
return [];
273+
}
274+
const entities = doc.entities ?? {};
300275
const filtered: Array<Entity<S>> = [];
301276
for (const id in entities) {
302277
const entity = entities[id];
303278
if (typeof entity === 'object' && entity != null && '@@types@@' in entity) {
304279
const types = entity['@@types@@'];
305280
if (Array.isArray(types) && types.includes(typeName)) {
306-
filtered.push({ ...decode({ ...entity, id }), type: typeName });
281+
const relations = getEntityRelations(entity, type, doc);
282+
filtered.push({ ...decode({ ...entity, ...relations, id }), type: typeName });
307283
}
308284
}
309285
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { hasArrayField } from '../utils/hasArrayField.js';
2+
import type { DocumentContent } from './index.js';
3+
import type { AnyNoContext, Entity } from './types.js';
4+
5+
export const getEntityRelations = <const S extends AnyNoContext>(
6+
entity: Entity<AnyNoContext>,
7+
type: S,
8+
doc: DocumentContent,
9+
) => {
10+
const relations: Record<string, Entity<AnyNoContext>> = {};
11+
for (const [fieldName, field] of Object.entries(type.fields)) {
12+
// TODO: this check is a hack atm, instead check if it is a class instead of specific name
13+
// TODO: what's the right way to get the name of the type?
14+
// @ts-expect-error name is defined
15+
if (field.name !== 'ArrayClass') continue;
16+
17+
const relationEntities: Array<Entity<AnyNoContext>> = [];
18+
19+
if (hasArrayField(entity, fieldName)) {
20+
for (const relationEntityId of entity[fieldName]) {
21+
const relationEntity = doc.entities?.[relationEntityId];
22+
if (
23+
!relationEntity ||
24+
typeof relationEntity !== 'object' ||
25+
!('@@types@@' in relationEntity) ||
26+
!Array.isArray(relationEntity['@@types@@'])
27+
)
28+
continue;
29+
30+
relationEntities.push({ ...relationEntity, id: relationEntityId });
31+
}
32+
}
33+
34+
relations[fieldName] = relationEntities;
35+
}
36+
37+
return relations;
38+
};

0 commit comments

Comments
 (0)