Skip to content

Commit 69923e1

Browse files
authored
add orderBy to Entities.findManyPublic and useEntities(mode: public) (#553)
1 parent 21bf0ba commit 69923e1

6 files changed

Lines changed: 256 additions & 14 deletions

File tree

.changeset/calm-games-trade.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@graphprotocol/hypergraph-react": patch
3+
"@graphprotocol/hypergraph": patch
4+
---
5+
6+
add orderBy to Entities.findManyPublic and useEntities(mode: 'public')
7+

apps/events/src/routes/podcasts.lazy.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ function RouteComponent() {
1616
include: {
1717
projects: {},
1818
},
19+
orderBy: { property: 'dateFounded', direction: 'asc' },
1920
});
2021
console.log({ data, isLoading, isError });
2122
return (
@@ -25,7 +26,9 @@ function RouteComponent() {
2526
{isError && <div>Error</div>}
2627
{data?.map((podcast) => (
2728
<div key={podcast.id}>
28-
<h2>{podcast.name}</h2>
29+
<h2>
30+
{podcast.dateFounded.toISOString()} {podcast.name}
31+
</h2>
2932
{podcast.projects.map((project) => (
3033
<div key={project._relation.id}>
3134
<h3>- {project.name}</h3>

packages/hypergraph-react/src/hooks/use-entities.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,25 @@ type UseEntitiesParams<S extends Schema.Schema.AnyNoContext> = {
1111
space?: string | undefined;
1212
first?: number | undefined;
1313
offset?: number | undefined;
14+
orderBy?:
15+
| {
16+
property: keyof Schema.Schema.Type<S>;
17+
direction: 'asc' | 'desc';
18+
}
19+
| undefined;
1420
};
1521

1622
export function useEntities<const S extends Schema.Schema.AnyNoContext>(type: S, params: UseEntitiesParams<S>) {
17-
const { mode, filter, include, space, first, offset } = params;
18-
const publicResult = useEntitiesPublic(type, { enabled: mode === 'public', filter, include, first, offset, space });
23+
const { mode, filter, include, space, first, offset, orderBy } = params;
24+
const publicResult = useEntitiesPublic(type, {
25+
enabled: mode === 'public',
26+
filter,
27+
include,
28+
first,
29+
offset,
30+
space,
31+
orderBy,
32+
});
1933
const localResult = useEntitiesPrivate(type, { enabled: mode === 'private', filter, include, space });
2034

2135
if (mode === 'public') {

packages/hypergraph-react/src/internal/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,10 @@ export type QueryPublicParams<S extends Schema.Schema.AnyNoContext> = {
99
space?: string | undefined;
1010
first?: number | undefined;
1111
offset?: number | undefined;
12+
orderBy?:
13+
| {
14+
property: keyof Schema.Schema.Type<S>;
15+
direction: 'asc' | 'desc';
16+
}
17+
| undefined;
1218
};

packages/hypergraph-react/src/internal/use-entities-public.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { QueryPublicParams } from './types.js';
77
import { useHypergraphSpaceInternal } from './use-hypergraph-space-internal.js';
88

99
export const useEntitiesPublic = <S extends Schema.Schema.AnyNoContext>(type: S, params?: QueryPublicParams<S>) => {
10-
const { enabled = true, filter, include, space: spaceFromParams, first = 100, offset } = params ?? {};
10+
const { enabled = true, filter, include, space: spaceFromParams, first = 100, offset, orderBy } = params ?? {};
1111
const { space: spaceFromContext } = useHypergraphSpaceInternal();
1212
const space = spaceFromParams ?? spaceFromContext;
1313

@@ -28,9 +28,10 @@ export const useEntitiesPublic = <S extends Schema.Schema.AnyNoContext>(type: S,
2828
filter,
2929
first,
3030
offset,
31+
orderBy,
3132
],
3233
queryFn: async () => {
33-
return Entity.findManyPublic(type, { filter, include, space, first, offset });
34+
return Entity.findManyPublic(type, { filter, include, space, first, offset, orderBy });
3435
},
3536
enabled,
3637
});

packages/hypergraph/src/entity/find-many-public.ts

Lines changed: 220 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export type FindManyPublicParams<S extends Schema.Schema.AnyNoContext> = {
1313
space: string;
1414
first?: number | undefined;
1515
offset?: number | undefined;
16+
orderBy?:
17+
| {
18+
property: keyof Schema.Schema.Type<S>;
19+
direction: 'asc' | 'desc';
20+
}
21+
| undefined;
1622
};
1723

1824
const entitiesQueryDocumentLevel0 = gql`
@@ -172,6 +178,168 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUI
172178
}
173179
`;
174180

181+
const entitiesOrderedByPropertyQueryDocumentLevel0 = gql`
182+
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!) {
183+
entities: entitiesOrderedByProperty(
184+
filter: { and: [{
185+
relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $typeIds}}},
186+
spaceIds: {in: [$spaceId]},
187+
}, $filter]}
188+
first: $first
189+
offset: $offset
190+
propertyId: $propertyId
191+
sortDirection: $sortDirection
192+
) {
193+
id
194+
name
195+
valuesList(filter: {spaceId: {is: $spaceId}}) {
196+
propertyId
197+
string
198+
boolean
199+
number
200+
time
201+
point
202+
}
203+
}
204+
}
205+
`;
206+
207+
const entitiesOrderedByPropertyQueryDocumentLevel1 = gql`
208+
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!) {
209+
entities: entitiesOrderedByProperty(
210+
first: $first
211+
filter: { and: [{
212+
relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $typeIds}}},
213+
spaceIds: {in: [$spaceId]},
214+
}, $filter]}
215+
offset: $offset
216+
propertyId: $propertyId
217+
sortDirection: $sortDirection
218+
) {
219+
id
220+
name
221+
valuesList(filter: {spaceId: {is: $spaceId}}) {
222+
propertyId
223+
string
224+
boolean
225+
number
226+
time
227+
point
228+
}
229+
relationsList(
230+
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel1}},
231+
) {
232+
id
233+
entity {
234+
valuesList(filter: {spaceId: {is: $spaceId}}) {
235+
propertyId
236+
string
237+
boolean
238+
number
239+
time
240+
point
241+
}
242+
}
243+
toEntity {
244+
id
245+
name
246+
valuesList(filter: {spaceId: {is: $spaceId}}) {
247+
propertyId
248+
string
249+
boolean
250+
number
251+
time
252+
point
253+
}
254+
}
255+
typeId
256+
}
257+
}
258+
}
259+
`;
260+
261+
const entitiesOrderedByPropertyQueryDocumentLevel2 = gql`
262+
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $relationTypeIdsLevel2: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!) {
263+
entities: entitiesOrderedByProperty(
264+
first: $first
265+
filter: { and: [{
266+
relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $typeIds}}},
267+
spaceIds: {in: [$spaceId]},
268+
}, $filter]}
269+
offset: $offset
270+
propertyId: $propertyId
271+
sortDirection: $sortDirection
272+
) {
273+
id
274+
name
275+
valuesList(filter: {spaceId: {is: $spaceId}}) {
276+
propertyId
277+
string
278+
boolean
279+
number
280+
time
281+
point
282+
}
283+
relationsList(
284+
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel1}},
285+
) {
286+
id
287+
entity {
288+
valuesList(filter: {spaceId: {is: $spaceId}}) {
289+
propertyId
290+
string
291+
boolean
292+
number
293+
time
294+
point
295+
}
296+
}
297+
toEntity {
298+
id
299+
name
300+
valuesList(filter: {spaceId: {is: $spaceId}}) {
301+
propertyId
302+
string
303+
boolean
304+
number
305+
time
306+
point
307+
}
308+
relationsList(
309+
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel2}},
310+
) {
311+
id
312+
entity {
313+
valuesList(filter: {spaceId: {is: $spaceId}}) {
314+
propertyId
315+
string
316+
boolean
317+
number
318+
time
319+
point
320+
}
321+
}
322+
toEntity {
323+
id
324+
name
325+
valuesList(filter: {spaceId: {is: $spaceId}}) {
326+
propertyId
327+
string
328+
boolean
329+
number
330+
time
331+
point
332+
}
333+
}
334+
typeId
335+
}
336+
}
337+
typeId
338+
}
339+
}
340+
}
341+
`;
342+
175343
type EntityQueryResult = {
176344
entities: {
177345
id: string;
@@ -239,6 +407,8 @@ type EntityQueryResult = {
239407
}[];
240408
};
241409

410+
type GraphSortDirection = 'ASC' | 'DESC';
411+
242412
export const parseResult = <S extends Schema.Schema.AnyNoContext>(queryData: EntityQueryResult, type: S) => {
243413
const schemaWithId = Utils.addIdSchemaField(type);
244414
const decode = Schema.decodeUnknownEither(schemaWithId);
@@ -296,7 +466,7 @@ export const findManyPublic = async <S extends Schema.Schema.AnyNoContext>(
296466
type: S,
297467
params?: FindManyPublicParams<S>,
298468
) => {
299-
const { filter, include, space, first = 100, offset = 0 } = params ?? {};
469+
const { filter, include, space, first = 100, offset = 0, orderBy } = params ?? {};
300470

301471
// constructing the relation type ids for the query
302472
const relationTypeIds = Utils.getRelationTypeIds(type, include);
@@ -305,25 +475,66 @@ export const findManyPublic = async <S extends Schema.Schema.AnyNoContext>(
305475
Option.getOrElse(() => []),
306476
);
307477

308-
let queryDocument = entitiesQueryDocumentLevel0;
309-
if (relationTypeIds.level1.length > 0) {
310-
queryDocument = entitiesQueryDocumentLevel1;
311-
}
312-
if (relationTypeIds.level2.length > 0) {
313-
queryDocument = entitiesQueryDocumentLevel2;
478+
const relationLevel = relationTypeIds.level2.length > 0 ? 2 : relationTypeIds.level1.length > 0 ? 1 : 0;
479+
480+
let orderByPropertyId: string | undefined;
481+
let sortDirection: GraphSortDirection | undefined;
482+
483+
if (orderBy) {
484+
const ast = type.ast as SchemaAST.TypeLiteral;
485+
const propertySignature = ast.propertySignatures.find((prop) => String(prop.name) === String(orderBy.property));
486+
487+
if (!propertySignature) {
488+
throw new Error(`Cannot order by unknown property "${String(orderBy.property)}"`);
489+
}
490+
491+
const propertyType =
492+
propertySignature.isOptional && SchemaAST.isUnion(propertySignature.type)
493+
? (propertySignature.type.types.find((member) => !SchemaAST.isUndefinedKeyword(member)) ??
494+
propertySignature.type)
495+
: propertySignature.type;
496+
497+
const propertyIdAnnotation = SchemaAST.getAnnotation<string>(Constants.PropertyIdSymbol)(propertyType);
498+
499+
if (Option.isNone(propertyIdAnnotation)) {
500+
throw new Error(`Property "${String(orderBy.property)}" is missing a propertyId annotation`);
501+
}
502+
503+
orderByPropertyId = propertyIdAnnotation.value;
504+
sortDirection = orderBy.direction === 'asc' ? 'ASC' : 'DESC';
314505
}
315506

507+
const queryDocument =
508+
relationLevel === 2
509+
? orderBy
510+
? entitiesOrderedByPropertyQueryDocumentLevel2
511+
: entitiesQueryDocumentLevel2
512+
: relationLevel === 1
513+
? orderBy
514+
? entitiesOrderedByPropertyQueryDocumentLevel1
515+
: entitiesQueryDocumentLevel1
516+
: orderBy
517+
? entitiesOrderedByPropertyQueryDocumentLevel0
518+
: entitiesQueryDocumentLevel0;
519+
316520
const filterParams = filter ? Utils.translateFilterToGraphql(filter, type) : {};
317521

318-
const result = await request<EntityQueryResult>(`${Graph.TESTNET_API_ORIGIN}/graphql`, queryDocument, {
522+
const queryVariables: Record<string, unknown> = {
319523
spaceId: space,
320524
typeIds,
321525
relationTypeIdsLevel1: relationTypeIds.level1,
322526
relationTypeIdsLevel2: relationTypeIds.level2,
323527
first,
324528
filter: filterParams,
325529
offset,
326-
});
530+
};
531+
532+
if (orderByPropertyId && sortDirection) {
533+
queryVariables.propertyId = orderByPropertyId;
534+
queryVariables.sortDirection = sortDirection;
535+
}
536+
537+
const result = await request<EntityQueryResult>(`${Graph.TESTNET_API_ORIGIN}/graphql`, queryDocument, queryVariables);
327538

328539
const { data, invalidEntities } = parseResult(result, type);
329540
return { data, invalidEntities };

0 commit comments

Comments
 (0)