UNPKG

@neo4j/graphql

Version:

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations

479 lines 25.8 kB
"use strict"; /* * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AggregateFactory = void 0; const RelationshipAdapter_1 = require("../../../../schema-model/relationship/model-adapters/RelationshipAdapter"); const check_authentication_1 = require("../../../authorization/check-authentication"); const AggregationOperation_1 = require("../../ast/operations/AggregationOperation"); const CompositeAggregationOperation_1 = require("../../ast/operations/composite/CompositeAggregationOperation"); const CompositeAggregationPartial_1 = require("../../ast/operations/composite/CompositeAggregationPartial"); const NodeSelection_1 = require("../../ast/selection/NodeSelection"); const RelationshipSelection_1 = require("../../ast/selection/RelationshipSelection"); const get_concrete_entities_1 = require("../../utils/get-concrete-entities"); const is_concrete_entity_1 = require("../../utils/is-concrete-entity"); const is_interface_entity_1 = require("../../utils/is-interface-entity"); const find_fields_by_name_in_fields_by_type_name_field_1 = require("../parsers/find-fields-by-name-in-fields-by-type-name-field"); class AggregateFactory { constructor(queryASTFactory) { this.queryASTFactory = queryASTFactory; } // TODO: dupe from read operation createAggregationOperation({ entityOrRel, resolveTree, context, extraWhereArgs = {}, }) { let entity; if (entityOrRel instanceof RelationshipAdapter_1.RelationshipAdapter) { entity = entityOrRel.target; // TODO: check this seems wrong but outside of the scope of this PR } else { entity = entityOrRel; } const resolveTreeWhere = extraWhereArgs; if (entityOrRel instanceof RelationshipAdapter_1.RelationshipAdapter) { if ((0, is_concrete_entity_1.isConcreteEntity)(entity)) { // RELATIONSHIP WITH CONCRETE TARGET (0, check_authentication_1.checkEntityAuthentication)({ entity: entity.entity, targetOperations: ["AGGREGATE"], context, }); const selection = new RelationshipSelection_1.RelationshipSelection({ relationship: entityOrRel, }); const operation = new AggregationOperation_1.AggregationOperation({ entity: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), selection, }); return this.hydrateAggregationOperation({ relationship: entityOrRel, operation, entity, resolveTree, context, whereArgs: resolveTreeWhere, }); } else { // RELATIONSHIP WITH INTERFACE TARGET const concreteEntities = (0, get_concrete_entities_1.getConcreteEntities)(entity, resolveTreeWhere.node ?? {}); const concreteAggregationOperations = concreteEntities.map((concreteEntity) => { const aggregationPartial = new CompositeAggregationPartial_1.CompositeAggregationPartial({ target: concreteEntity, entity: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), }); const parsedProjectionFields = this.getAggregationParsedProjectionFields(entityOrRel, resolveTree); const nodeRawFields = { ...parsedProjectionFields.node?.fieldsByTypeName[entityOrRel.operations.getAggregateFieldTypename("node")], }; const attributes = this.queryASTFactory.operationsFactory.getSelectedAttributes(concreteEntity, nodeRawFields); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity: concreteEntity, operations: ["AGGREGATE"], context, attributes, }); aggregationPartial.addAuthFilters(...authFilters); return aggregationPartial; }); const compositeAggregationOp = new CompositeAggregationOperation_1.CompositeAggregationOperation({ compositeEntity: entity, children: concreteAggregationOperations, }); this.hydrateAggregationOperation({ relationship: entityOrRel, entity, resolveTree, context, operation: compositeAggregationOp, whereArgs: resolveTreeWhere, }); return compositeAggregationOp; } } else { if ((0, is_concrete_entity_1.isConcreteEntity)(entity)) { // TOP LEVEL CONCRETE const selection = new NodeSelection_1.NodeSelection({ target: entity, alias: "this", }); const operation = new AggregationOperation_1.AggregationOperation({ entity, directed: Boolean(resolveTree.args?.directed ?? true), selection, }); return this.hydrateAggregationOperation({ operation, entity, resolveTree, context, whereArgs: resolveTreeWhere, }); } else { // TOP level interface/union const concreteEntities = (0, get_concrete_entities_1.getConcreteEntities)(entity, resolveTreeWhere); const parsedProjectionFields = this.getAggregationParsedProjectionFields(entity, resolveTree); const concreteAggregationOperations = concreteEntities.map((concreteEntity) => { const aggregationPartial = new CompositeAggregationPartial_1.CompositeAggregationPartial({ target: concreteEntity, directed: Boolean(resolveTree.args?.directed ?? true), }); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity: concreteEntity, operations: ["AGGREGATE"], attributes: this.queryASTFactory.operationsFactory.getSelectedAttributes(concreteEntity, parsedProjectionFields.fields), context, }); aggregationPartial.addAuthFilters(...authFilters); return aggregationPartial; }); const compositeAggregationOp = new CompositeAggregationOperation_1.CompositeAggregationOperation({ compositeEntity: entity, children: concreteAggregationOperations, }); return this.hydrateAggregationOperation({ entity, resolveTree, context, operation: compositeAggregationOp, whereArgs: resolveTreeWhere, }); } } } /** @deprecated */ createAggregationOperationDeprecated({ entityOrRel, resolveTree, context, }) { let entity; if (entityOrRel instanceof RelationshipAdapter_1.RelationshipAdapter) { entity = entityOrRel.target; // TODO: check this seems wrong but outside of the scope of this PR } else { entity = entityOrRel; } const resolveTreeWhere = { ...this.queryASTFactory.operationsFactory.getWhereArgs(resolveTree), }; if (entityOrRel instanceof RelationshipAdapter_1.RelationshipAdapter) { if ((0, is_concrete_entity_1.isConcreteEntity)(entity)) { // RELATIONSHIP WITH CONCRETE TARGET (0, check_authentication_1.checkEntityAuthentication)({ entity: entity.entity, targetOperations: ["AGGREGATE"], context, }); const selection = new RelationshipSelection_1.RelationshipSelection({ relationship: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), }); const operation = new AggregationOperation_1.AggregationOperation({ entity: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), selection, }); return this.hydrateAggregationOperationDeprecated({ relationship: entityOrRel, operation, entity, resolveTree, context, whereArgs: resolveTreeWhere, }); } else { // RELATIONSHIP WITH INTERFACE TARGET const concreteEntities = (0, get_concrete_entities_1.getConcreteEntities)(entity, resolveTreeWhere); const parsedProjectionFields = this.getAggregationParsedProjectionFields(entityOrRel, resolveTree); const nodeRawFields = { ...parsedProjectionFields.node?.fieldsByTypeName[entityOrRel.operations.getAggregateFieldTypename("node")], }; const concreteAggregationOperations = concreteEntities.map((concreteEntity) => { const aggregationPartial = new CompositeAggregationPartial_1.CompositeAggregationPartial({ target: concreteEntity, entity: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), }); const attributes = this.queryASTFactory.operationsFactory.getSelectedAttributes(concreteEntity, nodeRawFields); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity: concreteEntity, operations: ["AGGREGATE"], context, attributes, }); aggregationPartial.addAuthFilters(...authFilters); return aggregationPartial; }); const compositeAggregationOp = new CompositeAggregationOperation_1.CompositeAggregationOperation({ compositeEntity: entity, children: concreteAggregationOperations, }); this.hydrateAggregationOperationDeprecated({ relationship: entityOrRel, entity, resolveTree, context, operation: compositeAggregationOp, whereArgs: resolveTreeWhere, }); return compositeAggregationOp; } } else { if ((0, is_concrete_entity_1.isConcreteEntity)(entity)) { // TOP LEVEL CONCRETE let selection; // NOTE: If we introduce vector index aggregation, checking the phrase will cause a problem if (context.resolveTree.args.fulltext || context.resolveTree.args.phrase) { selection = this.queryASTFactory.operationsFactory.getFulltextSelection(entity, context); } else { selection = new NodeSelection_1.NodeSelection({ target: entity, alias: "this", }); } const operation = new AggregationOperation_1.AggregationOperation({ entity, directed: Boolean(resolveTree.args?.directed ?? true), selection, }); return this.hydrateAggregationOperationDeprecated({ operation, entity, resolveTree, context, whereArgs: resolveTreeWhere, }); } else { // TOP level interface/union const concreteEntities = (0, get_concrete_entities_1.getConcreteEntities)(entity, resolveTreeWhere); const parsedProjectionFields = this.getAggregationParsedProjectionFields(entity, resolveTree); const concreteAggregationOperations = concreteEntities.map((concreteEntity) => { const aggregationPartial = new CompositeAggregationPartial_1.CompositeAggregationPartial({ target: concreteEntity, directed: Boolean(resolveTree.args?.directed ?? true), }); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity: concreteEntity, operations: ["AGGREGATE"], attributes: this.queryASTFactory.operationsFactory.getSelectedAttributes(concreteEntity, parsedProjectionFields.fields), context, }); aggregationPartial.addAuthFilters(...authFilters); return aggregationPartial; }); const compositeAggregationOp = new CompositeAggregationOperation_1.CompositeAggregationOperation({ compositeEntity: entity, children: concreteAggregationOperations, }); return this.hydrateAggregationOperationDeprecated({ entity, resolveTree, context, operation: compositeAggregationOp, whereArgs: resolveTreeWhere, }); } } } getAggregationParsedProjectionFields(adapter, resolveTree) { const rawProjectionFields = { ...resolveTree.fieldsByTypeName[adapter.operations.getAggregateFieldTypename()], }; return this.queryASTFactory.operationsFactory.splitConnectionFields(rawProjectionFields); } hydrateAggregationOperation({ relationship, entity, operation, resolveTree, context, whereArgs, }) { // whereArgs = whereArgs.node ?? {}; const deprecatedAttributes = false; if (relationship) { const parsedProjectionFields = this.getAggregationParsedProjectionFields(relationship, resolveTree); const edgeRawFields = { ...parsedProjectionFields.edge?.fieldsByTypeName[relationship.operations.getAggregationFieldTypename("edge")], }; const nodeRawFields = { ...parsedProjectionFields.node?.fieldsByTypeName[relationship.operations.getAggregationFieldTypename("node")], }; const fields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, parsedProjectionFields.fields, deprecatedAttributes); const nodeFields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, nodeRawFields, deprecatedAttributes); const edgeFields = this.queryASTFactory.fieldFactory.createAggregationFields(relationship, edgeRawFields, deprecatedAttributes); if ((0, is_interface_entity_1.isInterfaceEntity)(entity)) { const nodeFilters = this.queryASTFactory.filterFactory.createInterfaceNodeFilters({ entity, whereFields: whereArgs.node ?? {}, }); const edgefilters = this.queryASTFactory.filterFactory.createEdgeFilters(relationship, whereArgs.edge ?? {}); operation.addFilters(...nodeFilters, ...edgefilters); } else { const nodeFilters = this.queryASTFactory.filterFactory.createNodeFilters(entity, whereArgs.node ?? {}); // Aggregation filters only apply to target node const edgeFilters = this.queryASTFactory.filterFactory.createEdgeFilters(relationship, whereArgs.edge ?? {}); // Aggregation filters only apply to target node operation.addFilters(...nodeFilters, ...edgeFilters); const attributes = this.queryASTFactory.operationsFactory.getSelectedAttributes(entity, nodeRawFields); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity, operations: ["AGGREGATE"], attributes, context, }); operation.addAuthFilters(...authFilters); } operation.setFields(fields); operation.setNodeFields(nodeFields); operation.setEdgeFields(edgeFields); } else { const rawProjectionFields = { ...resolveTree.fieldsByTypeName[entity.operations.aggregateTypeNames.selection], ...resolveTree.fieldsByTypeName[entity.operations.aggregateTypeNames.node], // Handles both, deprecated and new aggregation parsing }; const fields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, rawProjectionFields, deprecatedAttributes); // TOP Level aggregate in connection const connectionFields = { // TOP level connection fields ...resolveTree.fieldsByTypeName[entity.operations.aggregateTypeNames.connection], }; const nodeResolveTree = (0, find_fields_by_name_in_fields_by_type_name_field_1.findFieldsByNameInFieldsByTypeNameField)(connectionFields, "node")[0]; const nodeRawFields = { ...nodeResolveTree?.fieldsByTypeName[entity.operations.aggregateTypeNames.node], }; const nodeFields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, nodeRawFields, deprecatedAttributes); operation.setNodeFields(nodeFields); const countResolveTree = (0, find_fields_by_name_in_fields_by_type_name_field_1.findFieldsByNameInFieldsByTypeNameField)(connectionFields, "count")[0]; if (countResolveTree) { const connetionTopFields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, { count: countResolveTree, }, deprecatedAttributes); fields.push(...connetionTopFields); } if ((0, is_interface_entity_1.isInterfaceEntity)(entity)) { const filters = this.queryASTFactory.filterFactory.createInterfaceNodeFilters({ entity, whereFields: whereArgs.node ?? {}, }); operation.addFilters(...filters); } else { const filters = this.queryASTFactory.filterFactory.createNodeFilters(entity, whereArgs.node ?? {}); // Aggregation filters only apply to target node operation.addFilters(...filters); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity, operations: ["AGGREGATE"], context, attributes: this.queryASTFactory.operationsFactory.getSelectedAttributes(entity, { ...nodeRawFields, ...rawProjectionFields, }), }); operation.addAuthFilters(...authFilters); } operation.setFields(fields); } return operation; } /** @deprecated */ hydrateAggregationOperationDeprecated({ relationship, entity, operation, resolveTree, context, whereArgs, }) { const deprecatedAttributes = true; if (relationship) { const parsedProjectionFields = this.getAggregationParsedProjectionFields(relationship, resolveTree); const edgeRawFields = { ...parsedProjectionFields.edge?.fieldsByTypeName[relationship.operations.getAggregateFieldTypename("edge")], }; const nodeRawFields = { ...parsedProjectionFields.node?.fieldsByTypeName[relationship.operations.getAggregateFieldTypename("node")], }; const fields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, parsedProjectionFields.fields, deprecatedAttributes); const nodeFields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, nodeRawFields, deprecatedAttributes); const edgeFields = this.queryASTFactory.fieldFactory.createAggregationFields(relationship, edgeRawFields, deprecatedAttributes); if ((0, is_interface_entity_1.isInterfaceEntity)(entity)) { const filters = this.queryASTFactory.filterFactory.createInterfaceNodeFilters({ entity, whereFields: whereArgs, }); operation.addFilters(...filters); } else { const filters = this.queryASTFactory.filterFactory.createNodeFilters(entity, whereArgs); // Aggregation filters only apply to target node operation.addFilters(...filters); const attributes = this.queryASTFactory.operationsFactory.getSelectedAttributes(entity, nodeRawFields); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity, operations: ["AGGREGATE"], attributes, context, }); operation.addAuthFilters(...authFilters); } operation.setFields(fields); operation.setNodeFields(nodeFields); operation.setEdgeFields(edgeFields); } else { const rawProjectionFields = { ...resolveTree.fieldsByTypeName[entity.operations.aggregateTypeNames.selection], ...resolveTree.fieldsByTypeName[entity.operations.aggregateTypeNames.node], // Handles both, deprecated and new aggregation parsing }; const fields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, rawProjectionFields, deprecatedAttributes); // TOP Level aggregate in connection const connectionFields = { // TOP level connection fields ...resolveTree.fieldsByTypeName[entity.operations.aggregateTypeNames.connection], }; const nodeResolveTree = (0, find_fields_by_name_in_fields_by_type_name_field_1.findFieldsByNameInFieldsByTypeNameField)(connectionFields, "node")[0]; const nodeRawFields = { ...nodeResolveTree?.fieldsByTypeName[entity.operations.aggregateTypeNames.node], }; const nodeFields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, nodeRawFields, deprecatedAttributes); operation.setNodeFields(nodeFields); const countResolveTree = (0, find_fields_by_name_in_fields_by_type_name_field_1.findFieldsByNameInFieldsByTypeNameField)(connectionFields, "count")[0]; if (countResolveTree) { const connetionTopFields = this.queryASTFactory.fieldFactory.createAggregationFields(entity, { count: countResolveTree, }, deprecatedAttributes); fields.push(...connetionTopFields); } if ((0, is_interface_entity_1.isInterfaceEntity)(entity)) { const filters = this.queryASTFactory.filterFactory.createInterfaceNodeFilters({ entity, whereFields: whereArgs, }); operation.addFilters(...filters); } else { const filters = this.queryASTFactory.filterFactory.createNodeFilters(entity, whereArgs); // Aggregation filters only apply to target node operation.addFilters(...filters); const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity, operations: ["AGGREGATE"], context, attributes: this.queryASTFactory.operationsFactory.getSelectedAttributes(entity, { ...nodeRawFields, ...rawProjectionFields, }), }); operation.addAuthFilters(...authFilters); } operation.setFields(fields); } return operation; } } exports.AggregateFactory = AggregateFactory; //# sourceMappingURL=AggregateFactory.js.map