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