Intermédiaire6 min de lecture

Sort keys composites dans DynamoDB

Une est une partition key plus une sort key. Le truc qui la rend puissante est ce que tu mets dans la sort key : encode une hiérarchie comme une string délimitée, et un seul Query lit tout un sous-arbre en ordre de tri — pas de joins, pas de récursion, pas de second round-trip.

Comment fonctionnent les sort keys composites dans DynamoDB ?

Une sort key composite emballe une hiérarchie dans une string délimitée — root/photos/2026/ — que DynamoDB stocke en ordre d'octets UTF-8. Parce que le layout correspond déjà à l'arbre, un seul Query avec begins_with(SK, "root/photos/") lit tout un sous-arbre en ordre de chemin. Pas de joins, pas de récursion, pas de second round-trip — juste un scan de préfixe sur une contiguë.

  • La sort key est une string triable, pas juste un ID. Emballe un chemin dedans — root/photos/2026/ — et DynamoDB stocke les items de la partition en ordre d'octets UTF-8 automatiquement.
  • Un délimiteur transforme les matches de préfixe en lectures de sous-arbre. begins_with(SK, "root/photos/") renvoie chaque descendant de ce dossier en une query.
  • Les sort keys supportent des conditions de plage, pas des filters arbitraires. Tu as begins_with, between, >, < — conçois la clé pour que la lecture dont tu as besoin soit un préfixe ou une plage, pas un Scan.
  • Le délimiteur est structurel. Choisis-en un qui ne peut pas apparaître dans un segment de chemin, sinon deux branches sans rapport collisionnent.

Pourquoi la sort key est tout le jeu

Venant de SQL, tu modéliserais un arbre de dossiers avec un self-join parent_id et le parcourrais récursivement — une query par niveau. Dans DynamoDB c'est un footgun N+1 contre un store key-value qui n'a pas de joins.

DynamoDB stocke chaque item sous une partition key trié par sa sort key, en ordre d'octets UTF-8 pour les strings (AWS : Query key conditions). Donc si ta sort key est le chemin, le layout physique correspond déjà à l'arbre. Une lecture devient un scan de préfixe sur une tranche contiguë — pas une marche de graphe.

C'est le shift : la sort key n'est pas un identifiant que tu matches exactement. C'est une adresse triable. Conçois-la et la query tombe pour free.

Modélise un arbre de fichiers

Disons que tu stockes des arbres de fichiers per-account. Un drive par compte est la partition naturelle ; le chemin à l'intérieur est la sort key.

PKSKnode_typebytes
DRIVE#a91root/folder-
DRIVE#a91root/docs/folder-
DRIVE#a91root/docs/taxes.pdffile88210
DRIVE#a91root/photos/folder-
DRIVE#a91root/photos/2026/folder-
DRIVE#a91root/photos/2026/beach.jpgfile284910
DRIVE#a91root/photos/2026/sunset.jpgfile512004

Deux conventions originales font le travail ici :

  • PK = DRIVE#<account> garde tout l'arbre d'un compte dans une seule , donc toute lecture de sous-arbre est un Query single-partition.
  • SK est le chemin complet avec un / trailing sur les dossiers. Le slash trailing est délibéré — il fait trier un dossier avant ses propres enfants et garde root/photos/ distinct d'un fichier sibling nommé root/photos.

Lis un sous-arbre en une query

Liste tout sous root/photos/ — dossier, sous-dossiers et fichiers, récursivement :

Query
KeyConditionExpression = PK = :drive AND begins_with(SK, :prefix)
:drive   = "DRIVE#a91"
:prefix  = "root/photos/"

Ça renvoie root/photos/, root/photos/2026/, beach.jpg et sunset.jpg — en ordre de chemin, en une lecture facturée. Tu paies seulement pour les items dans cette tranche, pas tout le drive.

Dans DynoTable, tu lances exactement ce query begins_with contre la sort key de chemin et le dossier plus ses descendants reviennent en ordre de chemin — pas de syntaxe de placeholder à écrire à la main.

