wikimedica-disease-search/app/src/fetch.js

139 lines
3.8 KiB
JavaScript
Raw Normal View History

import * as mock from './data/mock.js';
/**
* Recherche lensemble 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 =>
{
// Si aucun terme dans la requête, toutes les maladies correspondent
if (!query.length)
{
return Object.values(mock.terms);
}
// Marqueurs indiquant pour chaque terme lensemble des éléments de la
// requête quil a pour symptôme au travers dune 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 = mock.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
// et qui sont des maladies constituent des résultats valides
return Object.entries(matchingSymptoms).filter(
([id, matches]) => (
matches.length === query.length
&& mock.terms[id].types.includes(mock.types.disease)
)
).map(
([id]) => mock.terms[id]
);
};
/**
* 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 = mock.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 => mock.terms[id]);;
};
/**
* Calcule le sous-graphe issu dun 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 mock.hasSymptom)
{
if (
termsIds.includes(from)
&& termsIds.includes(to)
)
{
edges.push([from, to]);
}
}
return {nodes, edges};
};