UNPKG

@neo4j/graphql

Version:

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

103 lines 4.99 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.matchMathField = matchMathField; exports.mathDescriptorBuilder = mathDescriptorBuilder; exports.buildMathStatements = buildMathStatements; const map_to_db_property_1 = __importDefault(require("../../utils/map-to-db-property")); /** Maps Neo4jGraphQL Math operator to Cypher symbol */ const CypherOperatorMap = new Map([ ["_ADD", "+"], ["_SUBTRACT", "-"], ["_MULTIPLY", "*"], ["_DIVIDE", "/"], ["_INCREMENT", "+"], ["_DECREMENT", "-"], ]); function mathOperatorToSymbol(mathOperator) { if (CypherOperatorMap.has(mathOperator)) { return CypherOperatorMap.get(mathOperator); } throw new Error(`${mathOperator} is not a valid math operator`); } const MATH_FIELD_REGEX = /(?<propertyName>\w*)(?<operatorName>_INCREMENT|_DECREMENT|_ADD|_SUBTRACT|_DIVIDE|_MULTIPLY)\b/; // Returns True in case of a valid match and the potential match. function matchMathField(graphQLFieldName) { const mathFieldMatch = graphQLFieldName.match(MATH_FIELD_REGEX); if (mathFieldMatch && mathFieldMatch.groups) { const { operatorName, propertyName } = mathFieldMatch.groups; const hasMatched = Boolean(mathFieldMatch && mathFieldMatch.length > 2 && operatorName && propertyName); return { hasMatched, operatorName, propertyName, }; } return { hasMatched: false, operatorName: "", propertyName: "", }; } function mathDescriptorBuilder(value, entity, fieldMatch) { const fieldName = fieldMatch.propertyName; const field = entity.primitiveFields.find((x) => x.fieldName === fieldName); if (!field) { throw new Error(`${fieldName} is not settable`); } return { dbName: (0, map_to_db_property_1.default)(entity, fieldName), graphQLType: field.typeMeta.name, fieldName, operationName: fieldMatch.operatorName, operationSymbol: mathOperatorToSymbol(fieldMatch.operatorName), value, }; } function buildMathStatements(mathDescriptor, scope, withVars, param) { if (mathDescriptor.operationSymbol === "/" && mathDescriptor.value === 0) { throw new Error("Division by zero is not supported"); } const validatePredicates = []; // Raise for operations with NAN validatePredicates.push(`apoc.util.validatePredicate(${scope}.${mathDescriptor.dbName} IS NULL, 'Cannot %s %s to Nan', ["${mathDescriptor.operationName}", $${param}])`); const bitSize = mathDescriptor.graphQLType === "Int" ? 32 : 64; // Avoid overflows, for 64 bit overflows, a long overflow is raised anyway by Neo4j validatePredicates.push(`apoc.util.validatePredicate(${scope}.${mathDescriptor.dbName} IS NOT NULL AND ${scope}.${mathDescriptor.dbName} ${mathDescriptor.operationSymbol} $${param} > 2^${bitSize - 1}-1, 'Overflow: Value returned from operator %s is larger than %s bit', ["${mathDescriptor.operationName}", "${bitSize}"])`); // Avoid type coercion where dividing an integer would result in a float value if (mathDescriptor.operationSymbol === "/" && mathDescriptor.graphQLType.includes("Int")) { validatePredicates.push(`apoc.util.validatePredicate(${scope}.${mathDescriptor.dbName} IS NOT NULL AND (${scope}.${mathDescriptor.dbName} ${mathDescriptor.operationSymbol} $${param}) % 1 <> 0, 'Type Mismatch: Value returned from operator %s does not match: %s', ["${mathDescriptor.operationName}", "${mathDescriptor.graphQLType}"])`); } const statements = []; const mathScope = Array.from(new Set([scope, ...withVars])); statements.push(`WITH ${mathScope.join(", ")}`); statements.push(`CALL(${scope}) {`); statements.push(`WITH ${scope}`); // Validations statements.push(`WHERE ${validatePredicates.join(" AND ")}`); statements.push(`SET ${scope}.${mathDescriptor.dbName} = ${scope}.${mathDescriptor.dbName} ${mathDescriptor.operationSymbol} $${param}`); statements.push(`RETURN ${scope} as ${scope}_${mathDescriptor.dbName}_${mathDescriptor.operationName}`); statements.push(`}`); return statements; } //# sourceMappingURL=math.js.map