Skip to content

Commit

Permalink
Merge pull request #5937 from MacondoExpress/deprecate-aggregation-fi…
Browse files Browse the repository at this point in the history
…elds-for-id

Add deprecations for aggregation ID fields
  • Loading branch information
MacondoExpress authored Jan 13, 2025
2 parents 9908bc9 + 290210b commit fd3f015
Show file tree
Hide file tree
Showing 63 changed files with 1,375 additions and 781 deletions.
13 changes: 13 additions & 0 deletions .changeset/beige-beds-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@neo4j/graphql": patch
---

Aggregations on ID fields are now deprecated.
As part of the change, the flag `idAggregations` has been added to the `excludeDeprecatedFields` setting.

```js
const neoSchema = new Neo4jGraphQL({
typeDefs,
features: { excludeDeprecatedFields: { idAggregations: true } },
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -192,17 +192,24 @@ export class AttributeAdapter {

isAggregableField(): boolean {
return (
!this.typeHelper.isList() && (this.typeHelper.isScalar() || this.typeHelper.isEnum()) && this.isAggregable()
!this.typeHelper.isList() &&
//uncomment me on 7.x !this.typeHelper.isID() &&
(this.typeHelper.isScalar() || this.typeHelper.isEnum()) &&
this.isAggregable()
);
}

isAggregationWhereField(): boolean {
const isGraphQLBuiltInScalarWithoutBoolean =
this.typeHelper.isGraphQLBuiltInScalar() && !this.typeHelper.isBoolean();
const isTemporalWithoutDate = this.typeHelper.isTemporal() && !this.typeHelper.isDate();
if (
this.typeHelper.isList() ||
// uncomment me on 7.x this.typeHelper.isID() ||
this.typeHelper.isBoolean() ||
this.typeHelper.isDate()
) {
return false;
}
return (
!this.typeHelper.isList() &&
(isGraphQLBuiltInScalarWithoutBoolean || isTemporalWithoutDate || this.typeHelper.isBigInt()) &&
(this.typeHelper.isGraphQLBuiltInScalar() || this.typeHelper.isTemporal() || this.typeHelper.isBigInt()) &&
this.isAggregationFilterable()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class AggregationTypesMapper {
resolve: numericalResolver,
args: {},
};

// TODO: REMOVE ID FIELD ON 7.x
const composeId = {
type: "ID",
resolve: idResolver,
Expand All @@ -64,6 +64,7 @@ export class AggregationTypesMapper {
fields?: Record<string, any>;
directives?: string[];
}> = [
// TODO: REMOVE ID FIELD ON 7.x
{
name: "ID",
fields: {
Expand Down Expand Up @@ -140,6 +141,7 @@ export class AggregationTypesMapper {
}): ObjectTypeComposer<any, any> {
return composer.getOrCreateOTC(`${name}AggregateSelection`, (tc) => {
tc.addFields(fields ?? { min: name, max: name });

for (const directive of directives) {
tc.setDirectiveByName(directive);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
*/

import { GraphQLInt, GraphQLNonNull } from "graphql";
import type { ObjectTypeComposer, SchemaComposer } from "graphql-compose";
import type { ObjectTypeComposer, ObjectTypeComposerFieldConfigMapDefinition, SchemaComposer } from "graphql-compose";
import type { Subgraph } from "../../classes/Subgraph";
import type { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter";
import type { InterfaceEntityAdapter } from "../../schema-model/entity/model-adapters/InterfaceEntityAdapter";
import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter";
import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter";
import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter";
import type { Neo4jFeaturesSettings } from "../../types";
import { DEPRECATE_ID_AGGREGATION } from "../constants";
import { shouldAddDeprecatedFields } from "../generation/utils";
import { numericalResolver } from "../resolvers/field/numerical";
import { AggregationTypesMapper } from "./aggregation-types-mapper";

Expand All @@ -39,29 +42,28 @@ export class FieldAggregationComposer {

private createAggregationField(
name: string,
fields: Record<string, ObjectTypeComposer>
fields: ObjectTypeComposerFieldConfigMapDefinition<any, any>
): ObjectTypeComposer | undefined {
if (Object.keys(fields).length > 0) {
return this.composer.createObjectTC({
name,
fields: {
...fields,
},
fields,
});
}
return undefined;
}

public createAggregationTypeObject(
relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter
relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter,
features: Neo4jFeaturesSettings | undefined
): ObjectTypeComposer {
let aggregateSelectionEdge: ObjectTypeComposer | undefined;

if (relationshipAdapter.target instanceof UnionEntityAdapter) {
throw new Error("UnionEntityAdapter not implemented");
}

const aggregateSelectionNodeFields = this.getAggregationFields(relationshipAdapter.target);
const aggregateSelectionNodeFields = this.getAggregationFields(relationshipAdapter.target, features);
const aggregateSelectionNodeName = relationshipAdapter.operations.getAggregationFieldTypename("node");

const aggregateSelectionNode = this.createAggregationField(
Expand All @@ -70,7 +72,7 @@ export class FieldAggregationComposer {
);

if (relationshipAdapter instanceof RelationshipAdapter && relationshipAdapter.attributes.size > 0) {
const aggregateSelectionEdgeFields = this.getAggregationFields(relationshipAdapter);
const aggregateSelectionEdgeFields = this.getAggregationFields(relationshipAdapter, features);
const aggregateSelectionEdgeName = relationshipAdapter.operations.getAggregationFieldTypename("edge");

aggregateSelectionEdge = this.createAggregationField(
Expand All @@ -94,14 +96,24 @@ export class FieldAggregationComposer {
}

private getAggregationFields(
entity: RelationshipAdapter | ConcreteEntityAdapter | InterfaceEntityAdapter
): Record<string, ObjectTypeComposer> {
entity: RelationshipAdapter | ConcreteEntityAdapter | InterfaceEntityAdapter,
features: Neo4jFeaturesSettings | undefined
): ObjectTypeComposerFieldConfigMapDefinition<any, any> {
return entity.aggregableFields.reduce((res, field) => {
const objectTypeComposer = this.aggregationTypesMapper.getAggregationType(field.getTypeName());

if (!objectTypeComposer) return res;

res[field.name] = objectTypeComposer.NonNull;
if (!objectTypeComposer) {
return res;
}
// TODO: REMOVE ID FIELD ON 7.x
if (field.typeHelper.isID()) {
if (shouldAddDeprecatedFields(features, "idAggregations")) {
res[field.name] = { type: objectTypeComposer.NonNull, directives: [DEPRECATE_ID_AGGREGATION] };
}
return res;
} else {
res[field.name] = objectTypeComposer.NonNull;
}

return res;
}, {});
Expand Down
8 changes: 8 additions & 0 deletions packages/graphql/src/schema/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ export const DEPRECATE_OVERWRITE = {
},
};


export const DEPRECATE_ID_AGGREGATION = {
name: DEPRECATED,
args: {
reason: "aggregation of ID fields are deprecated and will be removed",
},
};

export const DEPRECATE_TYPENAME_IN = {
name: DEPRECATED,
args: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,10 @@ export function createRelationshipFields({
// make a new fn augmentObjectTypeWithAggregationField
const fieldAggregationComposer = new FieldAggregationComposer(schemaComposer, subgraph);

const aggregationTypeObject = fieldAggregationComposer.createAggregationTypeObject(relationshipAdapter);
const aggregationTypeObject = fieldAggregationComposer.createAggregationTypeObject(
relationshipAdapter,
features
);

const aggregationFieldsBaseArgs = {
where: relationshipTarget.operations.whereInputTypeName,
Expand Down
57 changes: 48 additions & 9 deletions packages/graphql/src/schema/generation/aggregate-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import type { RelationshipAdapter } from "../../schema-model/relationship/model-
import { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter";
import type { Neo4jFeaturesSettings } from "../../types";
import type { AggregationTypesMapper } from "../aggregations/aggregation-types-mapper";
import { DEPRECATE_IMPLICIT_EQUAL_FILTERS } from "../constants";
import { DEPRECATE_ID_AGGREGATION, DEPRECATE_IMPLICIT_EQUAL_FILTERS } from "../constants";
import { numericalResolver } from "../resolvers/field/numerical";
import { graphqlDirectivesToCompose } from "../to-compose";
import { shouldAddDeprecatedFields } from "./utils";
Expand All @@ -43,11 +43,13 @@ export function withAggregateSelectionType({
aggregationTypesMapper,
propagatedDirectives,
composer,
features,
}: {
entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter;
aggregationTypesMapper: AggregationTypesMapper;
propagatedDirectives: DirectiveNode[];
composer: SchemaComposer;
features: Neo4jFeaturesSettings | undefined;
}): ObjectTypeComposer {
const aggregateSelection = composer.createObjectTC({
name: entityAdapter.operations.aggregateTypeNames.selection,
Expand All @@ -60,23 +62,35 @@ export function withAggregateSelectionType({
},
directives: graphqlDirectivesToCompose(propagatedDirectives),
});
aggregateSelection.addFields(makeAggregableFields({ entityAdapter, aggregationTypesMapper }));
aggregateSelection.addFields(makeAggregableFields({ entityAdapter, aggregationTypesMapper, features }));
return aggregateSelection;
}

function makeAggregableFields({
entityAdapter,
aggregationTypesMapper,
features,
}: {
entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter;
aggregationTypesMapper: AggregationTypesMapper;
features: Neo4jFeaturesSettings | undefined;
}): ObjectTypeComposerFieldConfigMapDefinition<any, any> {
const aggregableFields: ObjectTypeComposerFieldConfigMapDefinition<any, any> = {};
const aggregableAttributes = entityAdapter.aggregableFields;
for (const attribute of aggregableAttributes) {
const objectTypeComposer = aggregationTypesMapper.getAggregationType(attribute.getTypeName());
if (objectTypeComposer) {
aggregableFields[attribute.name] = objectTypeComposer.NonNull;
// TODO: REMOVE ID FIELD ON 7.x
if (attribute.typeHelper.isID()) {
if (shouldAddDeprecatedFields(features, "idAggregations")) {
aggregableFields[attribute.name] = {
type: objectTypeComposer.NonNull,
directives: [DEPRECATE_ID_AGGREGATION],
};
}
continue;
}
aggregableFields[attribute.name] = { type: objectTypeComposer.NonNull };
}
}
return aggregableFields;
Expand Down Expand Up @@ -151,6 +165,7 @@ function withAggregationWhereInputType({
entityAdapter,
composer,
userDefinedDirectivesOnTargetFields,
features,
}: {
relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter;
entityAdapter:
Expand Down Expand Up @@ -186,18 +201,24 @@ function withAggregationWhereInputType({
NOT: aggregationInput,
});

const aggrFields = makeAggregationFields(aggregationFields, userDefinedDirectivesOnTargetFields);
const aggrFields = makeAggregationFields(aggregationFields, userDefinedDirectivesOnTargetFields, features);
aggregationInput.addFields(aggrFields);
return aggregationInput;
}

function makeAggregationFields(
attributes: AttributeAdapter[],
userDefinedDirectivesOnTargetFields: Map<string, DirectiveNode[]> | undefined
userDefinedDirectivesOnTargetFields: Map<string, DirectiveNode[]> | undefined,
features: Neo4jFeaturesSettings | undefined
): InputTypeComposerFieldConfigMapDefinition {
const fields: InputTypeComposerFieldConfigMapDefinition = {};
for (const attribute of attributes) {
addAggregationFieldsByType(attribute, userDefinedDirectivesOnTargetFields?.get(attribute.name), fields);
addAggregationFieldsByType(
attribute,
userDefinedDirectivesOnTargetFields?.get(attribute.name),
fields,
features
);
}
return fields;
}
Expand All @@ -206,11 +227,13 @@ function makeAggregationFields(
function addAggregationFieldsByType(
attribute: AttributeAdapter,
directivesOnField: DirectiveNode[] | undefined,
fields: InputTypeComposerFieldConfigMapDefinition
fields: InputTypeComposerFieldConfigMapDefinition,
features: Neo4jFeaturesSettings | undefined
): InputTypeComposerFieldConfigMapDefinition {
const deprecatedDirectives = graphqlDirectivesToCompose(
(directivesOnField || []).filter((d) => d.name.value === DEPRECATED)
);

if (attribute.typeHelper.isString()) {
for (const operator of AGGREGATION_COMPARISON_OPERATORS) {
fields[`${attribute.name}_AVERAGE_LENGTH_${operator}`] = {
Expand Down Expand Up @@ -251,13 +274,29 @@ function addAggregationFieldsByType(
const averageType = attribute.typeHelper.isBigInt()
? "BigInt"
: attribute.typeHelper.isDuration()
? "Duration"
: GraphQLFloat;
? "Duration"
: GraphQLFloat;
fields[`${attribute.name}_AVERAGE_${operator}`] = { type: averageType, directives: deprecatedDirectives };
}
return fields;
}
for (const operator of AGGREGATION_COMPARISON_OPERATORS) {
// TODO: REMOVE ID FIELD ON 7.x
if (attribute.typeHelper.isID()) {
if (shouldAddDeprecatedFields(features, "idAggregations")) {
fields[`${attribute.name}_MIN_${operator}`] = {
type: attribute.getTypeName(),
directives: deprecatedDirectives.length ? deprecatedDirectives : [DEPRECATE_ID_AGGREGATION],
};
fields[`${attribute.name}_MAX_${operator}`] = {
type: attribute.getTypeName(),
directives: deprecatedDirectives.length ? deprecatedDirectives : [DEPRECATE_ID_AGGREGATION],
};
}

continue;
}

fields[`${attribute.name}_MIN_${operator}`] = {
type: attribute.getTypeName(),
directives: deprecatedDirectives,
Expand Down
2 changes: 2 additions & 0 deletions packages/graphql/src/schema/make-augmented-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ function generateObjectType({
aggregationTypesMapper,
propagatedDirectives,
composer,
features,
});

composer.Query.addFields({
Expand Down Expand Up @@ -716,6 +717,7 @@ function generateInterfaceObjectType({
aggregationTypesMapper,
propagatedDirectives,
composer,
features,
});

composer.Query.addFields({
Expand Down
1 change: 1 addition & 0 deletions packages/graphql/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ export type Neo4jFeaturesSettings = {
deprecatedOptionsArgument?: boolean;
directedArgument?: boolean;
connectOrCreate?: boolean;
idAggregations?: boolean;
typename_IN?: boolean;
};
vector?: Neo4jVectorSettings;
Expand Down
Loading

0 comments on commit fd3f015

Please sign in to comment.