All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ml-modules.root.data-hub.5.builtins.steps.mapping.default.lib.mjs Maven / Gradle / Ivy

There is a newer version: 6.1.1
Show newest version
import hubUtils from "../../../../impl/hub-utils.mjs";

const cachedMappingByNameAndVersion = {};
const cachedEntityByTitleAndVersion = {};

function getModel(targetEntity, version = '0.0.1') {
  let cacheKey = `${targetEntity}:${version}`;
  if (!cachedEntityByTitleAndVersion[cacheKey]) {
    cachedEntityByTitleAndVersion[cacheKey] = fn.head(cts.search(cts.andQuery([cts.collectionQuery('http://marklogic.com/entity-services/models'), cts.jsonPropertyScopeQuery('info', cts.andQuery([cts.jsonPropertyValueQuery('title', targetEntity, ['case-insensitive']), cts.jsonPropertyValueQuery('version', version, ['case-insensitive'])]))]), ["score-zero", "unfaceted", "document"], 0));
  }
  return cachedEntityByTitleAndVersion[cacheKey];
}

function getMapping(mappingName) {
  return fn.head(cts.search(cts.andQuery([cts.collectionQuery('http://marklogic.com/data-hub/mappings'), cts.jsonPropertyValueQuery('name', mappingName, ['unstemmed', 'case-insensitive'])]), ["score-zero", "unfaceted", cts.indexOrder(cts.uriReference(), "descending")]));
}

function getMappingWithVersion(mappingName, version) {
  let cacheKey = `${mappingName}:${version}`;
  if (!cachedMappingByNameAndVersion[cacheKey]) {
    cachedMappingByNameAndVersion[cacheKey] = fn.head(cts.search(cts.andQuery([cts.collectionQuery('http://marklogic.com/data-hub/mappings'), cts.jsonPropertyValueQuery('name', mappingName, ['unstemmed', 'case-insensitive']), cts.jsonPropertyValueQuery('version', version)]), ["score-zero", "unfaceted"], 0));
  }
  return cachedMappingByNameAndVersion[cacheKey];
}

function getSourceContext(sourceContext) {
  let connector = "/*:";
  let srcCtxArr;
  sourceContext = String(sourceContext);
  sourceContext = sourceContext.startsWith("/") ? sourceContext.substring(1, sourceContext.length) : sourceContext;
  srcCtxArr = sourceContext.split("/");
  sourceContext = "";

  srcCtxArr.forEach(function(element) {
    if (element.indexOf(':') === -1) {
      sourceContext += connector + element;
    } else {
      sourceContext += "/" + element;
    }
  });
  return sourceContext;
}

function getPath(sourceContext, connector, propertyName) {
  let path;
  // validExtractPath will recognize complex XPath, like attributes and filtered steps
  // if not a validExtractPath, likely a JSON property with XPath incompatible name
  if (cts.validExtractPath(propertyName)) {
    path = `${sourceContext}${connector}${propertyName}`;
    if (connector.includes("*:") && !cts.validExtractPath(path)) {
      connector = connector.replace("*:", "");
      path = `${sourceContext}${connector}${propertyName}`;
    }
  } else {
    if (connector.includes("*:")) {
      connector = connector.replace("*:", "");
    }
    path = `${sourceContext}${connector}node('${propertyName}')[fn:not(. instance of array-node())]`;
  }
  return path;
}

function processInstance(model, mapping, content, provenance = {}) {
  return extractInstanceFromModel(model, model.info.title, mapping, content, provenance);
}

