0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

any/interface{}型のSliceを分割したい

Last updated at Posted at 2023-02-22

どんな状況?

たとえば以下のようなCreateRecords()という関数があるとして、その返り値はなんらかの構造体Tを要素に持つSliceが実体のanyです。gormのCreateにそのまま渡すことができ、実行することでレコードがインサートされるものとします。

func CreateRecords() any {
  (実装は省略)
}

c:=CreateRecords()
if r:=db.Create(c); r.Error!=nil {
  (エラー処理)
}

db(ここでは*gorm.DBのこと)はOpenでどのデータベースのドライバーを使うかによって挙動が変わる可能性があり、今回はsqliteを使ったところCreateRecords()が返す要素数が多すぎて1度にINSERTするクエリが発行できないといった問題が発生しました。
cに含まれている要素数を絞って(*gorm.DB).Create(any)をコールすればよさそうなものの、CreateRecords()側の実装は変えたくありません(あるいはブラックボックスで変えようがないかもしれません)。加えてCreateRecords()がどんな構造体を要素に持つSliceを返すかは、実行するまでわかりません。

解答:reflectionを使う

すぐにピンと来るとは思いますが、やっぱりreflectionです。ただしもともとのcがなんらかの構造体Tを要素に持つSliceが実体のany([]T)であるということに注意する必要があります。

c:=CreateRecords()
if v:=reflect.ValueOf(c); len:=v.Len(); len>0 {
  for i:=0; ; i++ {
    // 例えば100レコードで分割
    begin:=i*100
    end:=(i+1)*100
    if end>=len {
      end=len
    }
    pv=v.Slice(begin,end).Interface()
    if r:=db.Create(pv); r.Error!=nil {
      log.Fatal("途中で処理とまったでー")  // エラーが起きたことだけ分かればヨシと言う雑な例
    }

    if end==len {
      break
    }
  }
}

reflect.Valueに対してSlice(n,m)を呼ぶことでsomeSlice[n:m]と同等のオペレーションが実現出来ます。ここでの返り値はSliceではなくreflect.Valueになるため、最後にInterface()を呼んでany([]T)へと変換しています。

注意

サンプルなので雑に書いてますが、分割して流す場合は途中で止まる可能性を考慮しましょう。今回の想定では途中で止まっても問題ない(そうなった場合はエラーを通知した上で、データを消して1から再実行しても大丈夫な用途でしか使わない)ことを前提にしています。
途中で止まってしまったら困るような処理の場合は、それ用の対策をちゃんと入れましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?