View a markdown version of this page

Utiliser des indices secondaires - AWS SDK for Java 2.x

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Utiliser des indices secondaires

La clé primaire d'une table définit l'index principal, qui détermine la manière dont DynamoDB stocke et extrait les éléments par défaut.

Les index secondaires fournissent des clés alternatives que vous pouvez utiliser dans les opérations de requête et d'analyse. Les index secondaires globaux (GSI) possèdent une clé de partition et une clé de tri facultative qui peuvent être différentes de celles de la table de base. En revanche, les index secondaires locaux (LSI) partagent la clé de partition de l'index principal mais définissent une clé de tri différente.

Définition des clés pour les index secondaires globaux et locaux

Les attributs qui participent aux index secondaires nécessitent l'@DynamoDbSecondarySortKeyannotation @DynamoDbSecondaryPartitionKey ou.

La classe suivante montre les annotations pour deux indices. Le GSI nommé SubjectLastPostedDateIndexutilise l'Subjectattribut pour la clé de partition et LastPostedDateTime pour la clé de tri. Le LSI nommé ForumLastPostedDateIndexutilise le ForumName comme clé de partition et LastPostedDateTime comme clé de tri.

Notez que l'Subjectattribut joue un double rôle. Il s'agit de la clé de tri de la clé primaire et de la clé de partition du GSI nommé SubjectLastPostedDateIndex.

classe MessageThread

