@biothings-explorer/query_graph_handler
Version:
A nodejs module to query TRAPI Query Graph
199 lines (190 loc) • 6.79 kB
JavaScript
const _ = require('lodash');
const LogEntry = require('./log_entry');
const config = require('./config');
const ID_WITH_PREFIXES = ['MONDO', 'DOID', 'UBERON', 'EFO', 'HP', 'CHEBI', 'CL', 'MGI', 'NCIT'];
const debug = require('debug')('bte:biothings-explorer-trapi:qedge2btedge');
module.exports = class QEdge2BTEEdgeHandler {
constructor(qEdges, kg) {
this.qEdges = qEdges;
this.kg = kg;
this.logs = [];
}
setQEdges(qEdges) {
this.qEdges = qEdges;
}
_findAPIsFromSmartAPIEdges(smartapiEdges) {
return smartapiEdges.map((edge) => edge.association.api_name);
}
/**
* Get SmartAPI Edges based on TRAPI Query Edge.
* @private
* @param {object} kg - SmartAPI Knowledge Graph Object
* @param {object} qEdge - TRAPI Query Edge Object
*/
_getSmartAPIEdges(qEdge, kg = this.kg) {
debug(`Subject node is ${qEdge.getSubject().id}`);
debug(`Object node is ${qEdge.getObject().id}`);
this.logs.push(
new LogEntry(
'DEBUG',
null,
`BTE is trying to find SmartAPI edges connecting from ${qEdge.getSubject().getCategories()} to ${qEdge
.getObject()
.getCategories()} with predicate ${qEdge.getPredicate()}`,
).getLog(),
);
let filterCriteria = {
input_type: qEdge.getSubject().getCategories(),
output_type: qEdge.getObject().getCategories(),
predicate: qEdge.getPredicate(),
};
debug(`Filter criteria is: ${JSON.stringify(filterCriteria)}`);
let smartapi_edges = kg.filter(filterCriteria);
smartapi_edges = smartapi_edges.map((item) => {
item.reasoner_edge = qEdge;
return item;
});
if (smartapi_edges.length === 0) {
debug(`No smartapi edge found for ${qEdge.getID()}`);
this.logs.push(
new LogEntry('WARNING', null, `BTE didn't find any smartapi edges corresponding to ${qEdge.getID()}`).getLog(),
);
} else {
this.logs.push(
new LogEntry(
'DEBUG',
null,
`BTE found ${
smartapi_edges.length
} smartapi edges corresponding to ${qEdge.getID()}. These smartaip edges comes from ${
new Set(this._findAPIsFromSmartAPIEdges(smartapi_edges)).size
} unique APIs. They are ${Array.from(new Set(this._findAPIsFromSmartAPIEdges(smartapi_edges))).join(',')}`,
).getLog(),
);
}
return smartapi_edges;
}
/**
* @private
* @param {object} resolvedIDs
* @param {object} smartAPIEdge
*/
_createNonBatchSupportBTEEdges(smartAPIEdge) {
const bteEdges = [];
const inputID = smartAPIEdge.association.input_id;
const inputType = smartAPIEdge.association.input_type;
const resolvedIDs = smartAPIEdge.reasoner_edge.input_equivalent_identifiers;
for (const curie in resolvedIDs) {
resolvedIDs[curie].map((entity) => {
if (entity.semanticType === inputType && inputID in entity.dbIDs) {
entity.dbIDs[inputID].map((id) => {
const edge = _.cloneDeep(smartAPIEdge);
edge.input = id;
edge.input_resolved_identifiers = {
[curie]: [entity],
};
if (ID_WITH_PREFIXES.includes(inputID) || id.toString().includes(':')) {
edge.original_input = {
[id]: curie,
};
} else {
edge.original_input = {
[inputID + ':' + id]: curie,
};
}
const edgeToBePushed = _.cloneDeep(edge);
edgeToBePushed.reasoner_edge = smartAPIEdge.reasoner_edge;
bteEdges.push(edgeToBePushed);
});
}
});
}
return bteEdges;
}
/**
* @private
* @param {object} resolvedIDs
* @param {object} smartAPIEdge
*/
_createBatchSupportBTEEdges(smartAPIEdge) {
const id_mapping = {};
const inputs = [];
const bteEdges = [];
const input_resolved_identifiers = {};
const inputID = smartAPIEdge.association.input_id;
const inputType = smartAPIEdge.association.input_type;
let resolvedIDs = smartAPIEdge.reasoner_edge.input_equivalent_identifiers;
debug(`Resolved ids: ${JSON.stringify(resolvedIDs)}`);
debug(`Input id: ${inputID}`);
for (const curie in resolvedIDs) {
resolvedIDs[curie].map((entity) => {
if (smartAPIEdge.tags.includes('bte-trapi')) {
if (entity.semanticType === inputType) {
input_resolved_identifiers[curie] = [entity];
inputs.push(entity.primaryID);
id_mapping[entity.primaryID] = curie;
}
} else if (entity.semanticType === inputType && inputID in entity.dbIDs) {
entity.dbIDs[inputID].map((id) => {
if (ID_WITH_PREFIXES.includes(inputID) || id.includes(':')) {
id_mapping[id] = curie;
} else {
id_mapping[inputID + ':' + id] = curie;
}
input_resolved_identifiers[curie] = [entity];
inputs.push(id);
});
}
});
}
if (Object.keys(id_mapping).length > 0) {
const edge = _.cloneDeep(smartAPIEdge);
edge.input = inputs;
edge.input_resolved_identifiers = input_resolved_identifiers;
edge.original_input = id_mapping;
const edgeToBePushed = _.cloneDeep(edge);
edgeToBePushed.reasoner_edge = smartAPIEdge.reasoner_edge;
bteEdges.push(edgeToBePushed);
}
return bteEdges;
}
/**
* Add inputs to smartapi edges
*/
_createBTEEdges(edge) {
const supportBatch = edge.query_operation.supportBatch;
let bteEdges;
if (supportBatch === false) {
bteEdges = this._createNonBatchSupportBTEEdges(edge);
} else {
bteEdges = this._createBatchSupportBTEEdges(edge);
}
return bteEdges;
}
convert(qEdges) {
let bteEdges = [];
qEdges.map((edge) => {
const smartapi_edges = this._getSmartAPIEdges(edge);
debug(`${smartapi_edges.length} SmartAPI edges are retrieved....`);
smartapi_edges.map((item) => {
let newEdges = this._createBTEEdges(item);
debug(`${newEdges.length} BTEEdges are created....`);
newEdges = newEdges.map((e) => {
e.filter = edge.filter;
return e;
});
bteEdges = [...bteEdges, ...newEdges];
});
});
if (bteEdges.length === 0) {
debug(`No bte edge found for this query batch.`);
this.logs.push(
new LogEntry('WARNING', null, `BTE didn't find any bte edges for this batch. Your query terminates.`).getLog(),
);
} else {
debug(`BTE found ${bteEdges.length} bte edges for this batch.`);
this.logs.push(new LogEntry('DEBUG', null, `BTE found ${bteEdges.length} bte edges for this batch.`).getLog());
}
return bteEdges;
}
};