Skip to content

Commit 34e845f

Browse files
committed
fix: pass dataType for orderBy entity queries
Ordered entity queries started returning empty results on testnet because entitiesOrderedByProperty now requires a dataType argument. The SDK was forwarding propertyId and sortDirection but not dataType, so orderBy calls silently returned no rows. This infers the GraphQL dataType from the schema property metadata, passes it with orderBy queries, and throws a clear error when an unsupported field is used for ordering. It also adds coverage for property-type to dataType mapping.
1 parent 4194dba commit 34e845f

3 files changed

Lines changed: 95 additions & 2 deletions

File tree

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const buildEntitiesQuery = (
5050
: undefined,
5151
'$typeIds: [UUID!]!',
5252
useOrderBy ? '$propertyId: UUID!' : undefined,
53+
useOrderBy ? '$dataType: String!' : undefined,
5354
useOrderBy ? '$sortDirection: SortOrder!' : undefined,
5455
'$first: Int',
5556
'$filter: EntityFilter!',
@@ -68,7 +69,7 @@ const buildEntitiesQuery = (
6869
// entitiesOrderedByProperty doesn't support the native typeIds filter yet,
6970
// so we fall back to the relation-based filter for orderBy queries
7071
if (useOrderBy) {
71-
const orderByArgs = 'propertyId: $propertyId\n sortDirection: $sortDirection\n ';
72+
const orderByArgs = 'propertyId: $propertyId\n dataType: $dataType\n sortDirection: $sortDirection\n ';
7273
const entitySpaceFilter =
7374
spaceSelection.mode === 'single'
7475
? 'spaceIds: {in: [$spaceId]},'
@@ -282,6 +283,7 @@ export const findManyPublic = async <
282283
);
283284

284285
let orderByPropertyId: string | undefined;
286+
let orderByDataType: Utils.OrderByDataType | undefined;
285287
let sortDirection: GraphSortDirection | undefined;
286288

287289
if (orderBy) {
@@ -304,6 +306,11 @@ export const findManyPublic = async <
304306
throw new Error(`Property "${String(orderBy.property)}" is missing a propertyId annotation`);
305307
}
306308

309+
orderByDataType = Utils.getOrderByDataType(propertyType);
310+
if (!orderByDataType) {
311+
throw new Error(`Property "${String(orderBy.property)}" cannot be used in orderBy`);
312+
}
313+
307314
orderByPropertyId = propertyIdAnnotation.value;
308315
sortDirection = orderBy.direction === 'asc' ? 'ASC' : 'DESC';
309316
}
@@ -329,8 +336,9 @@ export const findManyPublic = async <
329336
queryVariables.spaceIds = spaceSelection.spaceIds;
330337
}
331338