La MessageThread classe peut être utilisée comme classe de données pour l'exemple de table de threads du manuel Amazon DynamoDB Developer Guide.

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; import java.util.List;
@DynamoDbBean public class MessageThread { private String ForumName; private String Subject; private String Message; private String LastPostedBy; private String LastPostedDateTime; private Integer Views; private Integer Replies; private Integer Answered; private List<String> Tags; @DynamoDbPartitionKey public String getForumName() { return ForumName; } public void setForumName(String forumName) { ForumName = forumName; } // Sort key for primary index and partition key for GSI "SubjectLastPostedDateIndex". @DynamoDbSortKey @DynamoDbSecondaryPartitionKey(indexNames = "SubjectLastPostedDateIndex") public String getSubject() { return Subject; } public void setSubject(String subject) { Subject = subject; } // Sort key for GSI "SubjectLastPostedDateIndex" and sort key for LSI "ForumLastPostedDateIndex". @DynamoDbSecondarySortKey(indexNames = {"SubjectLastPostedDateIndex", "ForumLastPostedDateIndex"}) public String getLastPostedDateTime() { return LastPostedDateTime; } public void setLastPostedDateTime(String lastPostedDateTime) { LastPostedDateTime = lastPostedDateTime; } public String getMessage() { return Message; } public void setMessage(String message) { Message = message; } public String getLastPostedBy() { return LastPostedBy; } public void setLastPostedBy(String lastPostedBy) { LastPostedBy = lastPostedBy; } public Integer getViews() { return Views; } public void setViews(Integer views) { Views = views; } @DynamoDbSecondaryPartitionKey(indexNames = "ForumRepliesIndex") public Integer getReplies() { return Replies; } public void setReplies(Integer replies) { Replies = replies; } public Integer getAnswered() { return Answered; } public void setAnswered(Integer answered) { Answered = answered; } public List<String> getTags() { return Tags; } public void setTags(List<String> tags) { Tags = tags; } public MessageThread() { this.Answered = 0; this.LastPostedBy = ""; this.ForumName = ""; this.Message = ""; this.LastPostedDateTime = ""; this.Replies = 0; this.Views = 0; this.Subject = ""; } @Override public String toString() { return "MessageThread{" + "ForumName='" + ForumName + '\'' + ", Subject='" + Subject + '\'' + ", Message='" + Message + '\'' + ", LastPostedBy='" + LastPostedBy + '\'' + ", LastPostedDateTime='" + LastPostedDateTime + '\'' + ", Views=" + Views + ", Replies=" + Replies + ", Answered=" + Answered + ", Tags=" + Tags + '}'; } }

Création de l'index

À partir de la version 2.20.86 du SDK pour Java, la createTable() méthode génère automatiquement des index secondaires à partir des annotations de classes de données. Par défaut, tous les attributs de la table de base sont copiés dans un index et les valeurs de débit allouées sont de 20 unités de capacité de lecture et 20 unités de capacité d'écriture.

Toutefois, si vous utilisez une version du SDK antérieure à la version 2.20.86, vous devez créer l'index avec le tableau, comme indiqué dans l'exemple suivant. Cet exemple crée les deux index de la Thread table. Le paramètre builder possède des méthodes pour configurer les deux types d'index, comme indiqué après les lignes de commentaire 1 et 2. Vous utilisez la indexName() méthode du générateur d'index pour associer les noms d'index spécifiés dans les annotations de classe de données au type d'index souhaité.

Ce code configure tous les attributs de table pour qu'ils apparaissent dans les deux index après les lignes de commentaires 3 et 4. De plus amples informations sur les projections d'attributs sont disponibles dans le manuel du développeur Amazon DynamoDB.

public static void createMessageThreadTable(DynamoDbTable<MessageThread> messageThreadDynamoDbTable, DynamoDbClient dynamoDbClient) { messageThreadDynamoDbTable.createTable(b -> b // 1. Generate the GSI. .globalSecondaryIndices(gsi -> gsi.indexName("SubjectLastPostedDateIndex") // 3. Populate the GSI with all attributes. .projection(p -> p .projectionType(ProjectionType.ALL)) ) // 2. Generate the LSI. .localSecondaryIndices(lsi -> lsi.indexName("ForumLastPostedDateIndex") // 4. Populate the LSI with all attributes. .projection(p -> p .projectionType(ProjectionType.ALL)) ) );

Requête à l'aide d'un index

L’exemple suivant interroge l’index secondaire global ForumLastPostedDateIndex.

Après la ligne de commentaire 2, vous créez un QueryConditionalobjet obligatoire lors de l'appel de la méthode DynamoDbIndex.query().

Vous obtenez une référence à l'index que vous souhaitez interroger après la ligne de commentaire 3 en transmettant le nom de l'index. Après la ligne de commentaire 4, vous appelez la query() méthode sur l'index qui transmet l'QueryConditionalobjet.

Vous configurez également la requête pour renvoyer trois valeurs d'attribut, comme indiqué après la ligne de commentaire 5. Si elle n'attributesToProject()est pas appelée, la requête renvoie toutes les valeurs d'attribut. Notez que les noms d'attributs spécifiés commencent par des lettres minuscules. Ces noms d'attributs correspondent à ceux utilisés dans la table, pas nécessairement aux noms d'attributs de la classe de données.

Après la ligne de commentaire 6, parcourez les résultats, enregistrez chaque élément renvoyé par la requête et stockez-le également dans la liste pour le renvoyer à l'appelant.

public class IndexScanExamples { private static Logger logger = LoggerFactory.getLogger(IndexScanExamples.class); public static List<MessageThread> queryUsingSecondaryIndices(String lastPostedDate, DynamoDbTable<MessageThread> threadTable) { // 1. Log the parameter value. logger.info("lastPostedDate value: {}", lastPostedDate); // 2. Create a QueryConditional whose sort key value must be greater than or equal to the parameter value. QueryConditional queryConditional = QueryConditional.sortGreaterThanOrEqualTo(qc -> qc.partitionValue("Forum02").sortValue(lastPostedDate)); // 3. Specify the index name to query. final DynamoDbIndex<MessageThread> forumLastPostedDateIndex = threadTable.index("ForumLastPostedDateIndex"); // 4. Perform the query using the QueryConditional object. final SdkIterable<Page<MessageThread>> pagedResult = forumLastPostedDateIndex.query(q -> q .queryConditional(queryConditional) // 5. Request three attribute in the results. .attributesToProject("forumName", "subject", "lastPostedDateTime")); List<MessageThread> collectedItems = new ArrayList<>(); // 6. Iterate through pages response and sort the items. pagedResult.stream().forEach(page -> page.items().stream() .sorted(Comparator.comparing(MessageThread::getLastPostedDateTime)) .forEach(mt -> { // 7. Log the returned items and add the collection to return to the caller. logger.info(mt.toString()); collectedItems.add(mt); })); return collectedItems; }

Les éléments suivants existent dans la base de données avant l'exécution de la requête.

MessageThread{ForumName='Forum01', Subject='Subject01', Message='Message01', LastPostedBy='', LastPostedDateTime='2023.03.28', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject02', Message='Message02', LastPostedBy='', LastPostedDateTime='2023.03.29', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject04', Message='Message04', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject08', Message='Message08', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject10', Message='Message10', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum03', Subject='Subject03', Message='Message03', LastPostedBy='', LastPostedDateTime='2023.03.30', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum03', Subject='Subject06', Message='Message06', LastPostedBy='', LastPostedDateTime='2023.04.02', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum03', Subject='Subject09', Message='Message09', LastPostedBy='', LastPostedDateTime='2023.04.05', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum05', Subject='Subject05', Message='Message05', LastPostedBy='', LastPostedDateTime='2023.04.01', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum07', Subject='Subject07', Message='Message07', LastPostedBy='', LastPostedDateTime='2023.04.03', Views=0, Replies=0, Answered=0, Tags=null}

Les instructions de journalisation des lignes 1 et 6 génèrent la sortie de console suivante.

lastPostedDate value: 2023.03.31 MessageThread{ForumName='Forum02', Subject='Subject04', Message='', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject08', Message='', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject10', Message='', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}

La requête a renvoyé des éléments avec une forumName valeur de Forum02 et une lastPostedDateTime valeur supérieure ou égale à 2023.03.31. Les résultats affichent message des valeurs avec une chaîne vide, bien que les message attributs contiennent des valeurs dans l'index. Cela est dû au fait que l'attribut message n'a pas été projeté par le code après la ligne de commentaire 5.

Utiliser des clés composites pour les index secondaires globaux (GSI)

Le SDK pour Java 2.x DynamoDB Enhanced Client prend en charge les clés composites pour les index secondaires globaux. Vous pouvez définir jusqu'à quatre attributs de clé de partition et quatre attributs de clé de tri pour un seul GSI. Cela élimine le besoin de concaténer plusieurs attributs en une seule clé de chaîne côté client.

Utilisez le order paramètre sur les @DynamoDbSecondarySortKey annotations @DynamoDbSecondaryPartitionKey et pour spécifier la position de chaque attribut dans la clé composite. Le order paramètre accepte les valeurs de l'Orderénumération (intégrée au client DynamoDB amélioré) FIRST :SECOND,, et. THIRD FOURTH

Annotez une classe de données avec des annotations clés composites

L'exemple suivant modélise une table de commandes. La table de base est utilisée orderId comme clé de partition. Un GSI nommé OrdersByStatusDateAmountutilise une clé de partition composite de customerId etstatus, et une clé de tri composite de orderDate etamount. Cette conception vous permet d'interroger les commandes d'un client filtrées par statut, date et seuil de montant.

import software.amazon.awssdk.enhanced.dynamodb.mapper.Order; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey;
@DynamoDbBean public class OrderItem { private String orderId; private String customerId; private String status; private String orderDate; private Integer amount; @DynamoDbPartitionKey public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } // First partition key attribute for the GSI. @DynamoDbSecondaryPartitionKey(indexNames = "OrdersByStatusDateAmount", order = Order.FIRST) public String getCustomerId() { return customerId; } public void setCustomerId(String customerId) { this.customerId = customerId; } // Second partition key attribute for the GSI. @DynamoDbSecondaryPartitionKey(indexNames = "OrdersByStatusDateAmount", order = Order.SECOND) public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } // First sort key attribute for the GSI. @DynamoDbSecondarySortKey(indexNames = "OrdersByStatusDateAmount", order = Order.FIRST) public String getOrderDate() { return orderDate; } public void setOrderDate(String orderDate) { this.orderDate = orderDate; } // Second sort key attribute for the GSI. @DynamoDbSecondarySortKey(indexNames = "OrdersByStatusDateAmount", order = Order.SECOND) public Integer getAmount() { return amount; } public void setAmount(Integer amount) { this.amount = amount; } }

Requête à l'aide d'un index de clé composite

Lorsque vous interrogez un index de clé composite, utilisez les addSortValue() méthodes addPartitionValue() et du générateur de clés. Ajoutez des valeurs dans le même ordre que les Order valeurs définies dans les annotations. Toutes les clés de partition sont requises. Les touches de tri sont facultatives et peuvent être fournies de gauche à droite.

Vous créez un QueryConditionalobjet pour spécifier la condition de la requête. keyEqualToÀ utiliser pour des correspondances exactes ou pour une condition clé de tri telle que sortGreaterThansortLessThan,sortBetween, ousortBeginsWith. La condition s'applique à la dernière clé de tri que vous fournissez.

Les exemples suivants interrogent le OrdersByStatusDateAmountGSI, en ajoutant progressivement des clés de tri pour affiner les résultats.

DynamoDbIndex<OrderItem> index = orderTable.index("OrdersByStatusDateAmount"); // All PENDING orders for customer-123. SdkIterable<Page<OrderItem>> allPending = index.query(q -> q .queryConditional(QueryConditional.keyEqualTo(k -> k .addPartitionValue("customer-123") .addPartitionValue("PENDING")))); // PENDING orders on a specific date. SdkIterable<Page<OrderItem>> pendingOnDate = index.query(q -> q .queryConditional(QueryConditional.keyEqualTo(k -> k .addPartitionValue("customer-123") .addPartitionValue("PENDING") .addSortValue("2025-11-04")))); // PENDING orders on a specific date with amount greater than 100. SdkIterable<Page<OrderItem>> filtered = index.query(q -> q .queryConditional(QueryConditional.sortGreaterThan(k -> k .addPartitionValue("customer-123") .addPartitionValue("PENDING") .addSortValue("2025-11-04") .addSortValue(100))));

Pour plus d'informations, consultez la section Multi-key prise en charge de l'index secondaire global dans DynamoDB.