在上一個單元中,您設定了對 DynamoDB 中核心實體的存取權。主要金鑰結構解決了我們的許多主要存取模式。這包括讀取或寫入單一實體的所有模式,以及獲取多個相關實體的模式,例如屬於特定使用者的所有相片。

在本單元中,您將了解如何使用反向索引,這是 DynamoDB 的常見設計模式。

次要索引是 DynamoDB 中的關鍵資料建模工具。它們讓您可以調整資料的形狀,以允許使用替代查詢模式。

反向索引是 DynamoDB 中常見的次要索引設計模式。使用反向索引,您可以建立次要索引,與資料表的主要金鑰相反。資料表的雜湊金鑰將成為索引中的範圍金鑰,資料表的範圍金鑰將成為索引中的主要金鑰。

反向索引在兩種情況下很有用。首先,反向索引對於查詢多對多關係的「另一面」很有用。您的朋友關係實體就是這種情況。透過主要金鑰結構,您可以使用針對資料表主要金鑰的查詢,來查詢特定使用者的所有關注者。新增反向索引後,您可以透過查詢反向索引,來尋找使用者所關注的使用者 (「關注」)。

此外,針對本身是一對多關係主旨的實體,反向索引對於查詢一對多關係也很有用。您可以在資料表中的回應實體中看到此情況。單一相片可以有多個回應,並且每個回應都將包括適用回應的相片、回應使用者的使用者名稱,以及回應類型。然而,由於使用者可以擁有多張相片,因此相片的主要識別符位於其範圍金鑰 -- PHOTO#<USERNAME>#<TIMESTAMP> 中。因此,您無法使用主要金鑰將回應繫結至相片。

為了完成「檢視相片和回應」存取模式,資料模型將回應實體的相片識別符置於範圍金鑰中。現在,在查詢反向索引時,您可以使用相片識別符在單一請求中選取相片及其所有回應。這會在下面的步驟 2 中顯示。

