Query sur un GSI DynamoDB en Python (boto3)

Une requête sur un GSI est un query normal plus IndexName, et AlbumTitle-index nous donne les morceaux par album — un modèle d'accès que la clé de table Artist + SongTitle ne peut pas servir. Ce qui change en Python, c'est la gestion des erreurs : les deux erreurs d'index les plus courantes échouent dans des couches différentes de boto3, et une seule des deux est attrapable par classe d'exception.

Code

import boto3

client = boto3.client("dynamodb")

paginator = client.get_paginator("query")

items = []
for page in paginator.paginate(
    TableName="Music",
    IndexName="AlbumTitle-index",
    KeyConditionExpression="#hashKey = :hashKeyValue",
    ExpressionAttributeNames={"#hashKey": "AlbumTitle"},
    ExpressionAttributeValues={":hashKeyValue": {"S": "Danzon"}},
):
    items.extend(page["Items"])

print(f"Found {len(items)} songs on the album")

except ValidationException ne compilera même pas, encore moins n'attrapera

Ajoute ConsistentRead=True à la requête ci-dessus et boto3 lève ceci, aussi bien sur l'API client que sur l'API resource :

botocore.exceptions.ClientError: An error occurred (ValidationException) when
calling the Query operation: Consistent reads are not supported on global
secondary indexes

Le gestionnaire évident est except client.exceptions.ValidationException. Il n'existe pas :

AttributeError: <botocore.errorfactory.DynamoDBExceptions object> has no
attribute ValidationException. Valid exceptions are: BackupInUseException,
... IndexNotFoundException, ... ProvisionedThroughputExceededException, ...

botocore génère les classes d'exception à partir du modèle de service, et DynamoDB en modélise 33. ValidationException est une erreur de niveau protocole et n'en fait pas partie : la seule branche fiable porte donc sur le code :

except ClientError as exc:
    if exc.response["Error"]["Code"] == "ValidationException":
        ...

L'asymétrie est réelle. Écris mal le nom de l'index et tu obtiens IndexNotFoundException, qui est modélisée et attrapable par classe. Détourne le flag de cohérence et tu obtiens une comparaison de chaînes. Ce sont deux erreurs d'index ; une seule a un type.

Le curseur porte aussi la clé de table

Le paginateur cache LastEvaluatedKey, mais il vaut la peine de savoir ce qu'il contient sur un index. Sur 300 morceaux d'un même album :

page 1: Count 271  capacity 128.5  LastEvaluatedKey ['AlbumTitle', 'Artist', 'SongTitle']
page 2: Count  29  capacity  14.0  LastEvaluatedKey []

Une clé de GSI n'est pas unique : la clé d'index seule ne peut donc pas reprendre la lecture, et DynamoDB renvoie ensemble la clé d'index et la clé de la table de base. Une pagination faite maison qui ne stocke que la clé d'index répète ou perd des éléments.

Reproduit le 2026-07-28 sur DynamoDB Local (amazon/dynamodb-local) avec boto3 1.43.58 sur CPython 3.14.6. Le texte d'erreur et les listes de clés sont la sortie de la bibliothèque elle-même.

Explication

  • IndexName ne remplace pas TableName. Les deux vont dans le même appel, et la KeyConditionExpression nomme la clé de partition de l'index (AlbumTitle), avec le même jeu d'opérateurs qu'une requête sur table.
  • Tu obtiens la projection et rien d'autre. L'index renvoie ce qu'il projette (ALL, KEYS_ONLY ou la liste INCLUDE) ; selon la référence de l'API, "global secondary index queries cannot fetch attributes from the parent table". Un attribut manquant signifie un get_item de suivi sur la clé de base, ou une projection plus large sur un nouvel index.
  • Les éléments dépourvus de la clé d'index n'apparaissent jamais — le motif d'index épars. Il garde petit un index sur status = "OPEN", et c'est aussi pourquoi une requête sur GSI peut renvoyer moins que prévu sans rien lever.
  • L'API resource prend le même IndexName : table.query(IndexName="AlbumTitle-index", KeyConditionExpression=Key("AlbumTitle").eq("Danzon")), avec des valeurs Python natives en entrée et des Decimal en sortie.
  • Une écriture sur un GSI arrive après l'écriture sur la table. La réplication est asynchrone : un chemin de lecture-après-écriture visant l'index ratera de temps en temps. Le réessayer dans une boucle serrée brûle de la capacité sans accélérer la réplication.

Le faire visuellement

Le DynamoDB Expression Builder écrit la condition de clé d'index et la map de valeurs typées en Python prêt pour boto3, y compris les enveloppes {"S": ...} que le client exige et que l'API resource interdit.

Pour pointer la même requête d'index sur tes propres tables depuis un formulaire et lire les résultats dans une grille paginée, télécharge DynoTable.

Exemples liés

Références

Construis cette requête visuellement

Compose cette opération dans le Générateur de requêtes DynamoDB gratuit — condition de clé, filtre, index, Limit, ordre de tri et boucle de pagination — et copie-la en retour comme programme exécutable SDK v3, CLI ou boto3.

Ouvrir le Générateur de requêtes DynamoDB

Travaille avec DynamoDB sans la Console

Un client de bureau rapide pour DynamoDB qui exécute le vrai SQL que DynamoDB ne peut pas — JOINs, GROUP BY, agrégations — avec édition visuelle et un agent IA sur tes propres clés Bedrock.

Essai gratuit de 30 jours, sans carte bancaire — ensuite la formule Gratuit, sans limite de durée.