首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用BigQuery存储写入API时,BigQuery中的可空类型

使用BigQuery存储写入API时,BigQuery中的可空类型
EN

Stack Overflow用户
提问于 2022-07-30 15:54:40
回答 1查看 178关注 0票数 0

我将从遗留的流API切换到存储写入API,下面是golang:https://github.com/alexflint/bigquery-storage-api-example中的示例

在旧代码中,我使用bigquery的空类型来指示字段可以为空:

代码语言:javascript
复制
type Person struct {
    Name bigquery.NullString `bigquery:"name"`
    Age  bigquery.NullInt64  `bigquery:"age"`
}

var persons = []Person{
    {
        Name: ToBigqueryNullableString(""), // this will be null in bigquery
        Age:  ToBigqueryNullableInt64("20"),
    },
    {
        Name: ToBigqueryNullableString("David"),
        Age:  ToBigqueryNullableInt64("60"),
    },
}

func main() {
    ctx := context.Background()

    bigqueryClient, _ := bigquery.NewClient(ctx, "project-id")
    
    inserter := bigqueryClient.Dataset("dataset-id").Table("table-id").Inserter()
    err := inserter.Put(ctx, persons)
    if err != nil {
        log.Fatal(err)
    }
}

func ToBigqueryNullableString(x string) bigquery.NullString {
    if x == "" {
        return bigquery.NullString{Valid: false}
    }
    return bigquery.NullString{StringVal: x, Valid: true}
}
func ToBigqueryNullableInt64(x string) bigquery.NullInt64 {
    if x == "" {
        return bigquery.NullInt64{Valid: false}
    }
    if s, err := strconv.ParseInt(x, 10, 64); err == nil {
        return bigquery.NullInt64{Int64: s, Valid: true}
    }
    return bigquery.NullInt64{Valid: false}
}

切换到新API之后:

代码语言:javascript
复制
var persons = []*personpb.Row{
    {
        Name: "",
        Age: 20,
    },
    {
        Name: "David",
        Age: 60,
    },
}
func main() {
    ctx := context.Background()

    client, _ := storage.NewBigQueryWriteClient(ctx)
    defer client.Close()

    stream, err := client.AppendRows(ctx)
    if err != nil {
        log.Fatal("AppendRows: ", err)
    }

    var row personpb.Row
    descriptor, err := adapt.NormalizeDescriptor(row.ProtoReflect().Descriptor())
    if err != nil {
        log.Fatal("NormalizeDescriptor: ", err)
    }

    var opts proto.MarshalOptions
    var data [][]byte
    for _, row := range persons {
        buf, err := opts.Marshal(row)
        if err != nil {
            log.Fatal("protobuf.Marshal: ", err)
        }
        data = append(data, buf)
    }

    err = stream.Send(&storagepb.AppendRowsRequest{
        WriteStream: fmt.Sprintf("projects/%s/datasets/%s/tables/%s/streams/_default", "project-id", "dataset-id", "table-id"),
        Rows: &storagepb.AppendRowsRequest_ProtoRows{
            ProtoRows: &storagepb.AppendRowsRequest_ProtoData{
                WriterSchema: &storagepb.ProtoSchema{
                    ProtoDescriptor: descriptor,
                },
                Rows: &storagepb.ProtoRows{
                    SerializedRows: data,
                },
            },
        },
    })
    if err != nil {
        log.Fatal("AppendRows.Send: ", err)
    }

    _, err = stream.Recv()
    if err != nil {
        log.Fatal("AppendRows.Recv: ", err)
    }
}

使用新的API,我需要在.proto文件中定义类型,因此我需要使用其他方法来定义可空字段,我尝试使用可选字段:

代码语言:javascript
复制
syntax = "proto3";

package person;

option go_package = "/personpb";

message Row {
  optional string name = 1;
  int64 age = 2;
}

但是,在尝试流时(不是在编译时),它会给我带来错误:BqMessage.proto: person_Row.Name: The [proto3_optional=true] option may only be set on proto3fields, not person_Row.Name

我尝试过的另一个选项是使用oneof,并像下面这样编写proto文件

代码语言:javascript
复制
syntax = "proto3";

import "google/protobuf/struct.proto";

package person;

option go_package = "/personpb";

message Row {
  NullableString name = 1;
  int64 age = 2;
}

message NullableString {
  oneof kind {
    google.protobuf.NullValue null = 1;
    string data = 2;
  }
}

然后像这样使用它:

代码语言:javascript
复制
var persons = []*personpb.Row{
    {
        Name: &personpb.NullableString{Kind: &personpb.NullableString_Null{
            Null: structpb.NullValue_NULL_VALUE,
        }},
        Age: 20,
    },
    {
        Name: &personpb.NullableString{Kind: &personpb.NullableString_Data{
            Data: "David",
        }},
        Age: 60,
    },
}
...

但是这给了我以下错误:Invalid proto schema: BqMessage.proto: person_Row.person_NullableString.null: FieldDescriptorProto.oneof_index 0 is out of range for type "person_NullableString".

我想因为api不知道如何处理其中一个类型,所以我需要以某种方式告诉它。

在使用新的存储API时,我如何使用类似bigquery.Nullable类型的东西?如有任何帮助,将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-11 05:16:51

在go中使用一个这个样本语法文件查看proto2的端到端示例。

在使用Storage时,proto3仍然是一种特殊的工具,原因如下:

  • Storage的当前行为是使用proto2语义操作
  • 目前,Storage不理解包装器类型,这是proto3最初用于通信可选存在的方式(例如,在BigQuery字段中为NULL )。正因为如此,它倾向于将包装器字段作为带有值字段的子消息(在BigQuery中,在带有单个叶字段的结构块中)。
  • 在后来的发展过程中,proto3重新引入了optional关键字作为标记存在的一种方式,但是在内部表示中,这意味着添加另一个存在标记(在后端错误中观察到的proto3_optional警告的来源)。

看起来您已经使用了一些新的贴面,特别是adapt.NormalizeDescriptor()。我怀疑如果您正在使用该模块,您可能使用的是该模块的旧版本,因为规范化代码是在这个公关中更新并作为bigquery/v1.33.0的一部分发布的。

要改进存储API的体验,使整个体验更加流畅,还有很多工作要做,但是还有很多工作要做。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73177076

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档