@neo4j/graphql
Version:
A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations
165 lines • 7.02 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.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompositeAggregationOperation = void 0;
const cypher_builder_1 = __importDefault(require("@neo4j/cypher-builder"));
const utils_1 = require("../../../../../utils/utils");
const QueryASTContext_1 = require("../../QueryASTContext");
const operations_1 = require("../operations");
class CompositeAggregationOperation extends operations_1.Operation {
constructor({ compositeEntity, children, directed = true, }) {
super();
this.fields = [];
this.nodeFields = [];
this.edgeFields = [];
this.authFilters = [];
this.filters = [];
this.addWith = true;
this.aggregationProjectionMap = new cypher_builder_1.default.Map();
this.nodeMap = new cypher_builder_1.default.Map();
this.edgeMap = new cypher_builder_1.default.Map();
this.entity = compositeEntity;
this.children = children;
this.directed = directed;
}
getChildren() {
return (0, utils_1.filterTruthy)([
...this.fields,
...this.nodeFields,
...this.edgeFields,
...this.filters,
...this.authFilters,
...this.children,
]);
}
transpile(context) {
const parentNode = context.target;
if (parentNode) {
const result = this.transpileAggregationOperation(context);
return {
clauses: result.clauses,
projectionExpr: result.projectionExpr,
};
}
else {
const newContext = new QueryASTContext_1.QueryASTContext({
target: new cypher_builder_1.default.Node(),
neo4jGraphQLContext: context.neo4jGraphQLContext,
});
const result = this.transpileAggregationOperation(newContext, false);
return {
clauses: result.clauses,
projectionExpr: result.projectionExpr,
};
}
}
setFields(fields) {
this.fields = fields;
}
addFilters(...filters) {
this.filters.push(...filters);
}
addAuthFilters(...filter) {
this.authFilters.push(...filter);
}
setNodeFields(fields) {
this.nodeFields = fields;
}
setEdgeFields(fields) {
this.edgeFields = fields;
}
getFieldProjectionClause(target, returnVariable, field) {
return field.getAggregationProjection(target, returnVariable);
}
transpileAggregationOperation(context, addWith = true) {
this.addWith = addWith;
const fieldSubqueries = this.createSubqueries(this.fields, context, this.aggregationProjectionMap);
const nodeFieldSubqueries = this.createSubqueries(this.nodeFields, context, this.nodeMap);
const edgeFieldSubqueries = this.createSubqueries(this.edgeFields, context, this.edgeMap, new cypher_builder_1.default.NamedNode("edge"));
if (this.nodeMap.size > 0) {
this.aggregationProjectionMap.set("node", this.nodeMap);
}
if (this.edgeMap.size > 0) {
this.aggregationProjectionMap.set("edge", this.edgeMap);
}
return {
clauses: [...fieldSubqueries, ...nodeFieldSubqueries, ...edgeFieldSubqueries],
projectionExpr: this.aggregationProjectionMap,
};
}
getPredicates(queryASTContext) {
const authPredicates = this.getAuthFilterPredicate(queryASTContext);
return cypher_builder_1.default.and(...this.filters.map((f) => f.getPredicate(queryASTContext)), ...authPredicates);
}
getAuthFilterPredicate(context) {
return (0, utils_1.filterTruthy)(this.authFilters.map((f) => f.getPredicate(context)));
}
createSubqueries(fields, context, projectionMap, target = new cypher_builder_1.default.NamedNode("node")) {
return fields.map((field) => {
const returnVariable = new cypher_builder_1.default.Node();
const nestedContext = context.setReturn(returnVariable);
const withClause = this.createWithClause(context);
const nestedSubquery = this.createNestedSubquery(nestedContext, target);
projectionMap.set(field.getProjectionField(nestedContext.returnVariable));
return cypher_builder_1.default.utils.concat(nestedSubquery, withClause, field.getAggregationProjection(target, nestedContext.returnVariable));
});
}
createWithClause(context) {
const node = new cypher_builder_1.default.NamedNode("node");
const filterContext = new QueryASTContext_1.QueryASTContext({
neo4jGraphQLContext: context.neo4jGraphQLContext,
target: node,
relationship: new cypher_builder_1.default.NamedRelationship("edge"),
});
const filterPredicates = this.getPredicates(filterContext);
const validations = (0, utils_1.filterTruthy)(this.authFilters.map((auth) => auth.getValidation(filterContext)));
let withClause;
if (filterPredicates || validations.length > 0) {
withClause = new cypher_builder_1.default.With("*");
withClause.where(filterPredicates);
for (const validation of validations) {
withClause.call(validation);
}
}
return withClause;
}
createNestedSubquery(context, target) {
const parentNode = context.target;
const nestedSubqueries = this.children.flatMap((c) => {
if (target.name === "edge") {
c.setAttachedTo("relationship");
}
else {
c.setAttachedTo("node");
}
let clauses = c.getSubqueries(context);
if (parentNode && this.addWith) {
clauses = clauses.map((sq) => cypher_builder_1.default.utils.concat(new cypher_builder_1.default.With(parentNode), sq));
}
return clauses;
});
return new cypher_builder_1.default.Call(new cypher_builder_1.default.Union(...nestedSubqueries));
}
}
exports.CompositeAggregationOperation = CompositeAggregationOperation;
//# sourceMappingURL=CompositeAggregationOperation.js.map