Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,53 @@ describe('translateFilterToGraphql with complex nested filters', () => {
});
});
});

describe('translateFilterToGraphql date/point filters', () => {
class Event extends Entity.Class<Event>('Event')({
name: Type.String,
when: Type.Date,
location: Type.Point,
}) {}

const eventMapping: Mapping.Mapping = {
Event: {
typeIds: [Id('a288444f-06a3-4037-9ace-66fe325864d0')],
properties: {
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
when: Id('9b53690f-ea6d-4bd8-b4d3-9ea01e7f837f'),
location: Id('45e707a5-4364-42fb-bb0b-927a5a8bc061'),
},
},
};

it('should translate Date `is` filter correctly', () => {
const dt = new Date('2024-01-01T00:00:00Z');
const filter: Entity.EntityFilter<Event> = { when: { is: dt } };

const result = translateFilterToGraphql(filter, Event, eventMapping);

expect(result).toEqual({
values: {
some: {
propertyId: { is: '9b53690f-ea6d-4bd8-b4d3-9ea01e7f837f' },
time: { is: Graph.serializeDate(dt) },
},
},
});
});

it('should translate Point `is` filter correctly', () => {
const filter: Entity.EntityFilter<Event> = { location: { is: [1, 2] as const } };

const result = translateFilterToGraphql(filter, Event, eventMapping);

expect(result).toEqual({
values: {
some: {
propertyId: { is: '45e707a5-4364-42fb-bb0b-927a5a8bc061' },
point: { is: Graph.serializePoint([1, 2]) },
},
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ type GraphqlFilterEntry =
| {
propertyId: { is: string };
number: { is: string } | { greaterThan: string } | { lessThan: string };
}
| {
propertyId: { is: string };
time: { is: string };
}
| {
propertyId: { is: string };
point: { is: string };
};
};
}
Expand Down Expand Up @@ -125,6 +133,32 @@ export function translateFilterToGraphql<S extends Entity.AnyNoContext>(
},
});
}

if (TypeUtils.isDateOrOptionalDateType(type.fields[fieldName]) && fieldFilter.is instanceof Date) {
graphqlFilter.push({
values: {
some: {
propertyId: { is: propertyId },
time: { is: Graph.serializeDate(fieldFilter.is) },
},
},
});
}

if (
TypeUtils.isPointOrOptionalPointType(type.fields[fieldName]) &&
Array.isArray(fieldFilter.is) &&
fieldFilter.is.length === 2
) {
graphqlFilter.push({
values: {
some: {
propertyId: { is: propertyId },
point: { is: Graph.serializePoint([...(fieldFilter.is as ReadonlyArray<number>)]) },
},
},
});
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions packages/hypergraph/src/entity/findMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,19 @@ export function findMany<const S extends AnyNoContext>(
}
}
}
if (fieldValue instanceof Date && 'is' in fieldFilter) {
const target = (fieldFilter as { is?: Date }).is;
if (target instanceof Date) {
return fieldValue.getTime() === target.getTime();
}
}

if (Array.isArray(fieldValue) && fieldValue.length === 2 && 'is' in fieldFilter) {
const target = (fieldFilter as { is?: ReadonlyArray<number> }).is;
if (Array.isArray(target) && target.length === 2) {
return fieldValue[0] === target[0] && fieldValue[1] === target[1];
}
}
return true;
};

Expand Down
10 changes: 9 additions & 1 deletion packages/hypergraph/src/entity/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ export type EntityFieldFilter<T> = {
endsWith?: string;
contains?: string;
}
: Record<string, never>);
: T extends Date
? {
is?: Date;
}
: T extends ReadonlyArray<number>
? {
is?: ReadonlyArray<number>;
}
: Record<string, never>);

export type EntityFilter<T> = CrossFieldFilter<T>;
37 changes: 37 additions & 0 deletions packages/hypergraph/test/entity/findMany.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,40 @@ describe('findMany with filters', () => {
});
});
});

describe('Date & Point filters', () => {
class Meetup extends Entity.Class<Meetup>('Meetup')({
name: Type.String,
when: Type.Date,
location: Type.Point,
}) {}

const spaceId = '9a4a5a1b-2c3d-4e5f-8a9b-1234567890ab';
const automergeDocId = idToAutomergeId(spaceId) as AnyDocumentId;

let repo: Repo;
let handle: DocHandle<Entity.DocumentContent>;

beforeEach(() => {
repo = new Repo({});
const result = repo.findWithProgress<Entity.DocumentContent>(automergeDocId);
handle = result.handle;
handle.doneLoading();
});

it('filters by Date is ', () => {
Entity.create(handle, Meetup)({ name: 'A', when: new Date('2024-01-01T00:00:00Z'), location: [0, 0] });
Entity.create(handle, Meetup)({ name: 'B', when: new Date('2025-01-01T00:00:00Z'), location: [0, 0] });

const r = Entity.findMany(handle, Meetup, { when: { is: new Date('2024-01-01T00:00:00Z') } }, undefined);
expect(r.entities.map((e) => e.name)).toEqual(['A']);
});

it('filters by Point is ', () => {
Entity.create(handle, Meetup)({ name: 'X', when: new Date('2024-01-01T00:00:00Z'), location: [1, 2] });
Entity.create(handle, Meetup)({ name: 'Y', when: new Date('2024-01-01T00:00:00Z'), location: [3, 4] });

const r = Entity.findMany(handle, Meetup, { location: { is: [1, 2] } }, undefined);
expect(r.entities.map((e) => e.name)).toEqual(['X']);
});
});
Loading