Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changeset/kind-peas-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@graphprotocol/hypergraph-react": patch
"@graphprotocol/hypergraph": patch
---

add temporary backlinksTotalCountsTypeId1 to Entity.findManyPublic and useEntities

3 changes: 2 additions & 1 deletion apps/events/src/routes/podcasts.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function RouteComponent() {
projects: {},
},
orderBy: { property: 'dateFounded', direction: 'asc' },
backlinksTotalCountsTypeId1: '972d201a-d780-4568-9e01-543f67b26bee',
});
console.log({ data, isLoading, isError });
return (
Expand All @@ -27,7 +28,7 @@ function RouteComponent() {
{data?.map((podcast) => (
<div key={podcast.id}>
<h2>
{podcast.dateFounded.toISOString()} {podcast.name}
{podcast.backlinksTotalCountsTypeId1} - {podcast.dateFounded.toISOString()} {podcast.name}
</h2>
{podcast.projects.map((project) => (
<div key={project._relation.id}>
Expand Down
6 changes: 4 additions & 2 deletions packages/hypergraph-react/src/hooks/use-entities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ type UseEntitiesParams<S extends Schema.Schema.AnyNoContext> = {
direction: 'asc' | 'desc';
}
| undefined;
backlinksTotalCountsTypeId1?: string | undefined;
};

export function useEntities<const S extends Schema.Schema.AnyNoContext>(type: S, params: UseEntitiesParams<S>) {
const { mode, filter, include, space, first, offset, orderBy } = params;
const { mode, filter, include, space, first, offset, orderBy, backlinksTotalCountsTypeId1 } = params;
const publicResult = useEntitiesPublic(type, {
enabled: mode === 'public',
filter,
Expand All @@ -29,6 +30,7 @@ export function useEntities<const S extends Schema.Schema.AnyNoContext>(type: S,
offset,
space,
orderBy,
backlinksTotalCountsTypeId1,
});
const localResult = useEntitiesPrivate(type, { enabled: mode === 'private', filter, include, space });

Expand All @@ -41,7 +43,7 @@ export function useEntities<const S extends Schema.Schema.AnyNoContext>(type: S,

return {
...publicResult,
data: localResult.entities,
data: localResult.entities as (Entity.Entity<S> & { backlinksTotalCountsTypeId1?: number })[],

Copilot AI Nov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type cast assumes local (private mode) entities will have backlinksTotalCountsTypeId1, but this field is only populated in public mode queries via findManyPublic. Local entities retrieved through useEntitiesPrivate won't have this field, making this cast potentially misleading. The field should be marked as optional and only expected when mode === 'public'.

Copilot uses AI. Check for mistakes.
deleted: localResult.deletedEntities,
};
}
1 change: 1 addition & 0 deletions packages/hypergraph-react/src/internal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export type QueryPublicParams<S extends Schema.Schema.AnyNoContext> = {
direction: 'asc' | 'desc';
}
| undefined;
backlinksTotalCountsTypeId1?: string | undefined;
};
22 changes: 20 additions & 2 deletions packages/hypergraph-react/src/internal/use-entities-public.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ import type { QueryPublicParams } from './types.js';
import { useHypergraphSpaceInternal } from './use-hypergraph-space-internal.js';

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

Expand All @@ -29,9 +38,18 @@ export const useEntitiesPublic = <S extends Schema.Schema.AnyNoContext>(type: S,
first,
offset,
orderBy,
backlinksTotalCountsTypeId1,
],
queryFn: async () => {
return Entity.findManyPublic(type, { filter, include, space, first, offset, orderBy });
return Entity.findManyPublic(type, {
filter,
include,
space,
first,
offset,
orderBy,
backlinksTotalCountsTypeId1,
});
},
enabled,
});
Expand Down
57 changes: 48 additions & 9 deletions packages/hypergraph/src/entity/find-many-public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ export type FindManyPublicParams<S extends Schema.Schema.AnyNoContext> = {
direction: 'asc' | 'desc';
}
| undefined;
backlinksTotalCountsTypeId1?: string | undefined;
};

const entitiesQueryDocumentLevel0 = gql`
query entities($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int) {
query entities($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $backlinksTotalCountsTypeId1: UUID, $backlinksTotalCountsTypeId1Present: Boolean!) {
entities(
filter: { and: [{
relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $typeIds}}},
Expand All @@ -41,12 +42,15 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int, $filter: Entity
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
}
}
`;

const entitiesQueryDocumentLevel1 = gql`
query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int) {
query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $backlinksTotalCountsTypeId1: UUID, $backlinksTotalCountsTypeId1Present: Boolean!) {
entities(
first: $first
filter: { and: [{
Expand All @@ -65,6 +69,9 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUI
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
relationsList(
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel1}},
) {
Expand Down Expand Up @@ -98,7 +105,7 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUI
`;

const entitiesQueryDocumentLevel2 = gql`
query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $relationTypeIdsLevel2: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int) {
query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $relationTypeIdsLevel2: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $backlinksTotalCountsTypeId1: UUID, $backlinksTotalCountsTypeId1Present: Boolean!) {
entities(
first: $first
filter: { and: [{
Expand All @@ -117,6 +124,9 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUI
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
relationsList(
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel1}},
) {
Expand All @@ -142,6 +152,9 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUI
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
relationsList(
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel2}},
# filter: {spaceId: {is: $spaceId}, toEntity: {relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $relationTypeIdsLevel2}}}}}
Expand Down Expand Up @@ -179,7 +192,7 @@ query entities($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUI
`;

const entitiesOrderedByPropertyQueryDocumentLevel0 = gql`
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!) {
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!, $backlinksTotalCountsTypeId1: UUID, $backlinksTotalCountsTypeId1Present: Boolean!) {
entities: entitiesOrderedByProperty(
filter: { and: [{
relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $typeIds}}},
Expand All @@ -200,12 +213,15 @@ query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $first: Int
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
}
}
`;

const entitiesOrderedByPropertyQueryDocumentLevel1 = gql`
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!) {
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!, $backlinksTotalCountsTypeId1: UUID, $backlinksTotalCountsTypeId1Present: Boolean!) {
entities: entitiesOrderedByProperty(
first: $first
filter: { and: [{
Expand All @@ -226,6 +242,9 @@ query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTy
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
relationsList(
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel1}},
) {
Expand Down Expand Up @@ -259,7 +278,7 @@ query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTy
`;

const entitiesOrderedByPropertyQueryDocumentLevel2 = gql`
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $relationTypeIdsLevel2: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!) {
query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTypeIdsLevel1: [UUID!]!, $relationTypeIdsLevel2: [UUID!]!, $first: Int, $filter: EntityFilter!, $offset: Int, $propertyId: UUID!, $sortDirection: SortOrder!, $backlinksTotalCountsTypeId1: UUID, $backlinksTotalCountsTypeId1Present: Boolean!) {
entities: entitiesOrderedByProperty(
first: $first
filter: { and: [{
Expand All @@ -280,6 +299,9 @@ query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTy
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
relationsList(
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel1}},
) {
Expand All @@ -305,6 +327,9 @@ query entitiesOrderedByProperty($spaceId: UUID!, $typeIds: [UUID!]!, $relationTy
time
point
}
backlinksTotalCountsTypeId1: backlinks(filter: { spaceId: {is: $spaceId}, fromEntity: { typeIds: { is: [$backlinksTotalCountsTypeId1] } }}) @include(if: $backlinksTotalCountsTypeId1Present) {
totalCount
}
relationsList(
filter: {spaceId: {is: $spaceId}, typeId:{ in: $relationTypeIdsLevel2}},
) {
Expand Down Expand Up @@ -352,6 +377,9 @@ type EntityQueryResult = {
time: string;
point: string;
}[];
backlinksTotalCountsTypeId1: {
totalCount: number;
} | null;
relationsList: {
id: string;
entity: {
Expand Down Expand Up @@ -412,7 +440,7 @@ type GraphSortDirection = 'ASC' | 'DESC';
export const parseResult = <S extends Schema.Schema.AnyNoContext>(queryData: EntityQueryResult, type: S) => {
const schemaWithId = Utils.addIdSchemaField(type);
const decode = Schema.decodeUnknownEither(schemaWithId);
const data: Entity.Entity<S>[] = [];
const data: (Entity.Entity<S> & { backlinksTotalCountsTypeId1?: number })[] = [];

Copilot AI Nov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The return type intersection Entity.Entity<S> & { backlinksTotalCountsTypeId1?: number } is duplicated in multiple places (lines 443 and 46 in use-entities.tsx). Consider defining a reusable type alias like type EntityWithBacklinkCount<S> = Entity.Entity<S> & { backlinksTotalCountsTypeId1?: number } to improve maintainability.

Copilot uses AI. Check for mistakes.
const invalidEntities: Record<string, unknown>[] = [];

for (const queryEntity of queryData.entities) {
Expand Down Expand Up @@ -454,7 +482,11 @@ export const parseResult = <S extends Schema.Schema.AnyNoContext>(queryData: Ent

if (Either.isRight(decodeResult)) {
// injecting the schema to the entity to be able to access it in the preparePublish function
data.push({ ...decodeResult.right, __schema: type });
data.push({
...decodeResult.right,
__schema: type,
backlinksTotalCountsTypeId1: queryEntity.backlinksTotalCountsTypeId1?.totalCount,
});
} else {
invalidEntities.push(rawEntity);
}
Expand All @@ -466,7 +498,7 @@ export const findManyPublic = async <S extends Schema.Schema.AnyNoContext>(
type: S,
params?: FindManyPublicParams<S>,
) => {
const { filter, include, space, first = 100, offset = 0, orderBy } = params ?? {};
const { filter, include, space, first = 100, offset = 0, orderBy, backlinksTotalCountsTypeId1 } = params ?? {};

// constructing the relation type ids for the query
const relationTypeIds = Utils.getRelationTypeIds(type, include);
Expand Down Expand Up @@ -534,6 +566,13 @@ export const findManyPublic = async <S extends Schema.Schema.AnyNoContext>(
queryVariables.sortDirection = sortDirection;
}

if (backlinksTotalCountsTypeId1) {
queryVariables.backlinksTotalCountsTypeId1 = backlinksTotalCountsTypeId1;
queryVariables.backlinksTotalCountsTypeId1Present = true;
} else {
queryVariables.backlinksTotalCountsTypeId1Present = false;
}

const result = await request<EntityQueryResult>(`${Graph.TESTNET_API_ORIGIN}/graphql`, queryDocument, queryVariables);

const { data, invalidEntities } = parseResult(result, type);
Expand Down
1 change: 1 addition & 0 deletions packages/hypergraph/src/entity/search-many-public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ export const searchManyPublic = async <S extends Schema.Schema.AnyNoContext>(
offset,
});

// @ts-expect-error TODO: fix this
const { data, invalidEntities } = parseResult({ entities: result.search }, type);
Comment on lines +282 to 283

Copilot AI Nov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parseResult function expects entities with a backlinksTotalCountsTypeId1 field in EntityQueryResult, but SearchQueryResult doesn't include this field. This causes a type error that's being suppressed. To fix this, either add backlinksTotalCountsTypeId1 support to the search queries (similar to find-many-public.ts lines 45-47, 72-74, etc.) or create a separate parse function for search results that doesn't expect this field.

Copilot uses AI. Check for mistakes.
return { data, invalidEntities };
};
Loading