完成單元的時間:40 分鐘


  • 步驟 1:建立次要索引

    若要建立次要索引,您可以指定索引的主要金鑰,就像建立資料表時一樣。請注意,全域次要索引的主要金鑰不必是唯一的。然後,DynamoDB 將根據指定的屬性將項目複製到索引中,並且您可以像查詢資料表一樣對其進行查詢。

    反向索引是 DynamoDB 中的一種常見模式,您可以在其中建立一個次要索引,它是與資料表主要金鑰的反轉。資料表的雜湊金鑰在次要索引中指定為範圍金鑰,資料表的範圍金鑰在次要索引中指定為雜湊金鑰。

    建立次要索引類似於建立資料表。在您下載的程式碼中,名稱為 add_inverted_index.py 的檔案位於 scripts/ 目錄。該檔案內容如下所示。

    import boto3
    
    dynamodb = boto3.client('dynamodb')
    
    try:
        dynamodb.update_table(
            TableName='quick-photos',
            AttributeDefinitions=[
                {
                    "AttributeName": "PK",
                    "AttributeType": "S"
                },
                {
                    "AttributeName": "SK",
                    "AttributeType": "S"
                }
            ],
            GlobalSecondaryIndexUpdates=[
                {
                    "Create": {
                        "IndexName": "InvertedIndex",
                        "KeySchema": [
                            {
                                "AttributeName": "SK",
                                "KeyType": "HASH"
                            },
                            {
                                "AttributeName": "PK",
                                "KeyType": "RANGE"
                            }
                        ],
                        "Projection": {
                            "ProjectionType": "ALL"
                        },
                        "ProvisionedThroughput": {
                            "ReadCapacityUnits": 5,
                            "WriteCapacityUnits": 5
                        }
                    }
                }
            ],
        )
        print("Table updated successfully.")
    except Exception as e:
        print("Could not update table. Error:")
        print(e)
    

    每當在資料表或次要索引的主要金鑰中使用屬性時,都必須在 AttributeDefinitions 中定義屬性。然後,我們在 GlobalSecondaryIndexUpdates 屬性中建立新的次要索引。對於此次要索引,我們指定索引名稱、主要金鑰架構、佈建輸送量,以及我們要投射的屬性

    請注意,反向索引是設計模式的名稱,而非 DynamoDB 中的正式屬性。建立反向索引就像建立任何其他次要索引一樣。

    透過執行以下命令來建立反向索引。

    python scripts/add_inverted_index.py

    您應會在主控台中看到以下訊息:「資料表已成功更新。」

    在下一步中,我們將展示如何使用反向索引來尋找相片。

  • 第 2 步:查詢反向索引以尋找相片的回應

    現在我們已經設定了次要索引,讓我們使用它來滿足某些存取模式。

    若要使用次要索引,您僅可使用兩種 API 叫用模式︰查詢掃描。使用查詢時,必須指定雜湊金鑰,它會返回目標結果。使用掃描時,無需指定雜湊金鑰,該操作將遍歷整個資料表。除特定情況外,不建議在 DynamoDB 中使用掃描,因為它們會存取資料庫中的每個項目。若資料表中有大量資料,則掃描可能需要很長時間

    我們可以對次要索引使用查詢 API,來尋找特定相片上的所有回應。就像在前面單元中看到的那樣,您可以使用此查詢在單一命令中擷取兩種類型的實體。在此查詢中,您可以擷取相片及其回應。

    在您下載的程式碼中,名稱為 fetch_photo_and_reactions.py 的檔案位於 application/ 目錄。該指令碼的內容如下所示。

    import boto3
    
    from entities import Photo, Reaction
    
    dynamodb = boto3.client('dynamodb')
    
    USER = "david25"
    TIMESTAMP = '2019-03-02T09:11:30'
    
    
    def fetch_photo_and_reactions(username, timestamp):
        try:
            resp = dynamodb.query(
                TableName='quick-photos',
                IndexName='InvertedIndex',
                KeyConditionExpression="SK = :sk AND PK BETWEEN :reactions AND :user",
                ExpressionAttributeValues={
                    ":sk": { "S": "PHOTO#{}#{}".format(username, timestamp) },
                    ":user": { "S": "USER$" },
                    ":reactions": { "S": "REACTION#" },
                },
                ScanIndexForward=True
            )
        except Exception as e:
            print("Index is still backfilling. Please try again in a moment.")
            return False
    
        items = resp['Items']
        items.reverse()
    
        photo = Photo(items[0])
        photo.reactions = [Reaction(item) for item in items[1:]]
    
        return photo
    
    
    photo = fetch_photo_and_reactions(USER, TIMESTAMP)
    
    if photo:
        print(photo)
        for reaction in photo.reactions:
            print(reaction)
    

    fetch_photo_and_reactions 函數類似於您在應用程式中擁有的函數。該函數接受使用者名稱和時間戳記,並針對反向索引進行查詢,以尋找相片及該相片的回應。然後,它將返回的項目組合成一個相片實體,以及可以在您的應用程式中使用的多個回應實體。

    python application/fetch_photo_and_reactions.py

    您應該看到輸出相片及其五個回應。

    Photo<david25 -- 2019-03-02T09:11:30>
    Reaction<ylee -- PHOTO#david25#2019-03-02T09:11:30 -- smiley>
    Reaction<kennedyheather -- PHOTO#david25#2019-03-02T09:11:30 -- smiley>
    Reaction<jenniferharris -- PHOTO#david25#2019-03-02T09:11:30 -- +1>
    Reaction<geoffrey32 -- PHOTO#david25#2019-03-02T09:11:30 -- +1>
    Reaction<chasevang -- PHOTO#david25#2019-03-02T09:11:30 -- +1>

    請注意,次要索引需要一些時間來回填。您可能會收到一則錯誤訊息,指示正在回填。若是這樣,請在幾分鐘後重試。

    在下一步中,我們將看到如何使用反向索引,來擷取指定使用者關注的所有使用者。

  • 步驟 3:尋找關注的使用者

    在上一步中,您了解了如何使用反向索引,針對本身是一對多關係主旨的實體擷取一對多關係。在此步驟中,您將使用反向索引來擷取多對多關係的「另一面」。

    資料表中的主要金鑰可讓您尋找特定使用者的所有關注者,但不會讓您尋找某人關注的所有使用者。使用反向索引,則會翻轉 - 您可以尋找所有使用者,然後尋找特定使用者。

    在您下載的程式碼中,名稱為 find_following_for_user.py 的檔案位於 application/ 目錄。該指令碼的內容如下。

    import boto3
    
    from entities import Friendship
    
    dynamodb = boto3.client('dynamodb')
    
    USERNAME = "haroldwatkins"
    
    
    def find_following_for_user(username):
        resp = dynamodb.query(
            TableName='quick-photos',
            IndexName='InvertedIndex',
            KeyConditionExpression="SK = :sk",
            ExpressionAttributeValues={
                ":sk": { "S": "#FRIEND#{}".format(username) }
            },
            ScanIndexForward=True
        )
    
        return [Friendship(item) for item in resp['Items']]
    
    
    
    follows = find_following_for_user(USERNAME)
    
    print("Users followed by {}:".format(USERNAME))
    for follow in follows:
        print(follow)
    

    find_following_for_user 函數類似於您在應用程式中擁有的函數。該函數接受您要為其尋找關注使用者的使用者名稱。然後,該函數查詢反向索引以尋找所有朋友關係實體,其中關注使用者是指定的使用者名稱。

    透過在終端機中執行以下命令來執行此指令碼。

    python application/find_following_for_user.py

    您的主控台應輸出指定使用者名稱所關注使用者的清單︰

    Users followed by haroldwatkins:
    Friendship<chasevang -- haroldwatkins>
    Friendship<david25 -- haroldwatkins>
    Friendship<frankhall -- haroldwatkins>
    Friendship<geoffrey32 -- haroldwatkins>
    Friendship<jacksonjason -- haroldwatkins>
    Friendship<natasha87 -- haroldwatkins>
    Friendship<nmitchell -- haroldwatkins>
    Friendship<ppierce -- haroldwatkins>
    Friendship<tmartinez -- haroldwatkins>
    Friendship<vpadilla -- haroldwatkins>

    請注意,雖然這會返回使用者的所有朋友關係實體,但朋友關係實體中的資訊卻很少。它僅包括被關注使用者的使用者名稱,而不包括完整的使用者檔案。在下一個單元中,我們將討論如何使用部份規範化來有效處理此類情況。

  • 結論

    在此單元中,我們使用反向索引模式向資料表中新增次要索引。這滿足了兩種附加的存取模式:

    • 檢視相片和回應 (讀取)
    • 檢視使用者關注 (讀取)

    在擷取使用者的所有受關注使用者時,我們看到一個問題,即每個朋友關係實體都缺少受關注使用者的資訊。在下一個單元中,我們將了解如何使用部份規範化來協助實現這種存取模式。