const { _ } = require('@kitmi/utils');
const { deepCloneField, Clonable, isDotSeparateName } = require('./XemlUtils');
/**
* Geml dataset class.
* @class Dataset
*/
class Dataset extends Clonable {
/**
* @param {Linker} linker
* @param {string} name - Dataset name
* @param {object} xemlModule - Source ool module
* @param {object} info - Dataset info
*/
constructor(linker, name, xemlModule, info) {
super();
/**
* Linker to process this document
* @member {Linker}
*/
this.linker = linker;
/**
* Name of this document
* @member {string}
*/
this.name = _.camelCase(name);
/**
* Owner oolong module
* @member {object}
*/
this.xemlModule = xemlModule;
/**
* Raw metadata
* @member {Object}
*/
this.info = info;
}
/**
* Start linking this dataset
* @returns {Dataset}
*/
link() {
pre: !this.linked;
if (this.info.entity) {
let entity = this.linker.getReferencedEntity(this.xemlModule, this.info.entity);
this.mainEntity = entity.name;
} else {
let dataset = this.linker.loadDataset(this.xemlModule, this.info.dataset);
assert: dataset.linked;
this.mainEntity = dataset.mainEntity;
this.joinWith = _.cloneDeep(dataset.joinWith);
}
if (!_.isEmpty(this.info.joinWith)) {
if (!this.joinWith) {
this.joinWith = _.cloneDeep(this.info.joinWith);
} else {
this.joinWith = this.joinWith.concat(this.info.joinWith);
}
}
this.linked = true;
return this;
}
buildHierarchy(inSchema) {
return this._flattenDataset(inSchema, this);
}
_flattenDataset(inSchema, dataset) {
let hierarchy = {};
let leftEntity = inSchema.entities[dataset.mainEntity];
if (dataset.joinWith) {
dataset.joinWith.forEach((joining) => {
let leftField, rightEntity, rightField;
if (isDotSeparateName(joining.on.left)) {
let lastPos = joining.on.left.lastIndexOf('.');
let fieldRef = joining.on.left.substr(lastPos + 1);
let entityRef = joining.on.left.substr(0, lastPos);
if (entityRef === leftEntity.name) {
leftField = leftEntity.getEntityAttribute(fieldRef);
} else {
throw new Error(`Unsupported syntax of left side joining field "${joining.on.left}".`);
}
} else {
//field of leftEntity
leftField = leftEntity.getEntityAttribute(joining.on.left);
}
if (joining.dataset) {
let rightHierarchy = inSchema.getDocumentHierachy(this.xemlModule, joining.dataset);
if (isDotSeparateName(joining.on.right)) {
let parts = joining.on.right.split('.');
if (parts.length > 2) {
throw new Error(
'Joining a document should only referencing to a field of its main entity.'
);
}
let [entityRef, fieldRef] = parts;
if (entityRef !== rightHierarchy.entity) {
throw new Error(
`Referenced field "${joining.on.right}" not found while linking to document "${joining.dataset}".`
);
}
assert: !hierarchy[leftField.name],
'Duplicate joinings on the same field of the left side entity.';
rightEntity = inSchema.entities[entityRef];
rightField = rightEntity.getEntityAttribute(fieldRef);
hierarchy[leftField.name] = Object.assign({}, rightHierarchy, {
linkWithField: rightField.name,
});
return;
}
//joining.on.right is field name of the main entity
rightEntity = inSchema.entities[joining.dataset.mainEntity];
} else {
rightEntity = inSchema.entities[joining.entity];
if (isDotSeparateName(joining.on.right)) {
throw new Error(
`Referenced field "${joining.on.right}" not found while linking to entity "${joining.entity}".`
);
}
}
//field of rightEntity
rightField = rightEntity.getEntityAttribute(joining.on.right);
assert: !hierarchy[leftField.name], 'Duplicate joinings on the same field of the left side entity.';
hierarchy[leftField.name] = {
$xt: 'DocumentHierarchyNode',
entity: rightEntity.name,
linkWithField: rightField.name,
};
});
}
return {
$xt: 'DocumentHierarchyNode',
entity: leftEntity.name,
subDocuments: hierarchy,
};
}
/**
* Clone the document
* @returns {Dataset}
*/
clone() {
super.clone();
let dataset = new Dataset(this.linker, this.name, this.xemlModule, this.info);
dataset.mainEntity = this.mainEntity;
deepCloneField(this, dataset, 'joinWith');
dataset.linked = true;
return dataset;
}
/**
* Translate the document into a plain JSON object
* @returns {object}
*/
toJSON() {
return {
name: this.name,
mainEntity: this.mainEntity.toJSON(),
joinWith: this.joinWith,
};
}
}
module.exports = Dataset;