332-
if (orderByPropertyId && sortDirection) {
339+
if (orderByPropertyId && orderByDataType && sortDirection) {
333340
queryVariables.propertyId = orderByPropertyId;
341+
queryVariables.dataType = orderByDataType;
334342
queryVariables.sortDirection = sortDirection;
335343
}
336344

packages/hypergraph/src/utils/convert-property-value.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,37 @@ import { Constants } from '@graphprotocol/hypergraph';
22
import * as Option from 'effect/Option';
33
import * as SchemaAST from 'effect/SchemaAST';
44

5+
export type OrderByDataType = 'text' | 'boolean' | 'float' | 'datetime' | 'point' | 'schedule';
6+
7+
const ORDER_BY_DATA_TYPE_BY_PROPERTY_TYPE: Record<string, OrderByDataType | undefined> = {
8+
string: 'text',
9+
boolean: 'boolean',
10+
number: 'float',
11+
date: 'datetime',
12+
point: 'point',
13+
schedule: 'schedule',
14+
relation: undefined,
15+
};
16+
17+
export const getOrderByDataType = (type: SchemaAST.AST): OrderByDataType | undefined => {
18+
const propertyType = SchemaAST.getAnnotation<string>(Constants.PropertyTypeSymbol)(type);
19+
if (Option.isSome(propertyType)) {
20+
return ORDER_BY_DATA_TYPE_BY_PROPERTY_TYPE[propertyType.value];
21+
}
22+
23+
if (SchemaAST.isStringKeyword(type)) {
24+
return 'text';
25+
}
26+
if (SchemaAST.isBooleanKeyword(type)) {
27+
return 'boolean';
28+
}
29+
if (SchemaAST.isNumberKeyword(type)) {
30+
return 'float';
31+
}
32+
33+
return undefined;
34+
};
35+
536
export const convertPropertyValue = (
637
property: {
738
propertyId: string;

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ import { describe, expect, it } from 'vitest';
33
import { parseResult } from '../../src/entity/find-many-public.js';
44
import * as Entity from '../../src/entity/index.js';
55
import * as Type from '../../src/type/type.js';
6+
import { getOrderByDataType } from '../../src/utils/convert-property-value.js';
67
import { getRelationTypeIds } from '../../src/utils/get-relation-type-ids.js';
78
import { getRelationAlias } from '../../src/utils/relation-query-helpers.js';
89

910
const TITLE_PROPERTY_ID = Id('79c1a9510074401087d07501ef9d7b3d');
1011
const CHILDREN_RELATION_PROPERTY_ID = Id('ca7c7167250249c490b084c147f9b12b');
1112
const CHILD_NAME_PROPERTY_ID = Id('25584af039414ab986f7a603305b19bb');
13+
const SCORE_PROPERTY_ID = Id('0f0f62df02194f16983ad2ae5fc43ee5');
14+
const IS_ACTIVE_PROPERTY_ID = Id('774f4b5dbfaf4af5925ef4c7ef2ebd76');
15+
const PUBLISHED_AT_PROPERTY_ID = Id('2ece4d97ea964a269f3fee0d0f00de53');
16+
const LOCATION_PROPERTY_ID = Id('2df8bd4f7bc34aafaa8db20e3ad41657');
17+
const CADENCE_PROPERTY_ID = Id('0f7952a1f8474b4286d0ef7e6ef8dbb2');
1218

1319
const Child = Entity.Schema(
1420
{
@@ -36,6 +42,44 @@ const Parent = Entity.Schema(
3642
},
3743
);
3844

45+
const OrderableParent = Entity.Schema(
46+
{
47+
title: Type.String,
48+
score: Type.Number,
49+
isActive: Type.Boolean,
50+
publishedAt: Type.Date,
51+
location: Type.Point,
52+
cadence: Type.ScheduleString,
53+
children: Type.Relation(Child),
54+
},
55+
{
56+
types: [Id('af571d8c06d44add8cfa4c6b50412254')],
57+
properties: {
58+
title: TITLE_PROPERTY_ID,
59+
score: SCORE_PROPERTY_ID,
60+
isActive: IS_ACTIVE_PROPERTY_ID,
61+
publishedAt: PUBLISHED_AT_PROPERTY_ID,
62+
location: LOCATION_PROPERTY_ID,
63+
cadence: CADENCE_PROPERTY_ID,
64+
children: CHILDREN_RELATION_PROPERTY_ID,
65+
},
66+
},
67+
);
68+
69+
const getPropertyTypeAst = (property: string) => {
70+
const ast = OrderableParent.ast;
71+
if (!('propertySignatures' in ast)) {
72+
throw new Error('Expected schema AST to be a TypeLiteral');
73+
}
74+
75+
const signature = ast.propertySignatures.find((prop) => String(prop.name) === property);
76+
if (!signature) {
77+
throw new Error(`Property ${property} not found in schema`);
78+
}
79+
80+
return signature.type;
81+
};
82+
3983
const buildValueEntry = (
4084
propertyId: string,
4185
value: Partial<{
@@ -57,6 +101,16 @@ const buildValueEntry = (
57101
});
58102

59103
describe('findManyPublic parseResult', () => {
104+
it('maps schema property types to orderBy data types', () => {
105+
expect(getOrderByDataType(getPropertyTypeAst('title'))).toBe('text');
106+
expect(getOrderByDataType(getPropertyTypeAst('score'))).toBe('float');
107+
expect(getOrderByDataType(getPropertyTypeAst('isActive'))).toBe('boolean');
108+
expect(getOrderByDataType(getPropertyTypeAst('publishedAt'))).toBe('datetime');
109+
expect(getOrderByDataType(getPropertyTypeAst('location'))).toBe('point');
110+
expect(getOrderByDataType(getPropertyTypeAst('cadence'))).toBe('schedule');
111+
expect(getOrderByDataType(getPropertyTypeAst('children'))).toBeUndefined();
112+
});
113+
60114
it('collects invalidEntities when decoding fails', () => {
61115
const queryData = {
62116
entities: [

0 commit comments

Comments
 (0)