Golang 中 DynamoDB 索引键的空字符串

Empty string for DynamoDB index key in Golang

我一直在使用 Protobuf 来定义与 DynamoDB 通信时要使用的对象。到目前为止,此类对象看起来像这样:

type Record struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Id               string        `protobuf:"bytes,2,opt,name=id,json=id,proto3" dynamodbav:"id,omitempty" json:"id,omitempty"`
    Name             string        `protobuf:"bytes,3,opt,name=name,json=name,proto3" dynamodbav:"name,omitempty" json:"name,omitempty"`
    OrgId            string        `protobuf:"bytes,4,opt,name=org_id,json=orgId,proto3" dynamodbav:"org_id,omitempty" json:"org_id,omitempty"`
    AccountId        string        `protobuf:"bytes,7,opt,name=account_id,json=accountId,proto3" dynamodbav:"account_id,omitempty" json:"account_id,omitempty"`
    Address          string        `protobuf:"bytes,9,opt,name=address,proto3" dynamodbav:"address,omitempty" json:"address,omitempty"`
    BillingTitle     string        `protobuf:"bytes,10,opt,name=billing_title,json=billingTitle,proto3" dynamodbav:"billing_title,omitempty" json:"billing_title,omitempty"`
    Language         string        `protobuf:"bytes,11,opt,name=language,proto3" dynamodbav:"language,omitempty" json:"language,omitempty"`
    Personal         string        `protobuf:"bytes,12,opt,name=personal,proto3" dynamodbav:"personal,omitempty" json:"personal,omitempty"`
    Phonenumber      string        `protobuf:"bytes,13,opt,name=phonenumber,proto3" dynamodbav:"phonenumber,omitempty" json:"phonenumber,omitempty"`
    Postalcode       string        `protobuf:"bytes,14,opt,name=postalcode,proto3" dynamodbav:"postalcode,omitempty" json:"postalcode,omitempty"`
    ProjectId        string        `protobuf:"bytes,15,opt,name=project_id,json=projectId,proto3" dynamodbav:"project_id,omitempty" json:"project_id,omitempty"`
    Remarks          string        `protobuf:"bytes,16,opt,name=remarks,proto3" dynamodbav:"remarks,omitempty" json:"remarks,omitempty"`
}

我将这些对象添加到的 table 有一个全局二级索引,其中 project_id 作为排序键,但并不总是填充此字段。所以,当我去对 table 进行 PutObject 操作时,像这样:

record := Record {
    Id: "A000",
    Name: "Some Record",
    OrgId: "O000"
}

attrs, err := dynamodbattribute.MarshalMap(&record)
if err != nil {
    panic(err)
}

if _, err := conn.PutItem(&dynamodb.PutItemInput{
    TableName: aws.String(tableName),
    Item: attrs
}); err != nil {
    panic(err)
}

这段代码不会崩溃,我会在 DynamoDB 中看到值。但是,从所有 dynamodbav 标记中删除 omitempty 子句后,我注意到这现在失败并出现以下错误:

ValidationException: Invalid attribute value type
        status code: 400, request id: 6a626232-fcd4-4999-afe4-3df5769ce1b2

经过进一步调查,我发现该对象序列化为:

map[account_id:{
  NULL: true
} address:{
  NULL: true,
} billing_title:{
  NULL: true
} id:{
  S: "A000"
} name:{
  S: "Some Record"
} language:{
  NULL: true
} org_id:{
  S: "O000"
} personal:{
  NULL: true
} phonenumber:{
  NULL: true
} postalcode:{
  NULL: true
} project_id:{
  NULL: true
} remarks:{
  NULL: true
}

omitempty 未包含但序列化为:

map[id:{
  S: "A000"
} name:{
  S: "Some Record"
} org_id:{
  S: "O000"
}

当包含 omitempty 时。

我希望前者具有默认值而不是 NULL: true,但我没有看到任何序列化选项。有没有办法在不实现 DynamoDB 封送拆收器接口的情况下做到这一点?

经过一番调查,我找到了 Marshal 函数的源代码 here。据此,有一个名为 NullEmptyString 的字段,我可以设置它来告诉编码器我希望将空字符串作为字符串发送,而不是作为空属性发送。但是,如果我设置了这个,我就不能直接使用 MarshalMarshalMapMarshalList。所以,我复制了它们并写了我自己的:

func MarshalMap(in interface{}) (map[string]*dynamodb.AttributeValue, error) {
    av, err := getEncoder().Encode(in)
    if err != nil || av == nil || av.M == nil {
        return map[string]*dynamodb.AttributeValue{}, err
    }

    return av.M, nil
}

func getEncoder() *dynamodbattribute.Encoder {
    encoder := dynamodbattribute.NewEncoder()
    encoder.NullEmptyString = false
    return encoder
}