107 lines
4.3 KiB
TeX
Executable File
107 lines
4.3 KiB
TeX
Executable File
\chapter{Arborescences et graphs}
|
|
|
|
Les arborescences et les graphs sont deux structures de données extrêmement utiles et souvent rencontrées dans les applications modernes.
|
|
Le cas le plus simple pourrait être un forum de discussions \footnote{Anciennement, PhpBB permettait de répondre à un message antérieur, et copiait pour cela le message en question dans le nouveau encart. Niveau traçabilité, on était plus proche du zéro absolu que de la blockchain.}, un réseau social ou une plateforme de discussions chiffrée \footnote{iMessages est sans doute plus évolué que Signal à ce sujet, mais les deux permettent bien de répondre à un message en particulier}, où un intervenant `A` a la possibilité de répondre à un message antérieurement publié.
|
|
|
|
\section{Arborescences}
|
|
|
|
Il est possible de voir une arborescence comme un graph dirigé dont chaque noeud n'aurait au maximum qu'une seule relation vers son parent.
|
|
Chaque niveau de l'arborescence est un \texttt{noeud}, tandis que les noeuds n'ayant aucun enfant sont des \texttt{feuilles}.
|
|
|
|
\begin{graphic}{images/trees/tree01.png}
|
|
\caption{Un exemple d'arborescence sur quatre niveaux}
|
|
\end{graphic}
|
|
|
|
\subsection{Tables liées}
|
|
|
|
La représentation d'une arborescence grâce à des tables est la plus simple que nous pourrons trouver: elle consiste à créer une table par niveau devant être représenté.
|
|
|
|
\begin{minted}{python}
|
|
|
|
from django.db import models
|
|
|
|
|
|
class Level1(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
|
|
|
|
class Level2(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
parent = models.ForeignKey(Level1, null=True, blank=True)
|
|
|
|
|
|
class Level3(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
parent = models.ForeignKey(Level2, null=True, blank=True)
|
|
|
|
|
|
class Level4(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
parent = models.ForeignKey(Level3, null=True, blank=True)
|
|
|
|
\end{minted}
|
|
|
|
Cette représentation est réellement simpliste, et même si elle peut répondre rapidement à un besoin, nous nous rendons compte rapidement des limites de ce système:
|
|
|
|
\begin{enumerate}
|
|
\item Il est impossible de \textbf{déplacer} une instance d'un niveau vers un autre: tant que l'on restera au même niveau, il sera possible de modifier le parent, mais pas de changer un objet de niveau.
|
|
\item Si nous souhaitons ajouter un nouveau niveau, cela reviendra à ajouter une nouvelle classe (et donc, une nouvelle table).
|
|
\item La récupération de données (ie. \textit{Le chemin complet vers une entité}) reviendra à exécuter autant de requêtes qu'il y a de niveaux avant la racine.
|
|
\end{enumerate}
|
|
|
|
Ces points impliquent que l'évolutivité de la solution sera rapidement compromise.
|
|
|
|
\subsection{Listes adjacentes}
|
|
|
|
|
|
|
|
\subsection{\textit{Jaywalking}}
|
|
|
|
|
|
|
|
\subsection{\textit{Modified Preorder Tree Traversal}}
|
|
|
|
|
|
|
|
\subsection{Closure tables}
|
|
|
|
On a un exemple de remplissage/vidage d'une closure table, mais il faudrait en fait présenter les listes adjacentes et les autres structures de données.
|
|
Comme ça on pourra introduire les graphs juste après.
|
|
|
|
\begin{minted}[tabsize=4]{python}
|
|
# <app>/management/commands/rebuild.py
|
|
"""This command manages Closure Tables implementation
|
|
|
|
It adds new levels and cleans links between entities.
|
|
This way, it's relatively easy to fetch an entire tree with just one tiny
|
|
request.
|
|
"""
|
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
from structure.models import Entity, EntityTreePath
|
|
|
|
|
|
class Command(BaseCommand):
|
|
def handle(self, *args, **options):
|
|
entities = Entity.objects.all()
|
|
|
|
for entity in entities:
|
|
breadcrumb = [node for node in entity.breadcrumb()]
|
|
tree = set(EntityTreePath.objects.filter(descendant=entity))
|
|
|
|
for idx, node in enumerate(breadcrumb):
|
|
tree_path, _ = EntityTreePath.objects.get_or_create(
|
|
ancestor=node, descendant=entity, weight=idx + 1
|
|
)
|
|
|
|
if tree_path in tree:
|
|
tree.remove(tree_path)
|
|
for tree_path in tree:
|
|
tree_path.delete()
|
|
\end{minted}
|
|
|
|
\section{Graphs}
|
|
|
|
La représentation de graphs est hors de ce périmètre: il conviendrait d'aborder des bases de données ayant un modèle différent d'un modèle relatonnel, sans quoi les performances seront atrocement complexes et abominables \cite[p. 49-55]{data_intensive}.
|