web-dev-qa-db-fra.com

Clé primaire composite à 3 champs (élément unique) dans Dynamodb

J'essaie de créer un tableau pour stocker les éléments de ligne de facture dans DynamoDB. Supposons que l'élément soit défini par CompanyCode, InvoiceNumber et LineItemId, le montant et les autres détails de l'élément de campagne.

Un élément unique est défini par la combinaison des 3 premiers attributs. 2 de ces attributs peuvent être identiques pour les différents éléments. Que dois-je sélectionner comme attribut de hachage et attribut de plage?

32
HHH

Je crois que la première option offerte par @ georgeaf99 ne fonctionnera pas, car si vous le faites de cette façon, alors CompanyCode doit être unique dans le tableau. Par conséquent, un seul article serait autorisé par entreprise. Je pense que la deuxième solution est la seule vraie façon de le faire.

Vous pouvez utiliser CompanyCode comme clé de hachage, puis tous les autres champs qui se combinent pour rendre l'élément unique (dans ce cas InvoiceNumber et LineItemId) doivent être en quelque sorte combiné en une valeur (telle que la concaténation avec un délimiteur de champ), qui serait votre clé de plage. Malheureusement, c'est un peu moche, mais c'est la nature d'une base de données NoSQL comme DynamoDB. Cependant, il vous permettra de stocker avec succès les enregistrements avec l'unicité correcte. Lors de la lecture des enregistrements, si vous ne souhaitez pas analyser le champ combiné dans ses parties individuelles, vous devrez ajouter des champs séparés supplémentaires pour InvoiceNumber et LineItemID.

Si vous n'avez pas un grand nombre de factures par entreprise, vous pouvez interroger uniquement avec la clé de hachage et effectuer le filtrage côté client. Si vous avez un grand nombre de factures par entreprise et devez pouvoir interroger uniquement les articles pour une seule facture, je créerais un index secondaire sur CompanyCode et InvoiceNumber.

25
JoeMjr2

Quelques introductions

