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

How to serialize `LastEvaluatedKey` from DynamoDB's Golang SDK?

在 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 编码以获得令牌),但这样做是不可逆的。 LastEvaluatedKey 是一个 map[string]types.AttributeValuetypes.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. Unfortunately the golang SDK doesn't support this.

我想我可以为 AttributeValue 类型编写自己的序列化器/反序列化器,但这比这个项目应得的要多。

有没有人找到通用的方法来做到这一点?

好的,我想通了。

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 总是平坦的,所以它适用于这个用例。