在本單元中,您將逐步了解使用 DynamoDB 在一個 API 呼叫中擷取多個項目的一些簡單範例。您還將了解如何使用次要索引在 DynamoDB 資料表中啟用額外查詢模式。

完成單元的時間︰15 分鐘


在單元 2 中,您將了解如何使用 GetItem API 呼叫從 DynamoDB 資料表擷取單本書。此存取模式很有用,但是您的應用程式還需要能夠在單一呼叫中擷取多個項目。例如,您可能想要擷取 John Grisham 所著的所有書,因此,您可以向使用者顯示這些書。在本單元的第 1 步中,您使用 Query API 擷取特定作者的所有書。

用於取得單本書的 GetItem API 呼叫和用於擷取某個作者的所有書的 Query API 呼叫,皆使用 Books 資料表中指定的主索引鍵。但是,您可能需要啟用額外的存取模式,例如擷取特殊類別的所有書,例如歷史或傳記。Category 不是資料表主索引鍵的一部分,但是您可以建立次要索引,以允許額外的存取模式。您將在本單元的第 2 步和第 3 步建立次要索引和查詢次要索引。


  • 步驟 1.使用查詢擷取多個項目

    當您的資料表使用複合主索引鍵時,可以使用 Query API 呼叫擷取具有相同雜湊鍵的所有項目。對於您的應用程式,這意味著您可以擷取具有相同 Author 屬性的所有書。

    在 AWS Cloud9 終端機中,執行以下命令。

    $ python query_items.py

    此命令執行以下指令碼,擷取 John Grisham 所著的所有書。

    import boto3
    from boto3.dynamodb.conditions import Key
    
    # boto3 is the AWS SDK library for Python.
    # The "resources" interface allows for a higher-level abstraction than the low-level client interface.
    # For more details, go to http://boto3.readthedocs.io/en/latest/guide/resources.html
    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
    table = dynamodb.Table('Books')
    
    # When making a Query API call, you use the KeyConditionExpression parameter to specify the hash key on which you want to query.
    # You’re using the Key object from the Boto 3 library to specify that you want the attribute name ("Author")
    # to equal "John Grisham" by using the ".eq()" method.
    resp = table.query(KeyConditionExpression=Key('Author').eq('John Grisham'))
    
    print("The query returned the following items:")
    for item in resp['Items']:
        print(item)

    執行指令碼後,您應該會看到 John Grisham 的兩本書:The FirmThe Rainmaker

    $ python query_items.py
    The query returned the following items:
    {'Title': 'The Firm', 'Formats': {'Hardcover': 'Q7QWE3U2', 'Paperback': 'ZVZAYY4F', 'Audiobook': 'DJ9KS9NM'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
    {'Title': 'The Rainmaker', 'Formats': {'Hardcover': 'J4SUKVGU', 'Paperback': 'D7YF4FCX'}, 'Author': 'John Grisham', 'Category': 'Suspense'}

    在 DynamoDB 中透過單一呼叫擷取多個項目是一種常見模式,且易於使用 Query API 呼叫來完成。

  • 步驟 2.建立次要索引

    DynamoDB 可讓您建立次要索引,以說明資料表中的其他資料存取模式。次要索引是一種為 DynamoDB 資料表增加查詢靈活性的強大方式。

    DynamoDB 有兩類次要索引:全域次要索引和本機次要索引。在本部分,我們新增全域次要索引至您的 Category 屬性,這將讓您擷取特殊類別中的所有書。

    以下範例指令碼將全域次要索引新增至現有資料表。

    import boto3
    
    # Boto3 is the AWS SDK library for Python.
    # You can use the low-level client to make API calls to DynamoDB.
    client = boto3.client('dynamodb', region_name='us-east-1')
    
    try:
        resp = client.update_table(
            TableName="Books",
            # Any attributes used in your new global secondary index must be declared in AttributeDefinitions
            AttributeDefinitions=[
                {
                    "AttributeName": "Category",
                    "AttributeType": "S"
                },
            ],
            # This is where you add, update, or delete any global secondary indexes on your table.
            GlobalSecondaryIndexUpdates=[
                {
                    "Create": {
                        # You need to name your index and specifically refer to it when using it for queries.
                        "IndexName": "CategoryIndex",
                        # Like the table itself, you need to specify the key schema for an index.
                        # For a global secondary index, you can use a simple or composite key schema.
                        "KeySchema": [
                            {
                                "AttributeName": "Category",
                                "KeyType": "HASH"
                            }
                        ],
                        # You can choose to copy only specific attributes from the original item into the index.
                        # You might want to copy only a few attributes to save space.
                        "Projection": {
                            "ProjectionType": "ALL"
                        },
                        # Global secondary indexes have read and write capacity separate from the underlying table.
                        "ProvisionedThroughput": {
                            "ReadCapacityUnits": 1,
                            "WriteCapacityUnits": 1,
                        }
                    }
                }
            ],
        )
        print("Secondary index added!")
    except Exception as e:
        print("Error updating table:")
        print(e)

    建立全域次要索引與建立資料表有許多相同之處。您為索引指定名稱、索引中的屬性、索引的索引鍵結構描述和佈建輸送量 (應用程式可從資料表或索引中取用的最大容量)。每個索引的佈建輸送量與資料表中的佈建輸送量是分開的。這可讓您定義輸送量粒度以滿足您的應用程式需要。

    在終端機中執行以下命令,以新增全域次要索引。

    $ python add_secondary_index.py

    此指令碼新增稱為 CategoryIndex 的全域次要索引至您的 Books 資料表。

  • 步驟 3.查詢次要索引

    既然您擁有 CategoryIndex,您可以使用它來擷取具有特殊類別的所有書。使用次要索引查詢資料表與使用 Query API 呼叫類似。您現在將次要名稱新增至 API 呼叫。

    當您新增全域次要索引至現有資料表時,DynamoDB 會使用資料表中的現有項目填充索引。填充完所有項目後,索引可供查詢。填充時間視資料表大小而異。

    您可以使用 query_with_index.py 指令碼查詢新索引。在終端機中使用以下命令執行此指令碼。

    $ python query_with_index.py

    該命令執行以下指令碼來擷取書店中 Category Suspense 的所有書。

    import time
    
    import boto3
    from boto3.dynamodb.conditions import Key
    
    # Boto3 is the AWS SDK library for Python.
    # The "resources" interface allows for a higher-level abstraction than the low-level client interface.
    # For more details, go to http://boto3.readthedocs.io/en/latest/guide/resources.html
    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
    table = dynamodb.Table('Books')
    
    # When adding a global secondary index to an existing table, you cannot query the index until it has been backfilled.
    # This portion of the script waits until the index is in the “ACTIVE” status, indicating it is ready to be queried.
    while True:
        if not table.global_secondary_indexes or table.global_secondary_indexes[0]['IndexStatus'] != 'ACTIVE':
            print('Waiting for index to backfill...')
            time.sleep(5)
            table.reload()
        else:
            break
    
    # When making a Query call, you use the KeyConditionExpression parameter to specify the hash key on which you want to query.
    # If you want to use a specific index, you also need to pass the IndexName in our API call.
    resp = table.query(
        # Add the name of the index you want to use in your query.
        IndexName="CategoryIndex",
        KeyConditionExpression=Key('Category').eq('Suspense'),
    )
    
    print("The query returned the following items:")
    for item in resp['Items']:
        print(item)

    請注意,有一部分指令碼等候索引可供查詢。

    您應會在終端機中看到以下輸出。

    $ python query_with_index.py
    The query returned the following items:
    {'Title': 'The Firm', 'Formats': {'Hardcover': 'Q7QWE3U2', 'Paperback': 'ZVZAYY4F', 'Audiobook': 'DJ9KS9NM'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
    {'Title': 'The Rainmaker', 'Formats': {'Hardcover': 'J4SUKVGU', 'Paperback': 'D7YF4FCX'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
    {'Title': 'Along Came a Spider', 'Formats': {'Hardcover': 'C9NR6RJ7', 'Paperback': '37JVGDZG', 'Audiobook': '6348WX3U'}, 'Author': 'James Patterson', 'Category': 'Suspense'}

    查詢傳回由兩個不同作者編寫的三本書。這是一種查詢模式,難以使用資料表的主索引鍵結構描述,但在次要索引的助力下易於實作。


    在下一個單元,您將了解如何使用 UpdateItem API 更新資料表中現有項目的屬性。