/** * @module * Fonctions de requêtage des données d’exemple. */ import {termTypes} from '../model.js'; import * as data from './data.js'; /** * Recherche l’ensemble des maladies liées par une relation « a pour symptôme » * avec un ensemble de termes donné. * * @param query Ensemble de termes attendus. * @return Liste des maladies correspondantes. */ export const diseasesBySymptoms = async query => { let allMatches = []; if (!query.length) { // Si aucun terme dans la requête, tout correspond allMatches = Object.values(data.terms).map(({id}) => id); } else { // Marqueurs indiquant pour chaque terme l’ensemble des éléments de la // requête qu’il a pour symptôme au travers d’une relation directe ou // transitive const matchingSymptoms = {}; // Réalise un parcours en profondeur du graphe en partant de chaque terme // de la requête pour marquer les résultats for (let queryTerm of query) { const stack = [queryTerm.id]; while (stack.length) { const current = stack.pop(); const neighbors = data.symptomOf.filter( ([from]) => from === current ).map( ([, to]) => to ); for (let neighbor of neighbors) { if (!(neighbor in matchingSymptoms)) { matchingSymptoms[neighbor] = []; } if (!matchingSymptoms[neighbor].includes(queryTerm.id)) { matchingSymptoms[neighbor].push(queryTerm.id); stack.push(neighbor); } } } } // Seuls les termes ayant été visités par tous les éléments de la // requête sont des résultats valides allMatches = Object.entries(matchingSymptoms).filter( ([, matches]) => matches.length === query.length ).map( ([id]) => id ); } // On ne garde que les maladies return allMatches.map( id => data.terms[id] ).filter( term => term.types.includes(termTypes.disease) ); }; /** * Récupère tous les termes liés à un ensemble de termes par une relation « a * pour symptôme », directe ou transitive. * * @param terms Ensemble de termes initiaux. * @return Termes de `terms` ainsi que leurs voisins par la relation symptôme. */ export const exploreSymptoms = async terms => { // Voisins des termes de la requête const selected = terms.map(({id}) => id); // Fait un parcours en profondeur issu de chaque résultat pour obtenir // tous les voisins directs ou transitifs par la relation « a pour // symptôme » const stack = [...selected]; while (stack.length) { const current = stack.pop(); const neighbors = data.hasSymptom.filter( ([from]) => from === current ).map( ([, to]) => to ); for (let neighbor of neighbors) { if (!selected.includes(neighbor)) { selected.push(neighbor); stack.push(neighbor); } } } return selected.map(id => data.terms[id]); }; /** * Calcule le sous-graphe issu d’un ensemble de termes. * * @param terms Ensemble de termes à mettre dans le sous-graphe. * @return Objet contenant les nœuds et les arêtes du graphe. */ export const symptomsSubgraph = async terms => { const termsIds = terms.map(({id}) => id); // Construction du graphe constitué des éléments sélectionnés et des // arêtes qui les lient const nodes = {}; const edges = []; for (let term of terms) { nodes[term.id] = term; } // Sélection des arêtes liant les nœuds sélectionnés for (let [from, to] of data.hasSymptom) { if ( termsIds.includes(from) && termsIds.includes(to) ) { edges.push([from, to]); } } return {nodes, edges}; };