UNPKG

neode

Version:
302 lines (237 loc) 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addNodeToStatement = addNodeToStatement; exports.addRelationshipToStatement = addRelationshipToStatement; exports.addNodeRelationshipToStatement = addNodeRelationshipToStatement; exports.ORIGINAL_ALIAS = exports.MAX_CREATE_DEPTH = void 0; var _GenerateDefaultValues = _interopRequireDefault(require("./GenerateDefaultValues")); var _Node = _interopRequireDefault(require("../Node")); var _Entity = require("../Entity"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var MAX_CREATE_DEPTH = 99; exports.MAX_CREATE_DEPTH = MAX_CREATE_DEPTH; var ORIGINAL_ALIAS = 'this'; /** * Split properties into * * @param {String} mode 'create' or 'merge' * @param {Model} model Model to merge on * @param {Object} properties Map of properties * @param {Array} merge_on Array of properties explicitly stated to merge on * @return {Object} { inline, set, on_create, on_match } */ exports.ORIGINAL_ALIAS = ORIGINAL_ALIAS; function splitProperties(mode, model, properties) { var merge_on = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; var inline = {}; var set = {}; var on_create = {}; var on_match = {}; // Calculate Set Properties model.properties().forEach(function (property) { var name = property.name(); // Skip if not set if (!properties.hasOwnProperty(name)) { return; } var value = (0, _Entity.valueToCypher)(property, properties[name]); // If mode is create, go ahead and set everything if (mode == 'create') { inline[name] = value; } else if (merge_on.indexOf(name) > -1) { inline[name] = value; } // Only set protected properties on creation else if (property["protected"]() || property.primary()) { on_create[name] = value; } // Read-only property? else if (!property.readonly()) { set[name] = value; } }); return { inline: inline, on_create: on_create, on_match: on_match, set: set }; } /** * Add a node to the current statement * * @param {Neode} neode Neode instance * @param {Builder} builder Query builder * @param {String} alias Alias * @param {Model} model Model * @param {Object} properties Map of properties * @param {Array} aliases Aliases to carry through in with statement * @param {String} mode 'create' or 'merge' * @param {Array} merge_on Which properties should we merge on? */ function addNodeToStatement(neode, builder, alias, model, properties) { var aliases = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; var mode = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'create'; var merge_on = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : []; // Split Properties var _splitProperties = splitProperties(mode, model, properties, merge_on), inline = _splitProperties.inline, on_create = _splitProperties.on_create, on_match = _splitProperties.on_match, set = _splitProperties.set; // Add alias if (aliases.indexOf(alias) == -1) { aliases.push(alias); } // Create builder[mode](alias, model, inline); // On create set if (Object.keys(on_create).length) { Object.entries(on_create).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), key = _ref2[0], value = _ref2[1]; builder.onCreateSet("".concat(alias, ".").concat(key), value); }); } // On Match Set if (Object.keys(on_match).length) { Object.entries(on_match).forEach(function (_ref3) { var _ref4 = _slicedToArray(_ref3, 2), key = _ref4[0], value = _ref4[1]; builder.onCreateSet("".concat(alias, ".").concat(key), value); }); } // Set if (Object.keys(set).length) { Object.entries(set).forEach(function (_ref5) { var _ref6 = _slicedToArray(_ref5, 2), key = _ref6[0], value = _ref6[1]; builder.set("".concat(alias, ".").concat(key), value); }); } // Relationships model.relationships().forEach(function (relationship, key) { if (properties.hasOwnProperty(key)) { var value = properties[key]; var rel_alias = "".concat(alias, "_").concat(key, "_rel"); var target_alias = "".concat(alias, "_").concat(key, "_node"); // Carry alias through builder["with"].apply(builder, _toConsumableArray(aliases)); if (!relationship.target()) { throw new Error("A target defintion must be defined for ".concat(key, " on model ").concat(model.name())); } else if (Array.isArray(relationship.target())) { throw new Error("You cannot create a node with the ambiguous relationship: ".concat(key, " on model ").concat(model.name())); } switch (relationship.type()) { // Single Relationship case 'relationship': addRelationshipToStatement(neode, builder, alias, rel_alias, target_alias, relationship, value, aliases, mode); break; // Array of Relationships case 'relationships': if (!Array.isArray(value)) value = [value]; value.forEach(function (value, idx) { // Carry alias through addRelationshipToStatement(neode, builder, alias, rel_alias + idx, target_alias + idx, relationship, value, aliases, mode); }); break; // Single Node case 'node': addNodeRelationshipToStatement(neode, builder, alias, rel_alias, target_alias, relationship, value, aliases, mode); break; // Array of Nodes case 'nodes': if (!Array.isArray(value)) value = [value]; value.forEach(function (value, idx) { addNodeRelationshipToStatement(neode, builder, alias, rel_alias + idx, target_alias + idx, relationship, value, aliases, mode); }); break; } } }); return builder; } /** * Add a relationship to the current statement * * @param {Neode} neode Neode instance * @param {Builder} builder Query builder * @param {String} alias Current node alias * @param {String} rel_alias Generated alias for the relationship * @param {String} target_alias Generated alias for the relationship * @param {Relationship} relationship Model * @param {Object} value Value map * @param {Array} aliases Aliases to carry through in with statement * @param {String} mode 'create' or 'merge' */ function addRelationshipToStatement(neode, builder, alias, rel_alias, target_alias, relationship, value, aliases, mode) { if (aliases.length > MAX_CREATE_DEPTH) { return; } // Extract Node var node_alias = relationship.nodeAlias(); var node_value = value[node_alias]; delete value[node_alias]; // Create Node // If Node is passed, attempt to create a relationship to that specific node if (node_value instanceof _Node["default"]) { builder.match(target_alias).whereId(target_alias, node_value.identity()); } // If Primary key is passed then try to match on that else if (typeof node_value == 'string' || typeof node_value == 'number') { var model = neode.model(relationship.target()); builder.merge(target_alias, model, _defineProperty({}, model.primaryKey(), node_value)); } // If Map is passed, attempt to create that node and then relate else if (Object.keys(node_value).length) { var _model = neode.model(relationship.target()); if (!_model) { throw new Error("Couldn't find a target model for ".concat(relationship.target(), " in ").concat(relationship.name(), ". Did you use module.exports?")); } node_value = _GenerateDefaultValues["default"].async(neode, _model, node_value); addNodeToStatement(neode, builder, target_alias, _model, node_value, aliases, mode, _model.mergeFields()); } // Create the Relationship builder[mode](alias).relationship(relationship.relationship(), relationship.direction(), rel_alias).to(target_alias); // Set Relationship Properties relationship.properties().forEach(function (property) { var name = property.name(); if (value.hasOwnProperty(name)) { builder.set("".concat(rel_alias, ".").concat(name), value[name]); } }); } /** * Add a node relationship to the current statement * * @param {Neode} neode Neode instance * @param {Builder} builder Query builder * @param {String} alias Current node alias * @param {String} rel_alias Generated alias for the relationship * @param {String} target_alias Generated alias for the relationship * @param {Relationship} relationship Model * @param {Object} value Value map * @param {Array} aliases Aliases to carry through in with statement * @param {String} mode 'create' or 'merge' */ function addNodeRelationshipToStatement(neode, builder, alias, rel_alias, target_alias, relationship, value, aliases, mode) { if (aliases.length > MAX_CREATE_DEPTH) { return; } // If Node is passed, attempt to create a relationship to that specific node if (value instanceof _Node["default"]) { builder.match(target_alias).whereId(target_alias, value.identity()); } // If Primary key is passed then try to match on that else if (typeof value == 'string' || typeof value == 'number') { var model = neode.model(relationship.target()); builder.merge(target_alias, model, _defineProperty({}, model.primaryKey(), value)); } // If Map is passed, attempt to create that node and then relate // TODO: What happens when we need to validate this? // TODO: Is mergeFields() the right option here? else if (Object.keys(value).length) { var _model2 = neode.model(relationship.target()); if (!_model2) { throw new Error("Couldn't find a target model for ".concat(relationship.target(), " in ").concat(relationship.name(), ". Did you use module.exports?")); } value = _GenerateDefaultValues["default"].async(neode, _model2, value); addNodeToStatement(neode, builder, target_alias, _model2, value, aliases, mode, _model2.mergeFields()); } // Create the Relationship builder[mode](alias).relationship(relationship.relationship(), relationship.direction(), rel_alias).to(target_alias); }