Open CrescentKohana opened 2 years ago
Going to work on this right now.
Going to work on this right now.
Great! I have some preliminary stuff done in #25.
I ran into an issue with the Combined model and was not able get goqu to scan stuff correctly into this model:
type CombinedLibrary struct {
Library
Galleries []Gallery `db:"gallery"`
}
Expected:
[
{
"ID": 1,
"Path": "test/",
"Layout": "structured",
"Galleries": [ <Gallery Object>, <Gallery Object>, ... ]
},
...
]
Actual:
[
{
"ID": 1,
"Path": "test/",
"Layout": "structured",
"Galleries": null
},
...
]
I've just added db
tags to all models (https://github.com/karmek-k/server/commit/6d136735f8dab2d873e95d8c4b4179e5aea688c9).
Regarding the CombinedLibrary
issue, how are you scanning data from the db? Are you using GetLibraries
?
Btw, PostgreSQL supports arrays as a column type, but MySQL does not.
Regarding the
CombinedLibrary
issue, how are you scanning data from the db? Are you usingGetLibraries
?
Yes, I tried to use GetLibraries
with the ways listed on gogu docs.
Btw, PostgreSQL supports arrays as a column type, but MySQL does not.
Libraries are in one-to-many relationship with Galleries, and are being mapped/scanned to array here. No array columns used nor needed.
Actually I got it to work in really verbose way and there would probably be issues if two or more tables had columns named same.
Basically I had to make a new type which has all the columns on the same level, and scan them manually with Scanner() to a map which would then be converted into to struct we actually want.
There has to be a better way though. Even the documentation is saying this:
goqu also supports scanning into multiple structs. In the example below we define a Role and User struct that could both be used individually to scan into. However, you can also create a new struct that adds both structs as fields that can be populated in a single query.
type LibraryRow struct {
ID int32 `db:"id"`
Path string `db:"path"`
Layout string `db:"layout"`
UUID string `db:"uuid"`
LibraryID int32 `db:"library_id"`
ArchivePath string `db:"archive_path"`
Title string `db:"title"`
TitleNative *string `db:"title_native"`
TitleTranslated *string `db:"title_translated"`
Category *string `db:"category"`
Series *string `db:"series"`
Released *string `db:"released"`
Language *string `db:"language"`
Translated *bool `db:"translated"`
Nsfw bool `db:"nsfw"`
Hidden bool `db:"hidden"`
ImageCount *int32 `db:"image_count"`
ArchiveSize *int32 `db:"archive_size"`
ArchiveHash *string `db:"archive_hash"`
Thumbnail *string `db:"thumbnail"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
func GetLibraries() ([]model.CombinedLibrary, error) {
scanner, err := database.QB().
From("library").
Join(
goqu.T("gallery"),
goqu.On(goqu.I("gallery.library_id").Eq(goqu.I("library.id"))),
).
Executor().
Scanner()
if err != nil {
log.Error(err)
return nil, err
}
defer func(scanner exec.Scanner) {
if err := scanner.Close(); err != nil {
log.Error(err)
}
}(scanner)
librariesMap := make(map[int32]model.CombinedLibrary)
for scanner.Next() {
lr := LibraryRow{}
if err = scanner.ScanStruct(&lr); err != nil {
log.Error(err)
return nil, err
}
var gallery = model.Gallery{UUID: lr.UUID,
Title: lr.Title,
TitleNative: lr.TitleNative,
TitleTranslated: lr.TitleTranslated,
Category: lr.Category,
Series: lr.Series,
Released: lr.Released,
Language: lr.Language,
Translated: lr.Translated,
Nsfw: lr.Nsfw,
Hidden: lr.Hidden,
ImageCount: lr.ImageCount,
ArchiveSize: lr.ArchiveSize,
ArchiveHash: lr.ArchiveHash,
Thumbnail: lr.Thumbnail,
CreatedAt: lr.CreatedAt,
UpdatedAt: lr.UpdatedAt,
}
value, ok := librariesMap[lr.ID]
if ok {
value.Galleries = append(value.Galleries, gallery)
librariesMap[lr.ID] = value
} else {
librariesMap[lr.ID] = model.CombinedLibrary{
Library: model.Library{
ID: lr.ID,
Path: lr.Path,
Layout: lr.Layout,
},
Galleries: []model.Gallery{gallery},
}
}
}
librariesSlice := make([]model.CombinedLibrary, 0, len(librariesMap))
for _, val := range librariesMap {
librariesSlice = append(librariesSlice, val)
}
return librariesSlice, nil
}
We could probably use the types Jet generated as a base: pkg/types/model.