如何从DynamoDB的Golang SDK序列化'LastEvaluatedKey'?

vcirk6k6  于 2022-12-16  发布在  Go
关注(0)|答案(2)|浏览(253)

在Golang中使用DynamoDB时,如果对query的调用有更多结果,它将在QueryOutput上设置LastEvaluatedKey,然后您可以将其作为ExclusiveStartKey传递到对query的下一个调用,以从您停止的地方继续。
当值保留在Golang中时,这非常有效。但是,我正在编写一个分页的API端点,所以我想序列化这个键,这样我就可以将它作为分页令牌交给客户端。类似这样,其中something是一个神奇的包,它可以完成我想要的任务:

type GetDomainObjectsResponse struct {
  Items     []MyDomainObject `json:"items"`
  NextToken string           `json:"next_token"`
}
  
func GetDomainObjects(w http.ResponseWriter, req *http.Request) {
  // ... parse query params, set up dynamoIn ...

  dynamoIn.ExclusiveStartKey = something.Decode(params.NextToken)

  dynamoOut, _ := db.Query(dynamoIn)

  response := GetDomainObjectsResponse{}
  dynamodbattribute.UnmarshalListOfMaps(dynamoOut.Items, &response.Items)

  response.NextToken := something.Encode(dynamoOut.LastEvaluatedKey)
  
  // ... marshal and write the response ...
}

(请原谅上面的任何错别字,这是我为了隔离问题而快速编写的代码的玩具版本)
因为我需要支持具有不同搜索模式的多个端点,所以我希望有一种方法可以生成不依赖于特定搜索键的分页令牌。
问题是,我还没有找到一个干净和通用的方法来序列化LastEvaluatedKey。您可以将其直接封送到JSON(然后例如base64编码它以获得令牌),但这样做是不可逆的。LastEvaluatedKeymap[string]types.AttributeValue,而types.AttributeValue是接口,因此虽然json编码器可以读取它,但它不能写入它。
例如,下面的代码在panic: json: cannot unmarshal object into Go value of type types.AttributeValue时出现异常。

lastEvaluatedKey := map[string]types.AttributeValue{
    "year":  &types.AttributeValueMemberN{Value: "1993"},
    "title": &types.AttributeValueMemberS{Value: "Benny & Joon"},
}

bytes, err := json.Marshal(lastEvaluatedKey)
if err != nil {
  panic(err)
}

decoded := map[string]types.AttributeValue{}
err = json.Unmarshal(bytes, &decoded)
if err != nil {
  panic(err)
}

我喜欢的是一种直接使用DynamoDB风格的JSON的方法,比如what you get when you run aws dynamodb query on the CLI
我想我可以为AttributeValue类型编写自己的序列化器/反序列化器,但这比本项目所需的工作量要大。
有没有人找到一个通用的方法来做到这一点?

vsmadaxz

vsmadaxz1#

好吧,我想明白了。

type GetDomainObjectsResponse struct {
  Items     []MyDomainObject `json:"items"`
  NextToken string           `json:"next_token"`
}
  
func GetDomainObjects(w http.ResponseWriter, req *http.Request) {
  // ... parse query params, set up dynamoIn ...

  eskMap := map[string]string{}
  json.Unmarshal(params.NextToken, &eskMap)
  esk, _ = dynamodbattribute.MarshalMap(eskMap)
  dynamoIn.ExclusiveStartKey = esk

  dynamoOut, _ := db.Query(dynamoIn)

  response := GetDomainObjectsResponse{}
  dynamodbattribute.UnmarshalListOfMaps(dynamoOut.Items, &response.Items)

  lek := map[string]string{}
  dynamodbattribute.UnmarshalMap(dynamoOut.LastEvaluatedKey, &lek)
  response.NextToken := json.Marshal(lek)
  
  // ... marshal and write the response ...
}

(再次这是我的真实的解决方案匆忙转移回玩具问题,所以请原谅任何错别字)
正如@buraksurdar所指出的,attributevalue.Unmarshal需要一个inteface{},结果除了一个具体的类型之外,你还可以传入一个map[string]string,它就可以工作了。
我相信如果AttributeValue不是平面的话,这个方法是行不通的,所以这不是一个通用的解决方案。但是我的理解是,从Query调用返回的LastEvaluatedKey总是平面的,所以它适用于这个用例。

polhcujo

polhcujo2#

受Dan的启发,下面是一个与base64进行序列化和反序列化的解决方案

package dynamodb_helpers

import (
    "encoding/base64"
    "encoding/json"

    "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func Serialize(input map[string]types.AttributeValue) (*string, error) {
    var inputMap map[string]interface{}
    err := attributevalue.UnmarshalMap(input, &inputMap)
    if err != nil {
        return nil, err
    }
    bytesJSON, err := json.Marshal(inputMap)
    if err != nil {
        return nil, err
    }
    output := base64.StdEncoding.EncodeToString(bytesJSON)
    return &output, nil
}

func Deserialize(input string) (map[string]types.AttributeValue, error) {
    bytesJSON, err := base64.StdEncoding.DecodeString(input)
    if err != nil {
        return nil, err
    }
    outputJSON := map[string]interface{}{}
    err = json.Unmarshal(bytesJSON, &outputJSON)
    if err != nil {
        return nil, err
    }

    return attributevalue.MarshalMap(outputJSON)
}

相关问题