GORM bulk insert
GORM
- golang の ORM
- 直感的に扱えはするので◯
gorm.io
GORM で bulk insert
これ
gorm.io大量のレコードを効率的に挿入するには、Createメソッドにスライスを渡します。 GORMはすべてのデータを挿入し、主キーの値をバックフィルするための単一のSQL文を生成し、フックメソッドも起動されます。
GORMのCreateメソッドは insert に該当するのですが、そこに Slice を入れるだけで勝手にBulkになるのはありがたい。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}} db.Create(&users) for _, user := range users { user.ID // 1,2,3 }
CreateInBatches
というのもあり。
実行するサイズを指定して insert ができるっぽい。
大量データを扱うときは必要になるかも。ちゃんと用意されててすごい。
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}} // batch size 100 db.CreateInBatches(users, 100)
Golang for loop
よく使ってるやつ
for index, value := range []string{"a", "b", "c"} { fmt.Println(index, value) }
map を回すとき
for key, value := range map[string]string{"a": "a1", "b": "b1", "c": "c1"} { fmt.Println(key, value) }
あとは continue
, break
は他の言語同様存在する
私は使う場面に遭遇してないが、goto文もあるらしい
i := int64(0) loop: if i < 10 { i++ goto loop }
ついでに無限loopをしたいなら
for { fmt.Println("hogehoge") }
こんな感じ。
Golang gzip response → struct
gzip圧縮されたresponseをhoge struct にいれるまで
import ( "net/http" "compress/gzip" "bytes" "encoding/json" ) func main() { // どこかへGetRequest req, err := http.NewRequest(http.MethodGet, "https://hogehoge/foo/bar.jp", nil) if err != nil { panic(err) } req.Header.Set("Accept-Encoding", "deflate, gzip") client := new(http.Client) resp, err = client.Do(req) if err != nil { panic(err) } if resp.StatusCode != http.StatusOK { panic("なにかしら失敗") } reader, _ = gzip.NewReader(resp.Body) wb := new(bytes.Buffer) io.Copy(wb, reader) var bean *hoge err = json.Unmarshal(wb.Bytes(), &bean) if err != nil { panic(err) } println(bean) } type hoge struct { ... }
Accept-Encoding
Accept-Encoding ヘッダの目的は、クライアントがサポートしている圧縮方式をサーバーに教えることです。 サーバーは送られてきた Accept-Encoding ヘッダの値を見て、クライアントに合う圧縮アルゴリズムでコンテンツを圧縮して返却してあげれば良いというわけです。
今回は gzip 圧縮してresponseちょうだい ということなので、 Accept-Encoding : deflate, gzip
elementary OS 6 'Odin' が出たらしい
おすすめ記事になんか出てきた。
elementary OS を使っているので帰ったら早速DLしよう。
betanews.com
英語は読めないのでDeepLで翻訳
なぜ、デスクトップパソコンのユーザーは、LinuxベースのOSを使わないのでしょうか?ソフトウェアの互換性はさておき、変化や未知への恐れがあるか> らだ。ユーザーがWindowsから乗り換えるには、かなり単純な作業である必要があります。以前は、Linuxディストリビューションをインストールするだ> けでも大変な作業でした。しかし最近では、ディストリビューションにもよりますが、Windowsをインストールするよりも早くて簡単な場合もあります。
Linux初心者にとって、インストールしたディストリビューションは、直感的なデスクトップ環境で使いやすいものでなければなりません。私はGNOMEの大ファンですが、当然のことながら、すべての人(特にLinux初心者)がGNOMEを好むわけではありません。あるLinuxベースのデスクトップOSは、す> べての人が利用しやすいことに重点を置いています--elementary OSです。このディストリビューションは洗練されており、使いやすさを重視しています。熟練者にも初心者にもお勧めのディストリビューションです。本日、コードネーム「Odin」と呼ばれるelementary OS 6がダウンロード可能になり、エキサイティングな変更が加えられました。
「elementary OS 6は、最先端のサンドボックス技術を活用して、プライバシーとセキュリティの保護を技術的なレベルで実現しています。OS 6では、> すべてのAppCenterアプリがFlatpakとしてパッケージ化され、配布されます。Flatpakは最新のコンテナ形式で、アプリ同士や機密データを隔離することができます。OS 6では、すべてのAppCenterアプリがFlatpakとして配布されます。さらに、elementary OS 6ではポータルを利用して、アプリ同士の相互作用やデータを管理することができます。アプリは、明確に定義された方法で、明示的に許可を求めなければなりません。
さらにBlaede氏は、「Elementary OS 6がFlatpaksに全面的に対応したことで、AppCenterもそれに合わせてアップデートされました。サードパーティ> のAppCenterアプリは、これまでもレビュー、承認、キュレーションを行ってきましたが、プライバシーとセキュリティをさらに強化するために、サンドボックス化されたFlatpakとしても配布されるようになりました。リストからアプリがインストールされると、AppCenterでは完了時にアプリ内通知が表> 示されるようになり、より早くアプリを開くことができるようになりました。アプリのヘッダーのデザインが改善され、ボタンのコントラストも改善され> ました。また、AppCenterの通知では、インストールされたアプリやアップデートの言語やコンテクストバッジが改善され、より豊かなコンテキストが提供されるようになりました。"
elementary OS 6のアップデートについては、こちらをご覧ください。変更点のリストは非常に広範囲にわたっており、開発者がこのOSに非常に情熱を> 持っていることがわかるので、ぜひ一読されることをお勧めします。ISOをダウンロードしたい場合は、ここから入手できます。プロのアドバイス:開発> 者はダウンロードに寄付を求めていますが、0ドルを選択することもできます。
ふむ。セキュリティ面が強化された…的な…?
とりあえず開発者のblog
blog.elementary.io
Twitterでもめちゃ言ってる
The latest version of the thoughtful, capable, and ethical replacement for Windows and macOS is here. elementary OS 6 empowers you to be in control and express yourself, continues to innovate with new features, and is both easier to get and more inclusive. https://t.co/wJJ4KGVINF
— elementary (@elementary) August 10, 2021
Golang で日付操作(UTC→JST)
UTC → JSTに変えたくなった。
やったこと
package main import ( "fmt" "time" ) const ( ISO8601Format = "2006-01-02T15:04:05Z" ) func main() { // UTC value := "2021-08-10T13:41:32Z" jst, _ := time.LoadLocation("Asia/Tokyo") r, _ := time.ParseInLocation(ISO8601Format, value, jst) fmt.Println(r) // 2021-08-10 13:41:32 +0900 JST }
locationは変わっているが、+9時間されてない。
本当はこうなってほしい。
2021-08-10 22:41:32 +0900 JST
こう変えた
func main() { value := "2021-08-10T13:41:32Z" jst, _ := time.LoadLocation("Asia/Tokyo") r, _ := time.Parse(ISO8601Format, value) r2 := r.In(jst) fmt.Println(r) // 2021-08-10 13:41:32 +0000 UTC fmt.Println(r2) // 2021-08-10 22:41:32 +0900 JST }
意図通りにはなった。
が time.In は何をしているのか...
time.In は何をしているのか
// In returns a copy of t representing the same time instant, but // with the copy's location information set to loc for display // purposes. // // In panics if loc is nil. func (t Time) In(loc *Location) Time { if loc == nil { panic("time: missing Location in call to Time.In") } t.setLoc(loc) return t }
ほぉ。
Inは、同じ時間の瞬間を表すtのコピーを返しますが、表示のためにコピーの位置情報をlocに設定します。 locがnilの場合、Inはパニックになります。
Copyした time に位置情報を設定しているらしい。
ただ、中身は time.setLoc をしているだけ。
time.setLoc は何をしているのか
// setLoc sets the location associated with the time. func (t *Time) setLoc(loc *Location) { if loc == &utcLoc { loc = nil } t.stripMono() t.loc = loc }
ロケーションを設定しているだけのよう…
setLocは、時間に関連付けられたロケーションを設定します。
time.stripMono とは…
time.stripMono は何をしているのか
// stripMono strips the monotonic clock reading in t. func (t *Time) stripMono() { if t.wall&hasMonotonic != 0 { t.ext = t.sec() t.wall &= nsecMask } }
stripMonoは、tに読み込まれた単調なクロックをストリップします。
なるほどね。全然わからん。
動かしながらみる
Parseした段階ではこう
r, _ := time.Parse(ISO8601Format, value)
Parseした際のデフォルトTimezoneはUTCなのでこの段階では +0000
setLoc にきた段階ではこう
func (t *Time) setLoc(loc *Location) {
変わらない。
stripMono に来た段階でも
if t.wall&hasMonotonic != 0
この分岐に入らず終わったので変わらなかった
時間が変わったのは setLoc のここ
t.loc = loc
Copy した(?) time に location を追加したところで +0900 された
う〜ん・・・
とりあえず time の loc に jst 入れると変わる。
time.loc
// loc specifies the Location that should be used to // determine the minute, hour, month, day, and year // that correspond to this Time. // The nil location means UTC. // All UTC times are represented with loc==nil, never loc==&utcLoc. loc *Location
locは、このTimeに対応する分、時間、月、日、年を決定するために使用すべきLocationを指定します。nilのLocationはUTCを意味します。全てのUTCタイムはloc==nilで表され、決してloc==&utcLocではありません。
へぇ。location == nil は UTC と…
そもそも time.ParseInLocation で jst 設定したときはなぜ変わらなかった??
jst, _ := time.LoadLocation("Asia/Tokyo") r, _ := time.ParseInLocation(ISO8601Format, value, jst)
time.ParseInLocation は何をしているのか
// ParseInLocation is like Parse but differs in two important ways. // First, in the absence of time zone information, Parse interprets a time as UTC; // ParseInLocation interprets the time as in the given location. // Second, when given a zone offset or abbreviation, Parse tries to match it // against the Local location; ParseInLocation uses the given location. func ParseInLocation(layout, value string, loc *Location) (Time, error) { return parse(layout, value, loc, loc) }
実質 time.parse している
time.parse は何をしているのか
pase 関数は長すぎて載せないが、今回の例だとここ
return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
defaultLocation は
jst, _ := time.LoadLocation("Asia/Tokyo")
なので、jst がセットされている。なのになぜ変わらないのだろう。
Date() のなかでも loc は入っている。
そして Date() のなかで setLoc() もしている
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time { if loc == nil { panic("time: missing Location in call to Date") } ... t := unixTime(unix, int32(nsec)) t.setLoc(loc) return t }
腑に落ちない…
golang nil map で panic
よくやらかすやつ
var testMap map[int64]string testMap[100] = "あいうえお" testMap[101] = "かきくけこ" // panic: assignment to entry in nil map
なぜ?
初期化(メモリの確保)をしていないから
言われれば当然…
解決策
マップリテラルで初期化
2行になるから自分はやらない。
var testMap map[int64]string testMap = map[int64]string{} testMap[100] = "あいうえお" testMap[101] = "かきくけこ" fmt.Println(testMap) // map[100:あいうえお 101:かきくけこ]
makeで初期化
こっちの方が馴染みある
testMap := make(map[int64]string) // testMap = map[int64]string{} testMap[100] = "あいうえお" testMap[101] = "かきくけこ" fmt.Println(testMap) // map[100:あいうえお 101:かきくけこ]
make の方は第2引数にキャパシティを指定できる。指定しない場合は適当なキャパシティが自動で確保される。
これはマップリテラルでも同様らしい。
makeの説明にはこう書いている
// The make built-in function allocates and initializes an object of type // slice, map, or chan (only). Like new, the first argument is a type, not a // value. Unlike new, make's return type is the same as the type of its // argument, not a pointer to it. The specification of the result depends on // the type: // Slice: The size specifies the length. The capacity of the slice is // equal to its length. A second integer argument may be provided to // specify a different capacity; it must be no smaller than the // length. For example, make([]int, 0, 10) allocates an underlying array // of size 10 and returns a slice of length 0 and capacity 10 that is // backed by this underlying array. // Map: An empty map is allocated with enough space to hold the // specified number of elements. The size may be omitted, in which case // a small starting size is allocated. // Channel: The channel's buffer is initialized with the specified // buffer capacity. If zero, or the size is omitted, the channel is // unbuffered. func make(t Type, size ...IntegerType) Type ...
英語読めないのでDeepL変換。
make組み込み関数は、slice、map、chan(のみ)のいずれかの型のオブジェクトを割り当て、初期化します。newと同様に、第1引数は値ではなく型です。newとは異なり、makeの戻り値の型は引数の型と同じで、そのポインタではありません。
The make built-in function allocates and initializes an object of type slice, map, or chan (only). Like new, the first argument is a type, not a value. Unlike new, make's return type is the same as the type of its argument, not a pointer to it.
マップ: 空のマップは、指定された数の要素を格納するのに十分なスペースが割り当てられます。サイズを省略することもでき、その場合は小さなスタートサイズが割り当てられます。
Map: An empty map is allocated with enough space to hold the specified number of elements. The size may be omitted, in which case a small starting size is allocated.
"適当なキャパシティ"というか最小のキャパシティが割り当てられるっぽい。
golang 小ネタ
goでの前方一致と後方一致
imports "strings" prefix := "hoge" suffix := "bar" str := "hoge foo bar" strings.HasPrefix(str, prefix) // true strings.HasSuffix(str, suffix) // true
strings の中身
func HasPrefix(s, prefix string) bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } func HasSuffix(s, suffix string) bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix }
HasPrefix
対象文字列の長さとprefixの長さを比較して、対象文字列のほうが長かった
かつ
対象文字列の0番目からprefixの長さ(バイト長)分までを取って、引数のprefixと比較
HasSuffix
対象文字列の長さとsuffixの長さを比較して、対象文字列のほうが長かった
かつ
対象文字列長からsuffix長を引いた添字から対象文字列の最後までの切り出し、suffixと比較