前提・実現したいこと
Many2Many 間で 逆参照を行い And/Or で検索結果を取得したい
2つのモデルがあります
※実現したいことに不要なFieldは全て取り除いています
go
1type Problem struct { 2 gorm.Model 3 Title string `json:"title" binding:"required"` 4 Tags []Tag `gorm:"many2many:problem_tags;"` 5} 6 7type Tag struct { 8 gorm.Model 9 Name string `json:"name" binding:"required"` 10 Problems []Problem `gorm:"many2many:problem_tags;"` 11}
Tag.Name
が一つ / 複数与えられた時に
それらのTag.Nameを持つ Problemをすべて And/Or 検索して取得したいです
該当のソースコード
HTMLのForm から http://localhost:8080/?q=gin+gorm
のようなクエリを飛ばして
c.Request.URL.Query()
から query params を取得して
このparamsを用いて Problems を取得し表示
※paramsの取得は出来ています
※router.go, db.go などは省略します
go
1func Index(c *gin.Context) { 2 // ここで query parameters を取得 3 // params に入っているのは []string です 4 params, err := utils.URLQueryParse() 5 // err処理は省略 6 7 // !!ここ!! 8 output := []models.Problem{} 9 found := map[uint]bool{} 10 11 for _, tagname := range params { 12 var tag models.Tag 13 _ = tag.GetOneByName(tagname) 14 15 for _, p := range tag.Problems { 16 if _, ok := found[p.ID]; !ok { 17 output = append(output, p) 18 found[p.ID] = true 19 } 20 } 21 } 22 23 c.HTML(http.StatusOK, "Index.tmpl", gin.H{ 24 "Title": "Index", 25 "Result": params, 26 "Problems": output, 27 }) 28}
試したこと
上記のように無理やり実装はしました
一応動作はします
tag.GetOneByName
は以下のようになっています
go
1// func (self *Tag)GetOneByName(tagName string) error 2// tagName に部分一致する、Tag.Nameを持つ最初の models.Tag(ID昇順で) レコードを取得する 3// 見つかったら nil, 一件も見つからなかったら "record not found" を吐く 4// Problems も全部取得する ので処理遅い はず 5func (self *Tag)GetOneByName(tagName string) error { 6 return db.DB.Preload("Problems").Where("name LIKE ?", "%"+tagName+"%").First(self).Error 7}
一応And検索も無理やり実装はしました
が、O(N^3)
位のアルゴリズムになっています
gorm.io
の公式Referenceを見て Back-Referece
を発見したので
これは行けるだろうと思ってさんざん弄ったのですが、結局良くわかりませんでした
普通に考えると return db.DB.Model( ... ).Where( ... ).Association( ... ).Find( ... )
のような一行のSQLで取得出来ると思うのですが、可能でしょうか
生のSQLでの解消なども可能であればお願いします
補足情報(FW/ツールのバージョンなど)
bash
1❯ go version 2go version go1.16.2 linux/amd64 3 4❯ bat go go.mod 5go 1.16 6 7require ( 8 github.com/gin-gonic/gin v1.6.3 9 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect 10 github.com/k0kubun/pp v3.0.1+incompatible 11 github.com/mattn/go-colorable v0.1.8 // indirect 12 golang.org/x/sys v0.0.0-20210324051608-47abb6519492 // indirect 13 gorm.io/driver/sqlite v1.1.4 14 gorm.io/gorm v1.21.3 15)
schemeは以下のとおりです
bash
1❯ sqlite3 test.db 2SQLite version 3.31.1 2020-01-27 19:55:54 3Enter ".help" for usage hints. 4 5sqlite> .tables 6problem_tags problems solves tags 7sqlite> 8sqlite> 9sqlite> .schema problems 10CREATE TABLE `problems` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`title` text,`statement` text,`answer` text,`url` text,PRIMARY KEY (`id`)); 11CREATE INDEX `idx_problems_deleted_at` ON `problems`(`deleted_at`); 12sqlite> 13sqlite> 14sqlite> .schema tags 15CREATE TABLE `tags` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,PRIMARY KEY (`id`)); 16CREATE INDEX `idx_tags_deleted_at` ON `tags`(`deleted_at`); 17sqlite> 18sqlite> 19sqlite> .schema problem_tags 20CREATE TABLE `problem_tags` (`tag_id` integer,`problem_id` integer,PRIMARY KEY (`tag_id`,`problem_id`),CONSTRAINT `fk_problem_tags_tag` FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`),CONSTRAINT `fk_problem_tags_problem` FOREIGN KEY (`problem_id`) REFERENCES `problems`(`id`));
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。