@neo4j/graphql
Version:
A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations
479 lines • 25.8 kB
JavaScript
"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