使用 Gorm 查询多对多关系

Query a many-to-many relation using Gorm

我有一个 Movie 模型,它有很多在 Gorm 中定义的多对多关系。现在我必须创建一个 API 来查询在这些关系中具有 ID 的所有电影。

让我们把它分成小块:

电影模特:

type Movie struct {
    gorm.Model
    TitleID           string     `gorm:"size:50;not null" json:"title_id"`
    OriginalName      string     `gorm:"size:250;not null" json:"original_name"`
    Categories        []Category `gorm:"null;many2many:movie_categories;" json:"categories"`
    Types             []Type     `gorm:"null;many2many:movie_types;" json:"types"`
    Directors         []Cast     `gorm:"null;many2many:movie_directors;" json:"directors"`
    Writers           []Cast     `gorm:"null;many2many:movie_writers;" json:"writers"`
}

我的查询函数:

func (u *Movie) FindMoviesByDirectors(db *gorm.DB, directors string) (*[]Movie, error) {
    var err error
    movies := []Movie{}
    err = db.Set("gorm:auto_preload", true).Where("directors = ?", directors).Find(&movies).Error
    if err != nil {
        return &[]Movie{}, err
    }
    return &movies, err
}
  1. 如何从关系中查询具有 ID 的电影? (在我的例子中,查询所有 ID 1director 的电影。)
  2. 如何扩展它来查询具有多个ID和多个关系的电影? (例如,查询所有具有 [1,2] ID 的电影 directors[3,4]types。)

问题回复

  1. 您可以在引用的 many-to-many 关系中查询外键引用 ID(参见下面的示例)
  2. 您可以使用 IN 运算符一次查询多个值。 gorm 支持这个见 https://gorm.io/docs/query.html

可能的解决方案

为此,您的原始 sql 可能如下所示:

SELECT 
    M.* 
FROM
    movies M
LEFT JOIN movie_categories MC on MC.movie_id = M.id
LEFT JOIN movie_types MT on MT.movie_id = M.id
LEFT JOIN movie_directors MD on MD.movie_id = M.id
LEFT JOIN movie_writers MW on MW.movie_id = M.id
WHERE
  MC.category_id IN (?) OR
  MT.type_id IN (?) OR
  MD.director_id IN (?) OR
  MW.writer_id IN (?)

注意。如果电影在特定关系中没有记录,我会使用 Left join。 where 条件将确保返回所需的记录。此外,OR 意味着可以满足这些条件中的任何一个。如果您希望满足所有这些条件,您可以使用 AND 而不是 OR

以下可能解决方案的假设

假设

  1. 上面的sql存储在变量sql
  2. 存储在数组中的所需类别 ID categories 例如。 []int64{1,2,3}
  3. 存储在数组中的所需类型 ID types 例如。 []int64{1,2,3}
  4. 所需的导演 ID 存储在数组中 directors 例如。 []int64{1,2,3}
  5. 所需的作者 ID 存储在数组中 writers 例如。 []int64{1,2,3}

您可以根据需要修改您的函数签名以根据需要接受这些参数。

可能的解决方案 1

Gorm 允许您使用 raw-sql

例如


result := db.Raw(sql,categories,types,directors,writers).Scan(&movies)
err := result.Error

可能的解决方案 2

Gorm 允许您构建自己的 joins using their query builder 如果您想优化数据库上的查询 运行 这可能很有用。例如,如果您的 directors 数组为空但 categories 不是,则您只能查询 categories 关系。此外,您可以使用构建器对象

微调您的查询

例如


var query = db.Table("movies") //or you could use db.Model(&Movie{})

query = query.Select("movies.*")

// begin querying relations
if( len(categories)>0 ){
    query = query.Joins("LEFT JOIN movie_categories on movie_categories.movie_id = movies.id");
}

if( len(categories)>0 ){
    query = query.Where("movie_categories.category_id IN (?)",categories);
}
//end querying relations
//you could repeat this for each relation

//With the Gorm query builder object store in query you can make other modifications before executing the query

result := query.Scan(&movies)
err := result.Error

其他注意事项

如果您有兴趣根据类别名称查询所有电影,您可以使用与 joins 相同的方法来编写查询。

参考资料/资源

戈尔姆 Where In - https://gorm.io/docs/query.html

Gorm SQL 生成器 - https://gorm.io/docs/sql_builder.html