153 lines
4.1 KiB
JavaScript
153 lines
4.1 KiB
JavaScript
/**
|
||
* @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};
|
||
};
|