Skip to content

Commit

Permalink
alternative to simplify CollectFIelds for @defer @stream
Browse files Browse the repository at this point in the history
minimizes changes to CollectFields a la #3982 but still creates a single memoized incremental field plan per list item.
  • Loading branch information
yaacovCR committed Dec 8, 2023
1 parent 688ee2f commit 27fab86
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 302 deletions.
2 changes: 1 addition & 1 deletion src/execution/IncrementalPublisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
GraphQLFormattedError,
} from '../error/GraphQLError.js';

import type { GroupedFieldSet } from './collectFields.js';
import type { GroupedFieldSet } from './buildFieldPlan.js';

interface IncrementalUpdate<TData = unknown, TExtensions = ObjMap<unknown>> {
pending: ReadonlyArray<PendingResult>;
Expand Down
152 changes: 152 additions & 0 deletions src/execution/buildFieldPlan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { getBySet } from '../jsutils/getBySet.js';
import { isSameSet } from '../jsutils/isSameSet.js';

import type { FieldDetails } from './collectFields.js';

export interface DeferUsage {
label: string | undefined;
ancestors: ReadonlyArray<Target>;
}

export const NON_DEFERRED_TARGET_SET: TargetSet = new Set<Target>([undefined]);

export type Target = DeferUsage | undefined;
export type TargetSet = ReadonlySet<Target>;
export type DeferUsageSet = ReadonlySet<DeferUsage>;

export interface FieldGroup {
fields: ReadonlyArray<FieldDetails>;
targets?: TargetSet | undefined;
knownTargets?: TargetSet | undefined;
}

export type GroupedFieldSet = Map<string, FieldGroup>;

export interface NewGroupedFieldSetDetails {
groupedFieldSet: GroupedFieldSet;
shouldInitiateDefer: boolean;
}

export function buildFieldPlan(
fields: Map<string, ReadonlyArray<FieldDetails>>,
parentTargets = NON_DEFERRED_TARGET_SET,
knownTargets = NON_DEFERRED_TARGET_SET,
): {
groupedFieldSet: GroupedFieldSet;
newGroupedFieldSetDetailsMap: Map<DeferUsageSet, NewGroupedFieldSetDetails>;
newDeferUsages: ReadonlyArray<DeferUsage>;
} {
const newDeferUsages: Set<DeferUsage> = new Set<DeferUsage>();
const newKnownTargets = new Set<Target>(knownTargets);

const groupedFieldSet = new Map<
string,
{ fields: Array<FieldDetails>; targets: TargetSet; knownTargets: TargetSet }
>();

const newGroupedFieldSetDetailsMap = new Map<
DeferUsageSet,
{
groupedFieldSet: Map<
string,
{
fields: Array<FieldDetails>;
targets: TargetSet;
knownTargets: TargetSet;
}
>;
shouldInitiateDefer: boolean;
}
>();

const map = new Map<
string,
{ targetSet: TargetSet; fieldDetailsList: ReadonlyArray<FieldDetails> }
>();
for (const [responseKey, fieldDetailsList] of fields) {
const targetSet = new Set<Target>();
for (const fieldDetails of fieldDetailsList) {
const target = fieldDetails.deferUsage;
targetSet.add(target);
if (!knownTargets.has(target)) {
// all targets that are not known must be defined
newDeferUsages.add(target as DeferUsage);
}
newKnownTargets.add(target);
}
map.set(responseKey, { targetSet, fieldDetailsList });
}

for (const [responseKey, { targetSet, fieldDetailsList }] of map) {
const maskingTargetList: Array<Target> = [];
for (const target of targetSet) {
if (
target === undefined ||
target.ancestors.every((ancestor) => !targetSet.has(ancestor))
) {
maskingTargetList.push(target);
}
}

const maskingTargets: TargetSet = new Set<Target>(maskingTargetList);
if (isSameSet(maskingTargets, parentTargets)) {
let fieldGroup = groupedFieldSet.get(responseKey);
if (fieldGroup === undefined) {
fieldGroup = {
fields: [],
targets: maskingTargets,
knownTargets: newKnownTargets,
};
groupedFieldSet.set(responseKey, fieldGroup);
}
fieldGroup.fields.push(...fieldDetailsList);
continue;
}

let newGroupedFieldSetDetails = getBySet(
newGroupedFieldSetDetailsMap,
maskingTargets,
);
let newGroupedFieldSet;
if (newGroupedFieldSetDetails === undefined) {
newGroupedFieldSet = new Map<
string,
{
fields: Array<FieldDetails>;
targets: TargetSet;
knownTargets: TargetSet;
}
>();

newGroupedFieldSetDetails = {
groupedFieldSet: newGroupedFieldSet,
shouldInitiateDefer: maskingTargetList.some(
(deferUsage) => !parentTargets.has(deferUsage),
),
};
newGroupedFieldSetDetailsMap.set(
// all new grouped field sets must not contain the initial result as a target
maskingTargets as DeferUsageSet,
newGroupedFieldSetDetails,
);
} else {
newGroupedFieldSet = newGroupedFieldSetDetails.groupedFieldSet;
}
let fieldGroup = newGroupedFieldSet.get(responseKey);
if (fieldGroup === undefined) {
fieldGroup = {
fields: [],
targets: maskingTargets,
knownTargets: newKnownTargets,
};
newGroupedFieldSet.set(responseKey, fieldGroup);
}
fieldGroup.fields.push(...fieldDetailsList);
}

return {
groupedFieldSet,
newGroupedFieldSetDetailsMap,
newDeferUsages: Array.from(newDeferUsages),
};
}
Loading

0 comments on commit 27fab86

Please sign in to comment.