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

147 lines
4.0 KiB
JavaScript
Raw Normal View History

import * as mock from './mock.js';
/**
2019-12-04 04:20:06 +00:00
* 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 =>
{
2019-12-04 04:20:06 +00:00
let allMatches = [];
if (!query.length)
{
2019-12-04 04:20:06 +00:00
// Si aucun terme dans la requête, tout correspond
allMatches = Object.values(mock.terms).map(({id}) => id);
}
2019-12-04 04:20:06 +00:00
else
{
2019-12-04 04:20:06 +00:00
// 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)
{
2019-12-04 04:20:06 +00:00
const stack = [queryTerm.id];
2019-12-04 04:20:06 +00:00
while (stack.length)
{
const current = stack.pop();
const neighbors = mock.symptomOf.filter(
([from]) => from === current
).map(
([, to]) => to
);
for (let neighbor of neighbors)
{
2019-12-04 04:20:06 +00:00
if (!(neighbor in matchingSymptoms))
{
matchingSymptoms[neighbor] = [];
}
if (!matchingSymptoms[neighbor].includes(queryTerm.id))
{
matchingSymptoms[neighbor].push(queryTerm.id);
stack.push(neighbor);
}
}
}
}
2019-12-04 04:20:06 +00:00
// 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
);
}
2019-12-04 04:20:06 +00:00
// On ne garde que les maladies
return allMatches.map(
id => mock.terms[id]
).filter(
term => term.types.includes(mock.types.disease)
);
};
/**
2019-12-04 04:20:06 +00:00
* 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
2019-12-04 04:20:06 +00:00
// 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);
}
}
}
2019-12-04 04:20:06 +00:00
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};
};