Skip to content

Commit 15c9eff

Browse files
committedDec 15, 2024
refs #11 書き込みモデルを作成した
1 parent 411a825 commit 15c9eff

File tree

12 files changed

+412
-224
lines changed

12 files changed

+412
-224
lines changed
 

‎README.md

+6-14
Original file line numberDiff line numberDiff line change
@@ -67,29 +67,21 @@ import "google/protobuf/timestamp.proto";
6767
6868
message View1 {
6969
option (protobq.materialized_view) = {
70-
is_materialized_view: true
70+
base_table: "example"
7171
enable_refresh: true
7272
};
7373
7474
google.protobuf.Timestamp timestamp = 1 [(protobq.materialized_view_field) = {
75-
source: {
76-
table: "example"
77-
field: "timestamp"
78-
}
75+
origin_path: "timestamp"
7976
is_partitioned: true
8077
}];
81-
82-
string message = 3 [(protobq.materialized_view_field) = {
83-
source: {
84-
table: "example"
85-
field: "message"
86-
}
87-
}];
78+
79+
string message = 2;
8880
}
8981
```
9082

91-
### 2. Apply schema
83+
#### 2. Apply schema
9284

9385
```shell
94-
protobq apply -i example/view1.proto --project-id {YOUR_PROJECT_ID}
86+
protobq apply -i example/view1.proto --project-id {YOUR_PROJECT_ID} --dataset-id {YOUR_DATASET_ID}
9587
```

‎cmd/protobq/main.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@ func newCliApp() *cli.App {
3535
Usage: "google cloud project id",
3636
Required: true,
3737
},
38+
&cli.StringFlag{
39+
Name: "dataset-id",
40+
Usage: "bigquery dataset id",
41+
Required: true,
42+
},
3843
},
3944
Action: func(c *cli.Context) error {
40-
err := internal.Apply(context.Background(), c.String("project-id"))
45+
err := internal.Apply(context.Background(), c.String("project-id"), c.String("dataset-id"))
4146
if err != nil {
4247
return err
4348
}
@@ -59,6 +64,11 @@ func newCliApp() *cli.App {
5964
Usage: "google cloud project id",
6065
Required: true,
6166
},
67+
&cli.StringFlag{
68+
Name: "dataset-id",
69+
Usage: "bigquery dataset id",
70+
Required: true,
71+
},
6272
},
6373
Action: func(c *cli.Context) error {
6474
// TODO: implements me

‎internal/apply.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"cloud.google.com/go/bigquery"
77
)
88

9-
func Apply(ctx context.Context, projectID string) error {
9+
func Apply(ctx context.Context, projectID, datasetID string) error {
1010
cli, err := bigquery.NewClient(ctx, projectID)
1111
if err != nil {
1212
return err

‎internal/bigquery_model.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package internal
2+
3+
type BaseTableRowImpl struct {
4+
fields []BQField
5+
}
6+
7+
func (r *BaseTableRowImpl) AddField(f BQField) {
8+
r.fields = append(r.fields, f)
9+
}
10+
11+
func (r BaseTableRowImpl) ToMap() map[string]any {
12+
res := make(map[string]any)
13+
for _, f := range r.fields {
14+
currentMap := res
15+
for i, key := range f.Path {
16+
if i == len(f.Path)-1 {
17+
currentMap[key] = f.Value
18+
} else {
19+
if _, ok := currentMap[key]; !ok {
20+
currentMap[key] = make(map[string]any)
21+
}
22+
currentMap = currentMap[key].(map[string]any) //nolint:forcetypeassert
23+
}
24+
}
25+
}
26+
return res
27+
}
28+
29+
type BQField struct {
30+
Path []string
31+
Value any
32+
}

‎internal/bigquery_model_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package internal
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestBaseTableRowImpl_ToMap(t *testing.T) {
9+
type fields struct {
10+
fields []BQField
11+
}
12+
tests := []struct {
13+
name string
14+
fields fields
15+
want map[string]any
16+
}{
17+
{
18+
fields: fields{
19+
fields: []BQField{
20+
{
21+
Path: []string{"a", "b", "c"},
22+
Value: 1,
23+
},
24+
{
25+
Path: []string{"a", "d"},
26+
Value: 2,
27+
},
28+
{
29+
Path: []string{"a", "e", "f"},
30+
Value: 3,
31+
},
32+
},
33+
},
34+
want: map[string]any{
35+
"a": map[string]any{
36+
"b": map[string]any{
37+
"c": 1,
38+
},
39+
"d": 2,
40+
"e": map[string]any{
41+
"f": 3,
42+
},
43+
},
44+
},
45+
},
46+
}
47+
for _, tt := range tests {
48+
t.Run(tt.name, func(t *testing.T) {
49+
r := BaseTableRowImpl{
50+
fields: tt.fields.fields,
51+
}
52+
if got := r.ToMap(); !reflect.DeepEqual(got, tt.want) {
53+
t.Errorf("ToMap() = %v, want %v", got, tt.want)
54+
}
55+
})
56+
}
57+
}

‎internal/codegen.go

+68-22
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
11
package internal
22

33
import (
4+
"fmt"
45
"runtime/debug"
6+
"slices"
7+
"strings"
58

69
"github.com/averak/protobq/internal/protobuf/protobq"
710
"google.golang.org/protobuf/compiler/protogen"
811
"google.golang.org/protobuf/proto"
912
)
1013

11-
//goland:noinspection GoSnakeCaseUsage
1214
var (
1315
timeIdents = struct {
14-
Duration protogen.GoIdent
16+
Minute protogen.GoIdent
1517
}{
16-
Duration: protogen.GoImportPath("time").Ident("Duration"),
17-
}
18-
protoIdents = struct {
19-
GetExtension protogen.GoIdent
20-
}{
21-
GetExtension: protogen.GoImportPath("google.golang.org/protobuf/proto").Ident("GetExtension"),
22-
}
23-
internalIdents = struct {
24-
MaterializedView protogen.GoIdent
25-
E_MaterializedView protogen.GoIdent
26-
}{
27-
MaterializedView: protogen.GoImportPath("github.com/averak/protobq/internal/protobuf/protobq").Ident("MaterializedView"),
28-
E_MaterializedView: protogen.GoImportPath("github.com/averak/protobq/internal/protobuf/protobq").Ident("E_MaterializedView"),
18+
Minute: protogen.GoImportPath("time").Ident("Minute"),
2919
}
3020
protobqIdents = struct {
3121
MaterializedView protogen.GoIdent
3222
MaterializedViewOptions protogen.GoIdent
23+
BaseTableRow protogen.GoIdent
24+
internal struct {
25+
BaseTableRowImpl protogen.GoIdent
26+
BQField protogen.GoIdent
27+
}
3328
}{
3429
MaterializedView: protogen.GoImportPath("github.com/averak/protobq").Ident("MaterializedView"),
3530
MaterializedViewOptions: protogen.GoImportPath("github.com/averak/protobq").Ident("MaterializedViewOptions"),
31+
BaseTableRow: protogen.GoImportPath("github.com/averak/protobq").Ident("BaseTableRow"),
32+
internal: struct {
33+
BaseTableRowImpl protogen.GoIdent
34+
BQField protogen.GoIdent
35+
}{
36+
BaseTableRowImpl: protogen.GoImportPath("github.com/averak/protobq/internal").Ident("BaseTableRowImpl"),
37+
BQField: protogen.GoImportPath("github.com/averak/protobq/internal").Ident("BQField"),
38+
},
3639
}
3740
)
3841

@@ -69,17 +72,38 @@ func (g CodeGenerator) Gen() error {
6972
if !g.isMaterializedViewSchema(msg) {
7073
continue
7174
}
75+
ext, _ := proto.GetExtension(msg.Desc.Options(), protobq.E_MaterializedView).(*protobq.MaterializedView)
7276

7377
gf.P("var _ ", protobqIdents.MaterializedView, " = (*", msg.GoIdent.GoName, ")(nil)")
7478
gf.P()
79+
80+
gf.P("func (mv *", msg.GoIdent.GoName, ") Name() string {")
81+
gf.P(" return \"", msg.Desc.Name(), "\"")
82+
gf.P("}")
83+
gf.P()
84+
7585
gf.P("func (mv *", msg.GoIdent.GoName, ") Options() ", protobqIdents.MaterializedViewOptions, " {")
76-
gf.P(" ext, _ := ", protoIdents.GetExtension, "(mv.ProtoReflect().Descriptor().Options(), ", internalIdents.E_MaterializedView, ").(*", internalIdents.MaterializedView, ")")
7786
gf.P(" return ", protobqIdents.MaterializedViewOptions, "{")
78-
gf.P(" EnableRefresh: ext.GetEnableRefresh(),")
79-
gf.P(" RefreshInterval: ", timeIdents.Duration, "(ext.GetRefreshIntervalMinutes()) * time.Minute,")
87+
gf.P(" EnableRefresh: ", ext.GetEnableRefresh(), ",")
88+
gf.P(" RefreshInterval: ", ext.GetRefreshIntervalMinutes(), " * ", timeIdents.Minute, ",")
8089
gf.P(" }")
8190
gf.P("}")
8291
gf.P()
92+
93+
gf.P("func (mv *", msg.GoIdent.GoName, ") BaseTableRow() ", protobqIdents.BaseTableRow, " {")
94+
gf.P(" res := ", protobqIdents.internal.BaseTableRowImpl, "{}")
95+
for _, field := range msg.Fields {
96+
g.generateAddField(gf, field, nil, "res", "mv")
97+
}
98+
gf.P(" return res")
99+
gf.P("}")
100+
gf.P()
101+
102+
//for _, field := range msg.Fields {
103+
//fieldExt, _ := proto.GetExtension(field.Desc.Options(), protobq.E_MaterializedViewField).(*protobq.MaterializedViewField)
104+
//if fieldExt != nil {
105+
// gf.P(" res[\"", field.Desc.Name(), "\"] = mv.", field.GoName)
106+
//}
83107
}
84108
}
85109
return nil
@@ -100,9 +124,31 @@ func (g CodeGenerator) isMaterializedViewSchema(msg *protogen.Message) bool {
100124
return false
101125
}
102126

103-
ext, ok := proto.GetExtension(opts, protobq.E_MaterializedView).(*protobq.MaterializedView)
104-
if !ok {
105-
return false
127+
ext, _ := proto.GetExtension(opts, protobq.E_MaterializedView).(*protobq.MaterializedView)
128+
return ext != nil
129+
}
130+
131+
func (g CodeGenerator) generateAddField(gf *protogen.GeneratedFile, field *protogen.Field, parentPaths []string, result string, receiver string) {
132+
ext, _ := proto.GetExtension(field.Desc.Options(), protobq.E_MaterializedViewField).(*protobq.MaterializedViewField)
133+
if ext == nil {
134+
ext = &protobq.MaterializedViewField{}
135+
}
136+
137+
paths := parentPaths
138+
if len(ext.GetOriginPath()) > 0 {
139+
paths = append(paths, ext.GetOriginPath()...)
140+
} else {
141+
paths = append(paths, string(field.Desc.Name()))
142+
}
143+
144+
blacklist := []string{
145+
"google.protobuf.Timestamp",
146+
}
147+
if field.Message != nil && !slices.Contains(blacklist, string(field.Message.Desc.FullName())) {
148+
for _, nestedField := range field.Message.Fields {
149+
g.generateAddField(gf, nestedField, paths, result, receiver+".Get"+field.GoName+"()")
150+
}
151+
} else {
152+
gf.P(result, ".AddField(", protobqIdents.internal.BQField, "{[]string{", fmt.Sprintf(`"%s"`, strings.Join(paths, `", "`)), "}, ", receiver, ".Get", field.GoName, "()})")
106153
}
107-
return ext.GetIsMaterializedView()
108154
}

0 commit comments

Comments
 (0)