質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

解決済

golang gorm many2many 逆参照でのOR/AND検索を行いたい

minorm
minorm

総合スコア0

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

1回答

0評価

0クリップ

1084閲覧

投稿2021/03/26 11:39

編集2021/03/27 05:49

前提・実現したいこと

Many2Many 間で 逆参照を行い And/Or で検索結果を取得したい

2つのモデルがあります
※実現したいことに不要なFieldは全て取り除いています

go

type Problem struct { gorm.Model Title string `json:"title" binding:"required"` Tags []Tag `gorm:"many2many:problem_tags;"` } type Tag struct { gorm.Model Name string `json:"name" binding:"required"` Problems []Problem `gorm:"many2many:problem_tags;"` }

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

func Index(c *gin.Context) { // ここで query parameters を取得 // params に入っているのは []string です params, err := utils.URLQueryParse() // err処理は省略 // !!ここ!! output := []models.Problem{} found := map[uint]bool{} for _, tagname := range params { var tag models.Tag _ = tag.GetOneByName(tagname) for _, p := range tag.Problems { if _, ok := found[p.ID]; !ok { output = append(output, p) found[p.ID] = true } } } c.HTML(http.StatusOK, "Index.tmpl", gin.H{ "Title": "Index", "Result": params, "Problems": output, }) }

試したこと

上記のように無理やり実装はしました
一応動作はします

tag.GetOneByName は以下のようになっています

go

// func (self *Tag)GetOneByName(tagName string) error // tagName に部分一致する、Tag.Nameを持つ最初の models.Tag(ID昇順で) レコードを取得する // 見つかったら nil, 一件も見つからなかったら "record not found" を吐く // Problems も全部取得する ので処理遅い はず func (self *Tag)GetOneByName(tagName string) error { return db.DB.Preload("Problems").Where("name LIKE ?", "%"+tagName+"%").First(self).Error }

一応And検索も無理やり実装はしました
が、O(N^3) 位のアルゴリズムになっています

gorm.io の公式Referenceを見て Back-Refereceを発見したので
これは行けるだろうと思ってさんざん弄ったのですが、結局良くわかりませんでした

普通に考えると return db.DB.Model( ... ).Where( ... ).Association( ... ).Find( ... )
のような一行のSQLで取得出来ると思うのですが、可能でしょうか
生のSQLでの解消なども可能であればお願いします

補足情報(FW/ツールのバージョンなど)

bash

❯ go version go version go1.16.2 linux/amd64 ❯ bat go go.mod go 1.16 require ( github.com/gin-gonic/gin v1.6.3 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect github.com/k0kubun/pp v3.0.1+incompatible github.com/mattn/go-colorable v0.1.8 // indirect golang.org/x/sys v0.0.0-20210324051608-47abb6519492 // indirect gorm.io/driver/sqlite v1.1.4 gorm.io/gorm v1.21.3 )

schemeは以下のとおりです

bash

❯ sqlite3 test.db SQLite version 3.31.1 2020-01-27 19:55:54 Enter ".help" for usage hints. sqlite> .tables problem_tags problems solves tags sqlite> sqlite> sqlite> .schema problems CREATE TABLE `problems` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`title` text,`statement` text,`answer` text,`url` text,PRIMARY KEY (`id`)); CREATE INDEX `idx_problems_deleted_at` ON `problems`(`deleted_at`); sqlite> sqlite> sqlite> .schema tags CREATE TABLE `tags` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,PRIMARY KEY (`id`)); CREATE INDEX `idx_tags_deleted_at` ON `tags`(`deleted_at`); sqlite> sqlite> sqlite> .schema problem_tags CREATE 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`));

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。