ml-modules.root.data-hub.5.builtins.steps.mapping.default.lib.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of marklogic-data-hub Show documentation
Show all versions of marklogic-data-hub Show documentation
Library for Creating an Operational Data Hub on MarkLogic
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