diff --git a/src/index.js b/src/index.js index 3fdb066f..94f4398a 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,9 @@ const InvalidQueryGraphError = require('./exceptions/invalid_query_graph_error') const debug = require('debug')('bte:biothings-explorer-trapi:main'); const Graph = require('./graph/graph'); const EdgeManager = require('./edge_manager'); +const _ = require('lodash'); +const QEdge2BTEEdgeHandler = require('./qedge2bteedge'); +const LogEntry = require('./log_entry'); const redisClient = require('./redis-client'); exports.InvalidQueryGraphError = InvalidQueryGraphError; @@ -108,6 +111,47 @@ exports.TRAPIQueryHandler = class TRAPIQueryHandler { return handler; } + async _edgesSupported(qEdges, kg) { + // _.cloneDeep() is resource-intensive but only runs once per query + qEdges = _.cloneDeep(qEdges); + const manager = new EdgeManager(qEdges); + const edgesMissingOps = {}; + while (manager.getEdgesNotExecuted()) { + let current_edge = manager.getNext(); + const edgeConverter = new QEdge2BTEEdgeHandler([current_edge], kg); + const sAPIEdges = edgeConverter.getSmartAPIEdges(current_edge); + if (!sAPIEdges.length) { + edgesMissingOps[current_edge.qEdge.id] = current_edge.reverse; + } + // assume results so next edge may be reversed or not + current_edge.executed = true; + current_edge.object.entity_count = 1; + current_edge.subject.entity_count = 1; + // this.logs = [...this.logs, ...edgeConverter.logs]; + } + + const len = Object.keys(edgesMissingOps).length; + // this.logs = [...this.logs, ...manager.logs]; + let edgesToLog = Object.entries(edgesMissingOps).map(([edge, reversed]) => { + return reversed + ? `(reversed ${edge})` + : `(${edge})`; + }); + edgesToLog = edgesToLog.length > 1 + ? `[${edgesToLog.join(', ')}]` + : `${edgesToLog.join(', ')}` + if (len > 0) { + const terminateLog = `Query Edge${len > 1 ? 's' : ''} ${edgesToLog} ${ + len > 1 ? 'have' : 'has' + } no SmartAPI edges. Your query terminates.`; + debug(terminateLog); + this.logs.push(new LogEntry('WARNING', null, terminateLog).getLog()); + return false; + } else { + return true; + } + } + async query() { this._initializeResponse(); debug('Start to load metakg.'); @@ -115,6 +159,9 @@ exports.TRAPIQueryHandler = class TRAPIQueryHandler { debug('MetaKG successfully loaded!'); let queryEdges = await this._processQueryGraph(this.queryGraph); debug(`(3) All edges created ${JSON.stringify(queryEdges)}`); + if (!(await this._edgesSupported(queryEdges, kg))) { + return; + } const manager = new EdgeManager(queryEdges); while (manager.getEdgesNotExecuted()) { //next available/most efficient edge diff --git a/src/qedge2bteedge.js b/src/qedge2bteedge.js index ceae00a7..46d37b22 100644 --- a/src/qedge2bteedge.js +++ b/src/qedge2bteedge.js @@ -31,7 +31,7 @@ module.exports = class QEdge2BTEEdgeHandler { * @param {object} kg - SmartAPI Knowledge Graph Object * @param {object} qEdge - TRAPI Query Edge Object */ - _getSmartAPIEdges(qEdge, kg = this.kg) { + getSmartAPIEdges(qEdge, kg = this.kg) { debug(`Subject node is ${qEdge.getSubject().id}`); debug(`Object node is ${qEdge.getObject().id}`); this.logs.push( @@ -319,7 +319,7 @@ module.exports = class QEdge2BTEEdgeHandler { async convert(qEdges) { let bteEdges = []; await Promise.all(qEdges.map(async (edge) => { - const smartapi_edges = await this._getSmartAPIEdges(edge); + const smartapi_edges = await this.getSmartAPIEdges(edge); const apis = _.uniq(smartapi_edges.map(api => api.association.api_name)); debug(`${apis.length} APIs being used:`, JSON.stringify(apis)); debug(`${smartapi_edges.length} SmartAPI edges are retrieved....`);