2022/12/07

spanner nullable data type golang

 spanner nullable data type

```
// NullableValue is the interface implemented by all null value wrapper types.
type NullableValue interface {
// IsNull returns true if the underlying database value is null.
IsNull() bool
}
```
- how to use
- difference between null,"","someValue"
- null
- spanner.NullString(valid=false)
- ""
- spanner.NullString(valid=true,value="")
- update(value[*NullableValue]->model[NullableValue])
- update target items
- value is not nil
- not update target items
- value is nil
- vo is all pointer

by the way, another orm tool,gorm , don't solve this problem well.
var user User
db.First(&user)

db.Model(&user).Updates(User{Age: 18, Name: nil})
// 実行 SQL : UPDATE `users` SET `age` = '18'  WHERE `users`.`id` = '1'

GORM のドキュメントを見ると以下のように書いてあったので、構造体で実行すると無視されてしまうようです。 When query with struct, GORM will only query with those fields has non-zero value,

that means if your field's value is 0, '', false or other zero values,

it won't be used to build query conditions,

go instal where ?

go install xxx..xxx

==>the location is ~/go/bin
so ,set the ~/go/bin to .bash_profiles 's PATH


go version

go help environment

```

GOBIN

The directory where 'go install' will install a command.

GOPATH

For more details see: 'go help gopath'.

GOROOT

The root of the go tree.

```

go help gopath

```

If the environment variable is unset, GOPATH defaults

to a subdirectory named "go" in the user's home directory

($HOME/go on Unix, %USERPROFILE%\go on Windows),

unless that directory holds a Go distribution.

Run "go env GOPATH" to see the current GOPATH.

```

go env GOPATH

==>~/go

go help install

````

usage: go install [build flags] [packages]


Install compiles and installs the packages named by the import paths.


Executables are installed in the directory named by the GOBIN environment

variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH

environment variable is not set. Executables in $GOROOT

are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.

```

golang model

https://go.dev/play/p/B7ErJjt8JqU 

modelの役割ですが、
① entity,usecase,repo,querierなど繋ぐ
② validationなどの業務ロジックの担保
②の配慮でmodelのfiledは非公開で、Setter都度Validationする、常に完璧な状態を求めて、
trade-offで使いにくく、①の役割を損する。
なので、①の役割をPrimativeなstruct(一旦voと呼ぶ)に分担する方いいかなと思う。
vo<=>modelの変換は手間ですが、複雑ではないので、ある程度自動作成できる。

-- go.mod --

module sample

go 1.19
-- model/name.go --
package model

import "errors"

type Name struct {
string
}

func NewName(v string) (Name, error) {
n := Name{v}
if err := n.validate(); err != nil {
return Name{}, err
}

return n, nil
}

func (n Name) validate() error {
if len(n.string) > 10 {
return errors.New("name length must be less than equal 10")
}
return nil
}

func (n Name) String() string {
return n.string
}
-- model/user.go --
package model

import "errors"

// entity
type User struct {
id string
name Name
age *int
}

func (u User) validate() error {
if u.id == "" {
return errors.New("id must not be empty")
}
if err := u.name.validate(); err != nil {
return err
}
return nil
}

func NewUser(vo UserVO) (User, error) {
return vo.ToModel()
}

func InitUser(vo UserVO) (User, error) {
vo.ID = "1234567890" // random
return vo.ToModel()
}

func (u User) ToVO() UserVO {
return UserVO{
ID: u.id,
Name: u.name.String(),
Age: u.age,
}
}

type UserVO struct {
ID string
Name string
Age *int
}

func (uvo UserVO) ToModel() (User, error) {
u := User{
id: uvo.ID,
name: Name{uvo.Name},
age: uvo.Age,
}
if err := u.validate(); err != nil {
return User{}, err
}
return u, nil
}
-- main.go --
package main

import (
"fmt"
"sample/model"
)

func main() {
fmt.Println("success pattern")
age := 12
//New
vo := model.UserVO{
ID: "s11600",
Name: "kume",
Age: &age}
u, err := model.NewUser(vo)
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("valid user, %+v\n", u)

//Init
vo = model.UserVO{
Name: "ku",
Age: &age}
u, err = model.InitUser(vo)
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("valid user, %+v\n", u)

//update
vo = u.ToVO()
vo.Name = "updated"
vo.Age = nil
u, err = vo.ToModel()
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("update success, %+v\n", u)



fmt.Println("--------------")
fmt.Println("failed pattern")
vo.Name = "invalid name"
u, err = vo.ToModel()
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("update failed, %+v\n", u)
}


2022/12/02

2022/11/25

2022/11/22

sql join on using


select * from join1 inner join join2 on join1.join1_id = join2.join1_id;

select * from join1 inner join join2 using(join1_id);


usingで同じ名の列は一つになり、列減少する
基本、usingの方が便利と思う。

big query 配列 uunest

 キー以外の内容はkey-valueの配列かな
RDBと違う
配列の中身を取るに UNNEST関数を使う
NESTは階層で、UNNESTは階層をなくす、平準化にする
例えば。
epは配列で
select 他のカラム、ep.key, ep.value.string_value
from xxxx, UNNEST(配列のカラム) ep

以上は検索できるが、
問題は、配列中の値の複数を条件として検索できない
ep.a=a AND ep.b=b で取れない
で、それで別々に検索して、JIONなどのSQL技で解決。
複雑なSQLになる。


SELECT GENERATE_ARRAU(11,33,2) AS some
start,end,step(default 1)

+--------------------------------------------------+
| some                                             |
+--------------------------------------------------+
| [11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33] |
+--------------------------------------------------+


GENERATE_ARRAU(1,10)


cloud spanners shard

 - record->split->node
 - どこに保存されるはprimary keyで決める
- autoIncrementの仕組みがない、順番に保存するのは特定のnodeに集中するので、望ましくない(hotspot発生)
- autoIncrement以外のIDの生成方法

uuid 1~uuid4

  - uuid1はtimestampペース、mac addressを使う
連番になる可能性
- uuid4はランダムの乱数で、分散性が高い、衝突になる可能性が0ではない

snowflake

ULID

timestampを使ってIDを生成する場合、どうしても分散率が低い。

なので、
shardId=hash(key)%N
PRIMARY KEY(shardId,分散率低い値のcolumn)
の複合キーで対応する

内部のHash関数を使う。
 - farm_fingerprint
 - など