GolangのORMパッケージ「gorp」でSQLのプレースホルダーを使用しIN句を書きたい

最近、会社でGolangを使用したマイクロサービスの開発をやってました。
その際に、ちょっと詰まった部分があったのでメモがてら。
Golangで**gorp**というORMパッケージを使用してSQLで`in`句を使用したい人の話。

## そもそもGolangのGorpって何？

golangで使用できるORMパッケージ
https://github.com/go-gorp/gorp

SQLの文を直書きできて、構造体へのマッピングを行ってくれる。
個人的にSQLは直書きのままの方が嬉しいのでなかなか良いパッケージ。
だけど...？

### 結論だけ先に書いておく

結構無理矢理...
単純にいうとinterfaceを第３引数に渡せよってことなんだけど。

```go

	args := make([]interface{}, len(fileIds))
	quarks := make([]string, len(fileIds))
	for i, fileId := range fileIds {
		args[i] = fileId
		quarks[i] = "?"
	}

	var apps []*App
	_, err := dbMap.Select(&apps, fmt.Sprintf("SELECT * FROM app WHERE file_id in (%s) ORDER BY id DESC", strings.Join(quarks, ",")), args...)
	if err != nil {
		return nil, err
	}
```

参考：[Golang SqlExecutor.Select Examples](https://golang.hotexamples.com/examples/github.com.coopernurse.gorp/SqlExecutor/Select/golang-sqlexecutor-select-method-examples.html)

### 公式のドキュメント読もう

最初PHP脳だったのでsliceを`,`で展開して引数に渡せばいけると思ってたの...///
普通にドキュメント見たら。gorpではinterface渡せって書いてあった。
ちょっとプレースホルダーへの値の渡し方が特殊だったのでドキュメントちゃんと読もうや問題。

公式：https://gowalker.org/github.com/go-gorp/gorp#DbMap

同じように悩んでる人もいた
https://kabochapo.hateblo.jp/entry/2017/09/17/195805

```golang
type DbMap struct {

    // Db handle to use with this map
    Db  *sql.DB

    // Dialect implementation to use with this map
    Dialect Dialect

    TypeConverter TypeConverter

    // ExpandSlices when enabled will convert slice arguments in mappers into flat
    // values. It will modify the query, adding more placeholders, and the mapper,
    // adding each item of the slice as a new unique entry in the mapper. For
    // example, given the scenario bellow:
    //
    //     dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{
    //       "IDs": []int64{1, 2, 3},
    //     })
    //
    // The executed query would be:
    //
    //     SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2)
    //
    // With the mapper:
    //
    //     map[string]interface{}{
    //       "IDs":  []int64{1, 2, 3},
    //       "IDs0": int64(1),
    //       "IDs1": int64(2),
    //       "IDs2": int64(3),
    //     }
    //
    // It is also flexible for custom slice types. The value just need to
    // implement stringer or numberer interfaces.
    //
    //     type CustomValue string
    //
    //     const (
    //       CustomValueHey CustomValue = "hey"
    //       CustomValueOh  CustomValue = "oh"
    //     )
    //
    //     type CustomValues []CustomValue
    //
    //     func (c CustomValues) ToStringSlice() []string {
    //       values := make([]string, len(c))
    //       for i := range c {
    //         values[i] = string(c[i])
    //       }
    //       return values
    //     }
    //
    //     func query() {
    //       // ...
    //       result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{
    //         "Values": CustomValues([]CustomValue{CustomValueHey}),
    //       })
    //       // ...
    //     }
    ExpandSliceArgs bool
    // contains filtered or unexported fields
}
```

### Golang書ける人おいでよ

思った以上に書くことなかったので宣伝。
Golangを書けてマイクロサービスの開発をゴリゴリやっていきたいって人はうちの会社向いてると思うので是非カジュアル面談だけでも来てくださいまし！（Qiita Jobsで募集しています。）
プロジェクトでGolangを積極的に採用中でござんす。

👇👇👇👇👇バックエンドエンジニア募集中〜👇👇👇👇👇
https://jobs.qiita.com/employers/255/postings/1169