Skip to content

Commit e99e7c6

Browse files
committed
refs #11 書き込みモデルを作成した
1 parent 411a825 commit e99e7c6

File tree

12 files changed

+425
-224
lines changed

12 files changed

+425
-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

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

internal/bigquery_model_test.go

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

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+
InsertDTO protogen.GoIdent
24+
internal struct {
25+
NewInsertDTOImpl 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+
InsertDTO: protogen.GoImportPath("github.com/averak/protobq").Ident("InsertDTO"),
32+
internal: struct {
33+
NewInsertDTOImpl protogen.GoIdent
34+
BQField protogen.GoIdent
35+
}{
36+
NewInsertDTOImpl: protogen.GoImportPath("github.com/averak/protobq/internal").Ident("NewInsertDTOImpl"),
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, ") InsertDTO() ", protobqIdents.InsertDTO, " {")
94+
gf.P(" res := ", protobqIdents.internal.NewInsertDTOImpl, "(\"", ext.GetBaseTable(), "\", nil)")
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)