function extractInstanceFromModel(model, modelName, mapping, content, provenance = {}) {
  let sourceContext = mapping.sourceContext;
  if (hubUtils.isXmlDocument(content) && sourceContext !== '/' && sourceContext !== '//')  {
    sourceContext = getSourceContext(sourceContext);
  }
  let mappingProperties = mapping.properties;
  let instance = {};
  instance['$type'] = model.info.title;
  if (model.info.version) {
    instance['$version'] = model.info.version;
  } else {
    instance['$version'] = '0.0.1';
  }

  if (!(content.nodeName === 'envelope' || (content.nodeKind === 'document'))) {
    content = new NodeBuilder().addNode(fn.head(content)).toNode();
  }
  if (fn.head(content.xpath('/*:envelope'))) {
    let leadingXPath = '/*:envelope/*:instance';
    if (fn.count(content.xpath('/*:envelope/*:instance/(element() except *:info)')) === 1 && sourceContext === '/') {
      leadingXPath = leadingXPath + "/*";
    }
    sourceContext = leadingXPath + sourceContext;
  }
  let definition = model.definitions[modelName];
  //first let's get our required props and PK
  let required = definition.required;
  if (definition.primaryKey && definition.required.indexOf(definition.primaryKey) === -1) {
    definition.required.push(definition.primaryKey);
  }
  let properties = definition.properties;
  for (let property in properties) {
    if (properties.hasOwnProperty(property)) {
      let prop = properties[property];
      let dataType = prop["datatype"];
      let valueSource = null;
      let connector = "";
      let xpathToSource;
      if (mappingProperties && mappingProperties.hasOwnProperty(property)) {
        const sourcedFrom = String(mappingProperties[property].sourcedFrom);
        if (sourceContext[sourceContext.length-1] !== '/' &&  !sourcedFrom.startsWith('/') && !sourcedFrom.startsWith('[')) {
          connector += '/';
        }
        if (sourcedFrom.indexOf(':') === -1) {
          connector += '*:';
        }
        xpathToSource = getPath(sourceContext, connector, sourcedFrom);
      } else {
        if (sourceContext[sourceContext.length - 1] !== '/' && !property.startsWith('/') && !property.startsWith('[')) {
          connector += '/';
        }
        if (property.indexOf(':') === -1) {
          connector += '*:';
        }
        xpathToSource = getPath(sourceContext, connector, property);
      }
      valueSource = content.xpath(xpathToSource);
      if (dataType !== 'array') {
        valueSource = fn.head(valueSource);
      }
      let value = null;
      if (!dataType && prop['$ref']) {
        let refArr = String(prop['$ref']).split('/');
        let refModelName = refArr[refArr.length - 1];
        if (valueSource) {
          let itemSource = new NodeBuilder();
          itemSource.addNode(valueSource);
          value = {refModelName: extractInstanceFromModel(model, refModelName, mapping, itemSource.toNode())};
        } else {
          value = null;
        }
      } else if (dataType === 'array') {
        let items = prop['items'];
        let itemsDatatype = items['datatype'];
        let valueArray = [];
        if (!itemsDatatype && items['$ref']) {
          let refArr = String(items['$ref']).split('/');
          let refModelName = refArr[refArr.length - 1];
          for (const item of Sequence.from(valueSource)) {
          // let's create and pass the node
            let itemSource = new NodeBuilder();
            itemSource.addNode(item);
            valueArray.push(extractInstanceFromModel(model, refModelName, mapping, itemSource.toNode()));
          }
        } else {
          for (const val of Sequence.from(valueSource)) {
            valueArray.push(castDataType(dataType, val.valueOf()));
          }
        }
        value = valueArray;

      } else {
        if (valueSource) {
          try {
            value = castDataType(dataType, valueSource);
          } catch (e) {
            value = null;
          }
        }
      }
      if (required.indexOf(property) > -1 && !value) {
        throw Error('The property: ' + property + ' is required property on the model: ' + modelName + ' and must have a valid value. Value was: ' + valueSource + '.');
      }
      provenance[xpathToSource] = {destination: property, value: xdmp.quote(value)};
      instance[property] = value;
    }
  }

  return instance;
}

function castDataType(dataType, value) {
  //default, so let's set it
  let convertedValue = value;

  if (dataType === 'iri') {
    convertedValue = xs.string(value);
  } else if (dataType === 'duration') {
    convertedValue = xs.duration(value);
  } else if (dataType === 'negativeInteger') {
    convertedValue = xs.negativeInteger(value);
  } else if (dataType === 'array') {
  } else if (dataType === 'float') {
    convertedValue = xs.float(value);
  } else if (dataType === 'nonNegativeInteger') {
    convertedValue = xs.nonNegativeInteger(value);
  } else if (dataType === 'anyURI') {
    convertedValue = xs.string(value);
  } else if (dataType === 'gDay') {
    convertedValue = xs.gDay(value);
  } else if (dataType === 'nonPositiveInteger') {
    convertedValue = xs.nonPositiveInteger(value);
  } else if (dataType === 'base64Binary') {
    convertedValue = xs.base64Binary(value);
  } else if (dataType === 'gMonth') {
    convertedValue = xs.gMonth(value);
  } else if (dataType === 'short') {
    convertedValue = xs.short(value);
  } else if (dataType === 'boolean') {
    convertedValue = xs.boolean(value);
  } else if (dataType === 'gMonthDay') {
    convertedValue = xs.gMonthDay(value);
  } else if (dataType === 'string') {
    convertedValue = xs.string(value);
  } else if (dataType === 'byte') {
    convertedValue = xs.byte(value);
  } else if (dataType === 'gYear') {
    convertedValue = xs.gYear(value);
  } else if (dataType === 'time') {
    convertedValue = xs.time(value);
  } else if (dataType === 'date') {
    convertedValue = xs.date(value);
  } else if (dataType === 'gYearMonth') {
    convertedValue = xs.gYearMonth(value);
  } else if (dataType === 'unsignedByte') {
    convertedValue = xs.unsignedByte(value);
  } else if (dataType === 'dateTime') {
    convertedValue = xs.dateTime(value);
  } else if (dataType === 'hexBinary') {
    convertedValue = xs.hexBinary(value);
  } else if (dataType === 'unsignedInt') {
    convertedValue = xs.unsignedInt(value);
  } else if (dataType === 'dayTimeDuration') {
    convertedValue = xs.dayTimeDuration(value);
  } else if (dataType === 'int') {
    convertedValue = xs.int(value);
  } else if (dataType === 'unsignedLong') {
    convertedValue = xs.unsignedLong(value);
  } else if (dataType === 'decimal') {
    convertedValue = xs.decimal(value);
  } else if (dataType === 'integer') {
    convertedValue = xs.integer(value);
  } else if (dataType === 'unsignedShort') {
    convertedValue = xs.unsignedShort(value);
  } else if (dataType === 'double') {
    convertedValue = xs.double(value);
  } else if (dataType === 'long') {
    convertedValue = xs.long(value);
  } else if (dataType === 'yearMonthDuration') {
    convertedValue = xs.yearMonthDuration(value);
  }
  return convertedValue;
}

export default {
  // exporting the caches so tests can avoid DB inserts
  cachedMappingByNameAndVersion,
  cachedEntityByTitleAndVersion,
  castDataType,
  extractInstanceFromModel,
  getMapping,
  getMappingWithVersion,
  processInstance,
  getModel
};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy