【Go】XormでLIKE演算子を使ってパターンマッチングする(前方一致, 後方一致, 部分一致)


はじめに

Xormの前方一致の書き方で少し迷ったので、忘備録として記事にします。
誰かの役に立てば幸いです。

環境

  • go 1.18
  • github.com/go-sql-driver/mysql v1.6.0
  • xorm.io/xorm v1.2.5

MySQLのLIKE演算子とは

WHERE句で条件を指定するときに 、LIKE演算子を使用すると、前方一致や後方一致などパターンマッチングで検索出来ます。
(例1 前方一致)

SELECT * FROM users WHERE name LIKE '田中%';

このように指定すると、usersテーブルのnameカラムの中から、田中〜というデータを取得します。

(例2 後方一致)

SELECT * FROM users WHERE name LIKE '%太郎';

このように指定すると、usersテーブルのnameカラムの中から、〜太郎というデータを取得します。

(例3 部分一致)

SELECT * FROM users WHERE name LIKE '%太%';

このように指定すると、usersテーブルのnameカラムの中から、が含まれているデータを取得します。

やってみる

それでは、上記をXormで書く方法をやってみます。
DockerでMySQLのDBを作成し、データを挿入し、それをLIKE演算子を使用して検索します。

DB作成

docker-compose.yml

sample_dbという名前のDBを作成します。
docker-compose up -dでDB作成されます。

version: "3"
services:
  db:
    image: mysql:5.7
    environment:
    - MYSQL_DATABASE=sample_db
    - MYSQL_ROOT_PASSWORD=root
    command: >
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
    ports:
      - 3306:3306

DBにデータをinsertする

go run main.goコマンドでDBにデータを追加します。

infra/xorm.go

main.goから呼び出して、DBを初期化します。

func DBInit() *xorm.Engine {
	engine, err := xorm.NewEngine("mysql", "root:root@tcp([127.0.0.1]:3306)/sample_db?charset=utf8mb4&parseTime=true")
	if err != nil {
		log.Fatal(err)
	}

	// xormで使ったSQLをログに吐きます
	engine.ShowSQL(true)

	// usersテーブルを初期化します
	engine.Sync2(new(model.Users))

	return engine
}

model/user.go

下記の構造体を元にDBテーブルが作成されます。
IDはオートインクリメントにしてます。

package model

type Users struct {
	ID      int    `xorm:"id pk autoincr"`
	Name    string `xorm:"name"`
	Address string `xorm:"address"`
}

main.go

func main() {
	// engineを作成します。
	engine := infra.DBInit()

	user1 := model.Users{
		Name:    "田中太郎",
		Address: "大阪府",
	}
	user2 := model.Users{
		Name:    "田中隆二",
		Address: "京都府",
	}
	user3 := model.Users{
		Name:    "佐々木太一",
		Address: "東京都",
	}
	user4 := model.Users{
		Name:    "佐々木次郎",
		Address: "東京都",
	}

	users := []*model.Users{
		&user1,
		&user2,
		&user3,
		&user4,
	}

	create(*engine, users)
	find(*engine)
}

// create ユーザー作成
func create(engine xorm.Engine, users []*model.Users) {
	_, err := engine.Table("users").Insert(users)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("users:", users)
}

// find 複数取得(完全一致)
func find(engine xorm.Engine) {
	users := []model.Users{}
	// addressが大阪府のuserを全件取得します
	err := engine.Where("address = ?", "大阪府").Find(&users)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("users:", users)
}

上記の内容で
go run main.goをすると、
下記のようなデータが作成されます。

また、上記のfindの検索結果は完全一致の為、
住所が大阪府である1名だけが取得出来ます。

結果

[xorm] [info]  2022/04/03 15:19:18.242783 [SQL] INSERT INTO `users` (`name`,`address`) VALUES (?, ?),(?, ?),(?, ?) [田中太郎 大阪府 田中隆二 京都府 佐々木太一 東京都 佐々木次郎 東京都] - 15.334537ms
users: [0xc000109e30 0xc000109e60 0xc000109e90]
[xorm] [info]  2022/04/03 15:19:18.251110 [SQL] SELECT `id`, `name`, `address` FROM `users` WHERE (address = ?) [大阪府] - 8.156432ms
users: [{1 田中太郎 大阪府}]

では、次にLIKE検索してみます。

LIKE検索する

Xormでの書き方は以下のようになります。
%をどのように書くか迷ったけど、+"%"のように書くらしい。

前方一致

engine.Table("テーブル名")Where("カラム名 LIKE ?", "検索したい文字"+"%")

後方一致

engine.Table("テーブル名")Where("カラム名 LIKE ?", "%"+"検索したい文字")

部分一致

engine.Table("テーブル名")Where("カラム名 LIKE ?", "%"+"検索したい文字"+"%")

main.go

main.goを下記のように変更します。
searchWordsという変数に検索したい文字を入力して使います。
前方一致のメソッドをsearchForwordMatch
後方一致のメソッドをsearchBackwordMatch
部分一致のメソッドをsearchPartialMatch
としました。
使用する時にアンコメントします。

// ここに検索する文字を記載します。
var searchWords string = ""

func main() {
	// engineを作成します。
	engine := infra.DBInit()

	searchForwordMatch(*engine, searchWords)
	// searchBackwordMatch(*engine, searchWords)
	// searchPartialMatch(*engine, searchWords)
}

// searchForwordMatch 前方一致
func searchForwordMatch(engine xorm.Engine, searchWord string) {
	users := []model.Users{}
	err := engine.Table("users").Where("name LIKE?", searchWord+"%").Find(&users)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("users:", users)
}

// searchBackwordMatch 後方一致
func searchBackwordMatch(engine xorm.Engine, searchWord string) {
	users := []model.Users{}
	err := engine.Table("users").Where("name LIKE?", "%"+searchWord).Find(&users)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("users:", users)
}

// searchPartialMatch 部分一致
func searchPartialMatch(engine xorm.Engine, searchWord string) {
	users := []model.Users{}
	err := engine.Table("users").Where("name LIKE?", "%"+searchWord+"%").Find(&users)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("users:", users)
}

前方一致実行

名前が田中~の人を取得してみます。

  • var searchWords string = "田中"にします。
  • searchForwordMatch(*engine, searchWords)をアンコメントします。

go run main.goコマンド実行

結果

[xorm] [info]  2022/04/03 15:56:10.500290 [SQL] SELECT `id`, `name`, `address` FROM `users` WHERE (name LIKE?) [田中%] - 12.830607ms
users: [{1 田中太郎 大阪府} {2 田中隆二 京都府}]

田中○○さんを取得出来ました。

後方一致実行

  • var searchWords string = "郎"にします。
  • searchForwordMatch(*engine, searchWords)をコメントアウトします。
  • searchForwordMatch(*engine, searchWords)をコメントアウトします。

go run main.goコマンド実行

結果

[xorm] [info]  2022/04/03 16:22:49.139091 [SQL] SELECT `id`, `name`, `address` FROM `users` WHERE (name LIKE?) [%郎] - 8.410297ms
users: [{1 田中太郎 大阪府} {4 佐々木次郎 東京都}]

○○郎という名前の人を取得出来ました。

部分一致実行

  • var searchWords string = "太"にします。
  • searchBackwordMatch(*engine, searchWords)をコメントアウトします。
  • searchPartialMatch(*engine, searchWords)をコメントアウトします。

go run main.goコマンド実行

結果

[xorm] [info]  2022/04/03 16:26:05.179745 [SQL] SELECT `id`, `name`, `address` FROM `users` WHERE (name LIKE?) [%太%] - 9.484537ms
users: [{1 田中太郎 大阪府} {3 佐々木太一 東京都}]

「太」が入っている人を取得出来ました。

ちなみに、部分一致は、該当の検索ワードは真ん中に無いといけない訳ではなく、文字列のどこかにあれば、ヒットします。
なので、
var searchWords string = "佐"にすると、
佐々木さんが2人ヒットすることになります。

結果

[xorm] [info]  2022/04/03 16:31:05.152801 [SQL] SELECT `id`, `name`, `address` FROM `users` WHERE (name LIKE?) [%佐%] - 4.939901ms
users: [{3 佐々木太一 東京都} {4 佐々木次郎 東京都}]

さいごに

思ったより簡単に使えました。
部分一致が、中間の文字しか検索出来ないと思ってましたが、中間に限らず、文字列どこかにあればヒットするので、便利に使えそうです。

参考