Besoin du KeyConditionExpression brut (names, values et begins_with) pour ton propre code ? Construis-le et copie-le dans le DynamoDB Expression Builder.

Lancer un query begins_with sur la sort key de chemin dans DynoTable, renvoyant un dossier et ses descendants en ordre de chemin.
Lancer un query begins_with sur la sort key de chemin dans DynoTable, renvoyant un dossier et ses descendants en ordre de chemin.

Liste un niveau, pas tout le sous-arbre

begins_with te donne la lecture récursive. Pour un listing de répertoire non récursif — les enfants immédiats de root/photos/ et rien de plus profond — stocke un attribut depth et ajoute une plage de sort key plus un filter, ou découpe le chemin dans un GSI parent. La version la plus simple : garde un attribut parent (root/photos/) et un GSI keyed dessus.

Une sort key répond bon marché aux questions de préfixe et de plage. « Enfants directs seulement » est une question différente — modélise-la explicitement plutôt que d'espérer qu'un FilterExpression la rende efficace. Un filter tourne après la lecture et tu paies pour chaque item qu'il écarte.

Choisis le délimiteur avec soin

Le délimiteur fait partie de ton contrat de données. Deux règles :

  • Il ne doit jamais apparaître dans un segment de chemin. Si les filenames peuvent contenir /, / est le mauvais délimiteur — un fichier nommé a/b est indiscernable d'un dossier a tenant b. Choisis un octet réservé (certaines équipes utilisent # ou un caractère de contrôle) et interdis-le dans les segments.
  • Fais attention à l'ordre de tri aux frontières. / (0x2F) trie avant les chiffres et lettres, ce qui est généralement ce que tu veux pour l'ordre d'arbre. Change le délimiteur et tu changes l'ordre — vérifie contre de vraies données.

Sort key composite vs. un attribut de tri séparé

Sort key composite (root/photos/2026/x)Sort key ID plain + attribut parent
Lecture de sous-arbreUn query begins_withQueries récursives (N+1) ou marche GSI
OrdreOrdre de chemin, freeDoit ajouter un attribut de tri explicite
Move / renameRéécrire tous les descendantsUpdate un pointeur parent
Liste enfants directsBesoin d'attr depth ou GSINaturel (parent = x)

Les clés composites gagnent quand les lectures sont en forme de sous-arbre et que l'ordre compte ; le modèle flat-ID gagne quand l'arbre mute constamment. La plupart des hiérarchies read-heavy — arbres de fichiers, arbres de catégories, org charts — penchent composite.

Pièges et étapes suivantes

  • Ne sur-remplis pas la clé. Tout ce que tu encodes est immuable et indexé par préfixe seulement. Les attributs que tu queries par égalité appartiennent dans leurs propres fields ou un GSI, pas fourrés dans la sort key.
  • Une sort key ne peut pas faire de WHERE arbitraire. Seulement begins_with, between et comparaisons. Si tu te surprends à tendre la main vers un FilterExpression, tu as probablement mal modélisé la clé — vois Query vs. Scan.
  • Aller plus loin sur le key design vit dans single-table design ; pour quand une lecture de sous-arbre a besoin d'un index au lieu du table de base, vois GSI vs. LSI.

Construis la key condition begins_with avec l' Expression Builder, puis télécharge DynoTable pour lancer ces queries de préfixe contre tes propres tables et regarder un sous-arbre revenir en ordre de chemin. (Et le self-JOIN que tu as laissé derrière en SQL ? Le SQL Workbench de DynoTable le lance encore quand tu en as besoin.)

Mis à jour

Essaie cette conception de façon interactive

Esquisse tes entités et tes motifs d’accès dans l’outil gratuit de Single-Table Design DynamoDB — il suggère des modèles de clés PK/SK, prévisualise les collections d’items et montre quels motifs nécessitent un GSI.

Ouvrir l’outil de Single-Table Design