Pour plus d'efficacité, je proposerais une conception totalement différente. Avec les bases de données NoSQL (et DynamoDB n'est pas différent), nous devons toujours considérer les modèles d'accès en premier. De plus, si possible, nous devons nous efforcer d'adapter toutes nos données dans la même table et plusieurs index. D'après ce que nous avons d'OP et ses commentaires, voici les deux modèles d'accès:

  1. Pour une entreprise X, obtenez la facture complète Y (y compris tous les articles ou la gamme d'articles) [sur la base de cela commentaire ]
  2. Obtenez toutes les factures de la société X [sur la base de cela commentaire ]

Nous nous demandons maintenant ce qu'est une bonne clé primaire? Traduit pour se demander ce qu'est une bonne clé de partition (PK) et qu'est-ce qu'une bonne clé de tri (SK) et quels index secondaires devons-nous créer et de quel type (local ou global)? Quelques rappels:

  • La clé primaire peut être sur une colonne ou composite
  • La clé primaire composite se compose de la clé de partition et de la clé de tri
  • La clé de partition est utilisée comme entrée pour la fonction de hachage qui déterminera la partition des éléments
  • La clé de tri peut également être composite, ce qui nous permet de modéliser les relations un-à-plusieurs dans DynamoDB comme indiqué dans l'un des liens de commentaires: https://docs.aws.Amazon.com/amazondynamodb/latest/developerguide /bp-sort-keys.html
  • Lors de la création d'une requête sur la table ou l'index, vous devez toujours utiliser l'opérateur '=' sur la clé de partition
  • Lorsque vous interrogez des plages sur la clé de tri, vous avez l'option pour KeyConditionExpression qui vous fournit ensemble d'opérateurs pour le tri et tout le reste (l'un d'eux étant la fonction begins_with (a, substr))
  • Vous êtes également autorisé à utiliser FilterExpression si vous avez besoin d'affiner davantage les résultats de la requête (filtre sur les attributs projetés)
  • Les index secondaires locaux (LSI) ont la même clé de partition mais une clé de tri différente de votre table d'origine et vous donnent une vue différente de vos données, organisées selon une autre clé de tri
  • Les index secondaires globaux (GSI) ont une clé de partition et une clé de tri différentes de votre table d'origine et vous offrent une vue complètement différente des données
  • Tous les éléments avec la même clé de partition sont stockés ensemble et, pour les clés primaires composites, sont classés par valeur de clé de tri. DynamoDB divise les partitions par clé de tri si la taille de la collection dépasse 10 Go.

Retour à la modélisation

Il est évident que nous avons affaire à plusieurs entités qui doivent être modélisées et s'inscrire dans la même table. Pour satisfaire à la condition que la clé de partition soit unique sur la table, CompanyCode est une clé de partition naturelle - je m'assurerais donc que c'est unique. Sinon, vous devez vous demander comment modéliser le deuxième modèle d'accès?

En supposant que nous avons établi l'unicité sur le CompanyCode simplifions et disons qu'il se présente sous la forme d'un e-mail (ou pourrait être un domaine ou juste un code, mais j'utiliserai l'email pour la démonstration).

  • La relation entre la société et les factures est toujours 1: plusieurs.
  • La relation entre la facture et les articles est toujours 1: plusieurs.

Je propose le design comme dans l'image ci-dessous: Proposed design in DynamoDB

  • PK étant CompanyCode et SK étant InvoiceNumber peut stocker tous les attributs de cette facture pour cette entreprise.
  • Rien ne m'empêche d'ajouter également un enregistrement où le SK est Customer ce qui me permet de stocker tous les attributs de l'entreprise.
  • Avec GSI1, nous allons créer une recherche inversée où GSI1PK est mes tables SK (InvoiceNumber) et mon GSI1SK est mes tables PK (CompanyCode).
  • J'utilise la même table pour stocker les éléments de campagne avec PK étant LineItemId et SK étant CompanyCode (toujours unique)
  • Pour les éléments d'entité Item, mon GSI1PK est toujours InvoiceNumber et mon GSI1SK est LineItemId, ce qui correspond aux tables PK.

Maintenant, les modèles d'accès pris en charge avec ceci:

  • Si je veux obtenir la facture Y pour la société X et tous les articles (modèle d'accès 1): Recherchez la table où CompanyCode=X Et utilisez KeyConditionExpression avec l'opérateur = Sur la clé de tri InvoiceNumber. Si je veux obtenir tous les articles liés à cette facture, je projetterai l'attribut Items à l'aide de ProjectionExpression.
  • En récupérant tous les articles avec la requête précédente pour la société X et la facture Y, je peux maintenant exécuter BatchGetItem appel API (en utilisant ma clé composite unique LineItemId+CompanyCode) Sur la table pour obtenir tous les articles appartenant à ce particulier facture de ce client particulier. (cela vient avec quelques contraintes de BatchGetItem API )
  • Pour prendre en charge le modèle d'accès 2, je vais faire une requête avec CompanyCode=X Sur PK et utiliser KeyConditionExpression sur la SK avec begins_with (a, substr) fonction/opérateur pour obtenir uniquement les factures de la société X et pas les métadonnées sur cette entreprise. Cela me donnera toutes les factures pour une entreprise/un client donné.
  • De plus, avec GSI1 ci-dessus, pour tout InvoiceNumber je peux facilement sélectionner tous les éléments de ligne qui appartiennent à cette facture particulière. N'OUBLIEZ PAS: Les valeurs clés d'un index secondaire global n'ont pas besoin d'être uniques - donc dans mon GSI1 j'aurais pu avoir facture facilement_1 -> (item_1, item_2) puis une autre facture_1 -> (item_1, item_2) mais la différence entre deux articles en GSI serait dans la SK (elle serait associée à différents CompanyCode (mais pour démonstration) J'ai utilisé facture_1 et facture_2).
18
azec-pdx

Comme je suis sûr que vous avez compris que vous ne pouvez pas avoir plus de deux attributs de votre clé primaire (hachage + plage). Ainsi, selon le type de requêtes que vous effectuerez et la taille de vos données, vous pouvez structurer votre table de différentes manières.

(Optimisé pour le type de requête que vous avez mentionné ci-dessus: uniquement CompanyCode et tous les 3)

Meilleur sol'n pour les ensembles de données de petite/moyenne taille:

  • Clé de hachage: CompanyCode
  • Exécutez la requête en utilisant uniquement CompanyCode, puis filtrez vos résultats sur les deux autres attributs

Solution optimale pour les grands ensembles de données:

  • Clé de hachage: CompanyCode
  • Clé de plage: InvoiceNumber + LineItemId
  • Cela vous permet d'interroger uniquement sur un index, mais la structure de la table est assez moche
8
georgeaf99