server icon indicating copy to clipboard operation
server copied to clipboard

Create models for the database types (for goqu)

Open CrescentKohana opened this issue 3 years ago • 5 comments

We could probably use the types Jet generated as a base: pkg/types/model.

CrescentKohana avatar Apr 05 '22 17:04 CrescentKohana

Going to work on this right now.

karmek-k avatar Apr 12 '22 16:04 karmek-k

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
  },
  ...
]

CrescentKohana avatar Apr 12 '22 16:04 CrescentKohana

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.

karmek-k avatar Apr 12 '22 16:04 karmek-k

Regarding the CombinedLibrary issue, how are you scanning data from the db? Are you using GetLibraries?

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.

CrescentKohana avatar Apr 12 '22 17:04 CrescentKohana

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
}

CrescentKohana avatar Apr 12 '22 17:04 CrescentKohana