UNPKG

@neo4j/graphql

Version:

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

170 lines 8.16 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. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DeleteOperation = void 0; const cypher_builder_1 = __importDefault(require("@neo4j/cypher-builder")); const utils_1 = require("../../../../utils/utils"); const wrap_subquery_in_calls_1 = require("../../utils/wrap-subquery-in-calls"); const operations_1 = require("./operations"); class DeleteOperation extends operations_1.MutationOperation { constructor({ target, selection, nestedOperations = [], filters = [], authFilters = [], }) { super(); this.target = target; this.selection = selection; this.filters = filters; this.authFilters = authFilters; this.nestedOperations = nestedOperations; } getChildren() { return [this.selection, ...this.filters, ...this.authFilters, ...this.nestedOperations]; } transpile(context) { if (!context.hasTarget()) { throw new Error("No parent node found!"); } const { selection, nestedContext } = this.selection.apply(context); if (nestedContext.relationship) { return this.transpileNested(selection, nestedContext); } return this.transpileTopLevel(selection, nestedContext); } /** No need for this, as all auth is done before */ getAuthorizationSubqueries(_context) { return []; } transpileTopLevel(selection, context) { this.validateSelection(selection); const filterSubqueries = (0, wrap_subquery_in_calls_1.wrapSubqueriesInCypherCalls)(context, this.filters, [context.target]); const authBeforeSubqueries = this.getAuthFilterSubqueries(context).map((c) => { return new cypher_builder_1.default.Call(c, [context.target]); }); const predicate = this.getPredicate(context); const validations = this.getValidations(context); const extraSelections = this.getExtraSelections(context); const nestedOperations = this.getNestedDeleteSubQueries(context); let statements = [selection, ...extraSelections, ...filterSubqueries, ...authBeforeSubqueries]; statements = this.appendFilters(statements, predicate); statements.push(...validations); if (nestedOperations.length) { statements.push(new cypher_builder_1.default.With("*"), ...nestedOperations); } statements = this.appendDeleteClause(statements, context); const ret = cypher_builder_1.default.utils.concat(...statements); return { clauses: [ret], projectionExpr: context.target }; } transpileNested(selection, context) { this.validateSelection(selection); if (!context.relationship) { throw new Error("Transpile error: No relationship found!"); } const filterSubqueries = (0, wrap_subquery_in_calls_1.wrapSubqueriesInCypherCalls)(context, this.filters, [context.target]); const authBeforeSubqueries = this.getAuthFilterSubqueries(context).map((c) => { return new cypher_builder_1.default.Call(c, [context.target]); }); const extraSelections = this.getExtraSelections(context); const predicate = this.getPredicate(context); const validations = this.getValidations(context); const collect = cypher_builder_1.default.collect(context.target).distinct(); const deleteVar = new cypher_builder_1.default.Variable(); const withBeforeDeleteBlock = new cypher_builder_1.default.With(context.relationship, [collect, deleteVar]); const unwindDeleteVar = new cypher_builder_1.default.Variable(); const deleteClause = new cypher_builder_1.default.Unwind([deleteVar, unwindDeleteVar]).detachDelete(unwindDeleteVar); const deleteBlock = new cypher_builder_1.default.Call(deleteClause, [deleteVar]); const nestedOperations = this.getNestedDeleteSubQueries(context); const statements = this.appendFilters([selection, ...extraSelections, ...filterSubqueries, ...authBeforeSubqueries], predicate); statements.push(...validations); if (nestedOperations.length) { statements.push(new cypher_builder_1.default.With("*"), ...nestedOperations); } statements.push(withBeforeDeleteBlock, deleteBlock); const ret = cypher_builder_1.default.utils.concat(...statements); return { clauses: [ret], projectionExpr: cypher_builder_1.default.Null }; } appendDeleteClause(clauses, context) { const lastClause = this.getLastClause(clauses); if (lastClause instanceof cypher_builder_1.default.Match || lastClause instanceof cypher_builder_1.default.OptionalMatch || lastClause instanceof cypher_builder_1.default.With) { lastClause.detachDelete(context.target); return clauses; } const extraWith = new cypher_builder_1.default.With("*"); extraWith.detachDelete(context.target); clauses.push(extraWith); return clauses; } getLastClause(clauses) { const lastClause = clauses[clauses.length - 1]; if (!lastClause) { throw new Error("Transpile error"); } return lastClause; } appendFilters(clauses, predicate) { if (!predicate) { return clauses; } const lastClause = this.getLastClause(clauses); if (lastClause instanceof cypher_builder_1.default.Match || lastClause instanceof cypher_builder_1.default.OptionalMatch || lastClause instanceof cypher_builder_1.default.With) { lastClause.where(predicate); return clauses; } const withClause = new cypher_builder_1.default.With("*"); withClause.where(predicate); clauses.push(withClause); return clauses; } getNestedDeleteSubQueries(context) { const nestedOperations = []; for (const nestedDeleteOperation of this.nestedOperations) { const { clauses } = nestedDeleteOperation.transpile(context); nestedOperations.push(...clauses.map((c) => new cypher_builder_1.default.Call(c, "*"))); } return nestedOperations; } validateSelection(selection) { if (!(selection instanceof cypher_builder_1.default.Match || selection instanceof cypher_builder_1.default.With)) { throw new Error("Cypher Yield statement is not a valid selection for Delete Operation"); } } getPredicate(queryASTContext) { const authBeforePredicates = this.getAuthFilterPredicate(queryASTContext); return cypher_builder_1.default.and(...this.filters.map((f) => f.getPredicate(queryASTContext)), ...authBeforePredicates); } getValidations(queryASTContext) { return (0, utils_1.filterTruthy)(this.authFilters.map((auth) => auth.getValidation(queryASTContext))); } getAuthFilterSubqueries(context) { return this.authFilters.flatMap((f) => f.getSubqueries(context)); } getAuthFilterPredicate(context) { return (0, utils_1.filterTruthy)(this.authFilters.map((f) => f.getPredicate(context))); } getExtraSelections(context) { return this.getChildren().flatMap((f) => f.getSelection(context)); } } exports.DeleteOperation = DeleteOperation; //# sourceMappingURL=DeleteOperation.js.map