Intermédiaire6 min de lecture

Les key condition expressions DynamoDB

Une key condition expression est la KeyConditionExpression que tu passes à un Query — la seule partie de la requête que DynamoDB utilise pour trouver les items. Tout le reste (filtres, projections) s'exécute après que la lecture est déjà mesurée.

Qu'est-ce qu'une key condition expression dans DynamoDB ?

Une key condition expression est la KeyConditionExpression d'un Query qui indique à DynamoDB quels items lire. La doit être une égalité (PK = :v) ; la accepte un opérateur de plage — =, <, <=, >, >=, BETWEEN ou begins_with. C'est elle qui détermine ce qui est lu et facturé, contrairement à un filtre.

  • La clé de partition doit être une égalité. PK = :v et rien d'autre — pas de plages, pas de begins_with, pas de IN. DynamoDB la hache pour localiser une partition.
  • La clé de tri prend un opérateur de plage. =, <, <=, >, >=, BETWEEN, ou begins_with — c'est là que tu découpes une item collection.
  • Ce n'est pas un filtre. Une condition de clé décide ce qui est lu et facturé ; un FilterExpression ne fait que rogner le résultat après que tu as payé la lecture.
  • Les clés de tri sont ordonnées par octets. Les opérateurs de plage comparent lexicographiquement, donc la façon dont tu formates la chaîne de clé de tri est ta puissance de requête.

Pourquoi la clé de partition est verrouillée sur l'égalité

DynamoDB stocke les items en hachant la clé de partition vers une partition physique. Un hachage te donne un emplacement, pas une plage — donc il n'y a rien à scanner à travers.

C'est pourquoi PK > :v ou begins_with(PK, :v) sont rejetés d'emblée. Le moteur ne peut pas répondre à « toutes les partitions dont la clé commence par X » sans lire toute la table, ce qui est exactement le Scan qu'il est conçu pour éviter.

Venant de SQL, ça semble à l'envers : WHERE id LIKE 'order%' est trivial dans Postgres. Dans DynamoDB la clé de partition est une adresse, pas une colonne cherchable.

La clé de tri est là où vit la puissance

Au sein d'une partition, les items sont stockés triés par la clé de tri. Cet ordonnancement est ce que les opérateurs de plage exploitent — DynamoDB se positionne puis lit vers l'avant.

OpérateurLitSert pour
SK = :vUn item exactUn enfant précis par sa clé
SK < / <= / > / >= :vUne tranche ouverte d'un côté« Tout après ce point »
SK BETWEEN :a AND :bUne plage fermée (inclusive)Une fenêtre bornée — une plage de dates
begins_with(SK, :p)Une tranche par préfixeUn type ou une hiérarchie sous la PK

Il n'y a pas de LIKE, pas de CONTAINS, pas de ENDS_WITH sur la clé. La correspondance de sous-chaîne et de suffixe n'est pas ordonnée par octets, donc elle forcerait une lecture complète — par conception, l'API ne te le laisse pas faire. Ceux-là vivent dans FilterExpression, où tu as déjà payé. (AWS : Key condition expressions)

Un exemple travaillé : les messages d'une app de chat

Disons que tu construis un chat par canaux. Une table, partitionnée par canal, triée par heure de message. Schéma de clés original :

  • Clé de partition ChannelRefCH#{channelId}
  • Clé de tri PostedAt — un horodatage ISO-8601, MSG#2026-06-23T14:05:00Z

Le préfixe MSG# garde les lignes de message triables et distinctes de tout autre type de ligne que tu pourrais co-localiser sous le même canal (config épinglée, adhésion).

Charger les derniers messages d'un canal. Juste la clé de partition, du plus récent au plus ancien :

KeyConditionExpression:  ChannelRef = :ch
ExpressionAttributeValues:  { ":ch": "CH#general" }
ScanIndexForward:  false

ScanIndexForward: false parcourt la collection triée à l'envers — le moyen bon marché d'obtenir « le plus récent d'abord » sans trier côté client.

Un jour précis avec begins_with. Parce que l'horodatage est la clé de tri et qu'il est stocké comme texte, un préfixe de date est une tranche nette :

KeyConditionExpression  ChannelRef = :ch AND begins_with(PostedAt, :day)
:ch    "CH#general"
:day   "MSG#2026-06-23"

Ça lit chaque message du 2026-06-23 et rien d'autre — DynamoDB se positionne sur le préfixe et s'arrête quand il en sort. Ça ne marche que parce que le préfixe est un vrai ancrage à gauche d'une chaîne triée par octets.

Une fenêtre précise avec BETWEEN. Pour « les messages pendant l'heure de 14:00 », une plage inclusive bat un préfixe :

KeyConditionExpression  ChannelRef = :ch AND PostedAt BETWEEN :lo AND :hi
:ch    "CH#general"
:lo    "MSG#2026-06-23T14:00:00Z"
:hi    "MSG#2026-06-23T14:59:59Z"

BETWEEN est inclusif sur les deux bornes, donc choisis tes extrémités délibérément — un décalage d'un cran ici fait silencieusement tomber ou doubler un message de bordure.

Tu peux assembler et copier n'importe laquelle de ces expressions, avec la map ExpressionAttributeValues remplie pour toi, dans le DynamoDB expression builder — pratique pour réussir la syntaxe du begins_with et du BETWEEN du premier coup.

Vois-le dans DynoTable

Lance la même condition de clé contre une vraie partition de canal et regarde la capacité consommée se mettre à jour en direct, pour confirmer que tu lis une tranche — pas toute la collection.

Le piège : confondre une condition de clé avec un filtre

L'erreur coûteuse est de sortir FilterExpression pour faire le travail d'une clé.

KeyConditionExpression:  ChannelRef = :ch
FilterExpression:        begins_with(PostedAt, :day)

Ça semble équivalent à la condition de clé begins_with ci-dessus et renvoie les mêmes lignes — mais ça lit toute la partition du canal d'abord, puis jette tout ce qui est hors du jour. Tu es facturé pour la lecture complète.

Les filtres ne réduisent jamais le coût de lecture. Ils s'exécutent après que DynamoDB a mesuré les items, la même arme à fragmentation qu'un Scan filtré. Si un prédicat peut aller dans la condition de clé, c'est là qu'il appartient.

Le correctif est en amont : si un motif d'accès ne peut pas s'exprimer comme une égalité de PK plus une plage de clé de tri, c'est un signal de modélisation. Soit remodèle la clé de tri, soit ajoute un index clé pour le motif — vois GSI vs LSI et single-table design pour savoir comment disposer les clés.

Pièges et étapes suivantes

  • La clé de partition est toujours =. Pas de plages, jamais. Si tu as besoin d'une plage à travers les partitions, tu as dépassé un seul Query.
  • Une condition de clé de tri par requête. Tu ne peux pas faire un AND de deux prédicats de clé de tri ; choisis BETWEEN ou begins_with, pas les deux.
  • Les mots réservés ont besoin d'alias. Une clé nommée Timestamp ou Name doit utiliser ExpressionAttributeNames (#ts), ou la requête échoue. (AWS : reserved words)
  • BETWEEN est inclusif. Les deux extrémités sont matchées — conçois tes bornes en conséquence.

Rédige tes conditions de clé dans l'expression builder, puis essaie DynoTable pour les exécuter contre tes propres tables et regarder la capacité qu'elles consomment réellement.

Mis à jour