diff --git a/database/postgres/db.go b/database/postgres/db.go index 62c4558..c9aadda 100644 --- a/database/postgres/db.go +++ b/database/postgres/db.go @@ -65,7 +65,7 @@ func (d *DB) Characters() repositories.CharacterRepository { } func (d *DB) Tags() repositories.TagRepository { - panic("implement me") + return &tagRepository{db: d.db} } func (d *DB) Logs() repositories.LogRepository { diff --git a/database/postgres/migrations/20210323181041_create_table_tag.sql b/database/postgres/migrations/20210323181041_create_table_tag.sql new file mode 100644 index 0000000..c987cd1 --- /dev/null +++ b/database/postgres/migrations/20210323181041_create_table_tag.sql @@ -0,0 +1,15 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE common_tag ( + tag TEXT NOT NULL, + target_kind TEXT NOT NULL, + target_id TEXT NOT NULL, + + PRIMARY KEY (tag, target_kind, target_id) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE common_tag; +-- +goose StatementEnd diff --git a/database/postgres/migrations/20210323181601_create_index_tag_target.sql b/database/postgres/migrations/20210323181601_create_index_tag_target.sql new file mode 100644 index 0000000..14a7ac4 --- /dev/null +++ b/database/postgres/migrations/20210323181601_create_index_tag_target.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +CREATE INDEX common_tag_index_target ON common_tag (target_kind, target_id); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS common_tag_index_target; +-- +goose StatementEnd diff --git a/database/postgres/migrations/20210323201210_create_table_story.sql b/database/postgres/migrations/20210323201210_create_table_story.sql new file mode 100644 index 0000000..0eded1a --- /dev/null +++ b/database/postgres/migrations/20210323201210_create_table_story.sql @@ -0,0 +1,20 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE story ( + id TEXT NOT NULL PRIMARY KEY, + author TEXT NOT NULL, + name TEXT NOT NULL, + category TEXT NOT NULL, + open BOOLEAN NOT NULL, + listed BOOLEAN NOT NULL, + sort_by_fictional_date BOOLEAN NOT NULL, + created_date TIMESTAMP NOT NULL, + fictional_date TIMESTAMP NOT NULL, + updated_date TIMESTAMP NOT NULL +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE story; +-- +goose StatementEnd diff --git a/database/postgres/migrations/20210323203036_create_index_author.sql b/database/postgres/migrations/20210323203036_create_index_author.sql new file mode 100644 index 0000000..8630bf2 --- /dev/null +++ b/database/postgres/migrations/20210323203036_create_index_author.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +CREATE INDEX story_index_author ON story (author); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS story_index_author +-- +goose StatementEnd diff --git a/database/postgres/migrations/20210323203042_create_index_fictional_date.sql b/database/postgres/migrations/20210323203042_create_index_fictional_date.sql new file mode 100644 index 0000000..84e8b20 --- /dev/null +++ b/database/postgres/migrations/20210323203042_create_index_fictional_date.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +CREATE INDEX story_index_fictional_date ON story (fictional_date); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS story_index_fictional_date +-- +goose StatementEnd diff --git a/database/postgres/migrations/20210323203049_create_index_created_date.sql b/database/postgres/migrations/20210323203049_create_index_created_date.sql new file mode 100644 index 0000000..4823886 --- /dev/null +++ b/database/postgres/migrations/20210323203049_create_index_created_date.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +CREATE INDEX story_index_created_date ON story (created_date); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS story_index_created_date +-- +goose StatementEnd diff --git a/database/postgres/migrations/20210323203054_create_index_updated_date.sql b/database/postgres/migrations/20210323203054_create_index_updated_date.sql new file mode 100644 index 0000000..09bbeba --- /dev/null +++ b/database/postgres/migrations/20210323203054_create_index_updated_date.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +CREATE INDEX story_index_updated_date ON story (updated_date); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS story_index_updated_date +-- +goose StatementEnd diff --git a/database/postgres/psqlcore/models.go b/database/postgres/psqlcore/models.go index 7913511..dfa104b 100644 --- a/database/postgres/psqlcore/models.go +++ b/database/postgres/psqlcore/models.go @@ -8,6 +8,12 @@ import ( "time" ) +type CommonTag struct { + Tag string `json:"tag"` + TargetKind string `json:"target_kind"` + TargetID string `json:"target_id"` +} + type CoreCounter struct { ID string `json:"id"` Value sql.NullInt32 `json:"value"` @@ -41,3 +47,16 @@ type DataCharacter struct { Description string `json:"description"` TsVector interface{} `json:"ts_vector"` } + +type Story struct { + ID string `json:"id"` + Author string `json:"author"` + Name string `json:"name"` + Category string `json:"category"` + Open bool `json:"open"` + Listed bool `json:"listed"` + SortByFictionalDate bool `json:"sort_by_fictional_date"` + CreatedDate time.Time `json:"created_date"` + FictionalDate time.Time `json:"fictional_date"` + UpdatedDate time.Time `json:"updated_date"` +} diff --git a/database/postgres/psqlcore/stories.sql.go b/database/postgres/psqlcore/stories.sql.go new file mode 100644 index 0000000..0f3a37f --- /dev/null +++ b/database/postgres/psqlcore/stories.sql.go @@ -0,0 +1,113 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: stories.sql + +package psqlcore + +import ( + "context" + "time" + + "github.com/lib/pq" +) + +const selectStories = `-- name: SelectStories :many +SELECT id, author, name, category, open, listed, sort_by_fictional_date, created_date, fictional_date, updated_date FROM story +WHERE ($1::bool = false OR id = ANY($2::text[])) + AND ($3::bool = false OR name = $4::text) + AND ($5::bool = false OR fictional_date >= $6::timestamp) + AND ($7::bool = false OR fictional_date <= $8::timestamp) + AND ($9::bool = false OR category = $10::text) + AND ($11::bool = false OR open = $12::bool) + AND ($13::bool = false OR unlisted = $14::bool) +LIMIT $15::int +` + +type SelectStoriesParams struct { + FilterID bool `json:"filter_id"` + Ids []string `json:"ids"` + FilterAuthor bool `json:"filter_author"` + Author string `json:"author"` + FilterEarlistFictionalDate bool `json:"filter_earlist_fictional_date"` + EarliestFictionalDate time.Time `json:"earliest_fictional_date"` + FilterLastestFictionalDate bool `json:"filter_lastest_fictional_date"` + LatestFictionalDate time.Time `json:"latest_fictional_date"` + FilterCategory bool `json:"filter_category"` + Category string `json:"category"` + FilterOpen bool `json:"filter_open"` + Open bool `json:"open"` + FilterUnlisted bool `json:"filter_unlisted"` + Unlisted bool `json:"unlisted"` + LimitSize int32 `json:"limit_size"` +} + +func (q *Queries) SelectStories(ctx context.Context, arg SelectStoriesParams) ([]Story, error) { + rows, err := q.db.QueryContext(ctx, selectStories, + arg.FilterID, + pq.Array(arg.Ids), + arg.FilterAuthor, + arg.Author, + arg.FilterEarlistFictionalDate, + arg.EarliestFictionalDate, + arg.FilterLastestFictionalDate, + arg.LatestFictionalDate, + arg.FilterCategory, + arg.Category, + arg.FilterOpen, + arg.Open, + arg.FilterUnlisted, + arg.Unlisted, + arg.LimitSize, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Story{} + for rows.Next() { + var i Story + if err := rows.Scan( + &i.ID, + &i.Author, + &i.Name, + &i.Category, + &i.Open, + &i.Listed, + &i.SortByFictionalDate, + &i.CreatedDate, + &i.FictionalDate, + &i.UpdatedDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const selectStory = `-- name: SelectStory :one +SELECT id, author, name, category, open, listed, sort_by_fictional_date, created_date, fictional_date, updated_date FROM story WHERE id = $1 LIMIT 1 +` + +func (q *Queries) SelectStory(ctx context.Context, id string) (Story, error) { + row := q.db.QueryRowContext(ctx, selectStory, id) + var i Story + err := row.Scan( + &i.ID, + &i.Author, + &i.Name, + &i.Category, + &i.Open, + &i.Listed, + &i.SortByFictionalDate, + &i.CreatedDate, + &i.FictionalDate, + &i.UpdatedDate, + ) + return i, err +} diff --git a/database/postgres/psqlcore/tags.sql.go b/database/postgres/psqlcore/tags.sql.go new file mode 100644 index 0000000..477d5f6 --- /dev/null +++ b/database/postgres/psqlcore/tags.sql.go @@ -0,0 +1,235 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: tags.sql + +package psqlcore + +import ( + "context" + + "github.com/lib/pq" +) + +const clearTag = `-- name: ClearTag :exec +DELETE FROM common_tag +WHERE tag=$1::TEXT + AND target_kind=$2::TEXT + AND target_id=$3::TEXT +` + +type ClearTagParams struct { + Tag string `json:"tag"` + TargetKind string `json:"target_kind"` + TargetID string `json:"target_id"` +} + +func (q *Queries) ClearTag(ctx context.Context, arg ClearTagParams) error { + _, err := q.db.ExecContext(ctx, clearTag, arg.Tag, arg.TargetKind, arg.TargetID) + return err +} + +const clearTagsByTarget = `-- name: ClearTagsByTarget :exec +DELETE FROM common_tag +WHERE target_kind=$1::TEXT + AND target_id=$2::TEXT +` + +type ClearTagsByTargetParams struct { + TargetKind string `json:"target_kind"` + TargetID string `json:"target_id"` +} + +func (q *Queries) ClearTagsByTarget(ctx context.Context, arg ClearTagsByTargetParams) error { + _, err := q.db.ExecContext(ctx, clearTagsByTarget, arg.TargetKind, arg.TargetID) + return err +} + +const selectDistinctTags = `-- name: SelectDistinctTags :many +SELECT DISTINCT tag FROM common_tag +` + +func (q *Queries) SelectDistinctTags(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, selectDistinctTags) + if err != nil { + return nil, err + } + defer rows.Close() + items := []string{} + for rows.Next() { + var tag string + if err := rows.Scan(&tag); err != nil { + return nil, err + } + items = append(items, tag) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const selectDistinctTagsByKind = `-- name: SelectDistinctTagsByKind :many +SELECT DISTINCT tag FROM common_tag WHERE tag LIKE '$1::text%' +` + +func (q *Queries) SelectDistinctTagsByKind(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, selectDistinctTagsByKind) + if err != nil { + return nil, err + } + defer rows.Close() + items := []string{} + for rows.Next() { + var tag string + if err := rows.Scan(&tag); err != nil { + return nil, err + } + items = append(items, tag) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const selectTagsByTag = `-- name: SelectTagsByTag :many +SELECT tag, target_kind, target_id FROM common_tag WHERE tag = $1::text +` + +func (q *Queries) SelectTagsByTag(ctx context.Context, tagName string) ([]CommonTag, error) { + rows, err := q.db.QueryContext(ctx, selectTagsByTag, tagName) + if err != nil { + return nil, err + } + defer rows.Close() + items := []CommonTag{} + for rows.Next() { + var i CommonTag + if err := rows.Scan(&i.Tag, &i.TargetKind, &i.TargetID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const selectTagsByTags = `-- name: SelectTagsByTags :many +SELECT tag, target_kind, target_id FROM common_tag WHERE tag = ANY($1::text[]) +` + +func (q *Queries) SelectTagsByTags(ctx context.Context, tagNames []string) ([]CommonTag, error) { + rows, err := q.db.QueryContext(ctx, selectTagsByTags, pq.Array(tagNames)) + if err != nil { + return nil, err + } + defer rows.Close() + items := []CommonTag{} + for rows.Next() { + var i CommonTag + if err := rows.Scan(&i.Tag, &i.TargetKind, &i.TargetID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const selectTagsByTarget = `-- name: SelectTagsByTarget :many +SELECT tag, target_kind, target_id FROM common_tag WHERE target_kind = $1 AND target_id = $2::text +` + +type SelectTagsByTargetParams struct { + TargetKind string `json:"target_kind"` + Column2 string `json:"column_2"` +} + +func (q *Queries) SelectTagsByTarget(ctx context.Context, arg SelectTagsByTargetParams) ([]CommonTag, error) { + rows, err := q.db.QueryContext(ctx, selectTagsByTarget, arg.TargetKind, arg.Column2) + if err != nil { + return nil, err + } + defer rows.Close() + items := []CommonTag{} + for rows.Next() { + var i CommonTag + if err := rows.Scan(&i.Tag, &i.TargetKind, &i.TargetID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const selectTagsByTargets = `-- name: SelectTagsByTargets :many +SELECT tag, target_kind, target_id FROM common_tag WHERE target_kind = $1 AND target_id = ANY($2::text[]) +` + +type SelectTagsByTargetsParams struct { + TargetKind string `json:"target_kind"` + Column2 []string `json:"column_2"` +} + +func (q *Queries) SelectTagsByTargets(ctx context.Context, arg SelectTagsByTargetsParams) ([]CommonTag, error) { + rows, err := q.db.QueryContext(ctx, selectTagsByTargets, arg.TargetKind, pq.Array(arg.Column2)) + if err != nil { + return nil, err + } + defer rows.Close() + items := []CommonTag{} + for rows.Next() { + var i CommonTag + if err := rows.Scan(&i.Tag, &i.TargetKind, &i.TargetID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const setTag = `-- name: SetTag :exec +INSERT INTO common_tag (tag, target_kind, target_id) +VALUES ( + $1::TEXT, $2::TEXT, $3::TEXT +) +ON CONFLICT DO NOTHING +` + +type SetTagParams struct { + Tag string `json:"tag"` + TargetKind string `json:"target_kind"` + TargetID string `json:"target_id"` +} + +func (q *Queries) SetTag(ctx context.Context, arg SetTagParams) error { + _, err := q.db.ExecContext(ctx, setTag, arg.Tag, arg.TargetKind, arg.TargetID) + return err +} diff --git a/database/postgres/queries/stories.sql b/database/postgres/queries/stories.sql new file mode 100644 index 0000000..eeefa54 --- /dev/null +++ b/database/postgres/queries/stories.sql @@ -0,0 +1,13 @@ +-- name: SelectStory :one +SELECT * FROM story WHERE id = $1 LIMIT 1; + +-- name: SelectStories :many +SELECT * FROM story +WHERE (sqlc.arg(filter_id)::bool = false OR id = ANY(sqlc.arg(ids)::text[])) + AND (sqlc.arg(filter_author)::bool = false OR name = sqlc.arg(author)::text) + AND (sqlc.arg(filter_earlist_fictional_date)::bool = false OR fictional_date >= sqlc.arg(earliest_fictional_date)::timestamp) + AND (sqlc.arg(filter_lastest_fictional_date)::bool = false OR fictional_date <= sqlc.arg(latest_fictional_date)::timestamp) + AND (sqlc.arg(filter_category)::bool = false OR category = sqlc.arg(category)::text) + AND (sqlc.arg(filter_open)::bool = false OR open = sqlc.arg(open)::bool) + AND (sqlc.arg(filter_unlisted)::bool = false OR unlisted = sqlc.arg(unlisted)::bool) +LIMIT sqlc.arg(limit_size)::int; diff --git a/database/postgres/queries/tags.sql b/database/postgres/queries/tags.sql new file mode 100644 index 0000000..1c9f3be --- /dev/null +++ b/database/postgres/queries/tags.sql @@ -0,0 +1,35 @@ +-- name: SetTag :exec +INSERT INTO common_tag (tag, target_kind, target_id) +VALUES ( + sqlc.arg(tag)::TEXT, sqlc.arg(target_kind)::TEXT, sqlc.arg(target_id)::TEXT +) +ON CONFLICT DO NOTHING; + +-- name: ClearTag :exec +DELETE FROM common_tag +WHERE tag=sqlc.arg(tag)::TEXT + AND target_kind=sqlc.arg(target_kind)::TEXT + AND target_id=sqlc.arg(target_id)::TEXT; + +-- name: ClearTagsByTarget :exec +DELETE FROM common_tag +WHERE target_kind=sqlc.arg(target_kind)::TEXT + AND target_id=sqlc.arg(target_id)::TEXT; + +-- name: SelectDistinctTags :many +SELECT DISTINCT tag FROM common_tag; + +-- name: SelectDistinctTagsByKind :many +SELECT DISTINCT tag FROM common_tag WHERE tag LIKE '$1::text%'; + +-- name: SelectTagsByTag :many +SELECT * FROM common_tag WHERE tag = sqlc.arg(tag_name)::text; + +-- name: SelectTagsByTags :many +SELECT * FROM common_tag WHERE tag = ANY(sqlc.arg(tag_names)::text[]); + +-- name: SelectTagsByTarget :many +SELECT * FROM common_tag WHERE target_kind = $1 AND target_id = $2::text; + +-- name: SelectTagsByTargets :many +SELECT * FROM common_tag WHERE target_kind = $1 AND target_id = ANY($2::text[]); diff --git a/database/postgres/stories.go b/database/postgres/stories.go new file mode 100644 index 0000000..273fb61 --- /dev/null +++ b/database/postgres/stories.go @@ -0,0 +1,129 @@ +package postgres + +import ( + "context" + "database/sql" + "git.aiterp.net/rpdata/api/database/postgres/psqlcore" + "git.aiterp.net/rpdata/api/models" +) + +type storyRepository struct { + insertWithIDs bool + db *sql.DB +} + +func (r *storyRepository) Find(ctx context.Context, id string) (*models.Story, error) { + q := psqlcore.New(r.db) + story, err := q.SelectStory(ctx, id) + if err != nil { + return nil, err + } + + tags, err := q.SelectTagsByTarget(ctx, psqlcore.SelectTagsByTargetParams{ + TargetKind: "Story", + TargetID: story.ID, + }) + if err != nil && err != sql.ErrNoRows { + return nil, err + } + + return r.story(story, tags), nil +} + +func (r *storyRepository) List(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) { + q := psqlcore.New(r.db) + params := psqlcore.SelectStoriesParams{LimitSize: 100} + + if len(filter.Tags) > 0 { + params.FilterID = true + if len(filter.Tags) == 1 { + tags, err := q.SelectTagsByKindName(ctx, psqlcore.SelectTagsByKindNameParams{ + Kind: string(filter.Tags[0].Kind), + Name: filter.Tags[0].Name, + }) + if err != nil && err != sql.ErrNoRows { + return nil, err + } + + for _, tag := range tags { + params.Ids = append(params.Ids, tag.TargetID) + } + } else { + + } + + if len(params.Ids) == 0 { + return []*models.Story{}, nil + } + } + if filter.Limit > 0 { + params.LimitSize = 1000000 + } + + panic("implement me") +} + +func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) { + panic("implement me") +} + +func (r *storyRepository) Update(ctx context.Context, story models.Story, update models.StoryUpdate) (*models.Story, error) { + panic("implement me") +} + +func (r *storyRepository) AddTag(ctx context.Context, story models.Story, tag models.Tag) error { + return psqlcore.New(r.db).SetTag(ctx, psqlcore.SetTagParams{ + Kind: string(tag.Kind), + Name: tag.Name, + TargetKind: "Story", + TargetID: story.ID, + }) +} + +func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag models.Tag) error { + return psqlcore.New(r.db).ClearTag(ctx, psqlcore.ClearTagParams{ + Kind: string(tag.Kind), + Name: tag.Name, + TargetKind: "Story", + TargetID: story.ID, + }) +} + +func (r *storyRepository) Delete(ctx context.Context, story models.Story) error { + panic("implement me") +} + +func (r *storyRepository) story(story psqlcore.Story, tags []psqlcore.CommonTag) *models.Story { + tags2 := make([]models.Tag, 0, 8) + for _, tag := range tags { + if tag.TargetKind == "Story" && tag.TargetID == story.ID { + tags2 = append(tags2, models.Tag{ + Kind: models.TagKind(tag.Kind), + Name: tag.Name, + }) + } + } + + return &models.Story{ + ID: story.ID, + Author: story.Author, + Name: story.Name, + Category: models.StoryCategory(story.Category), + Open: story.Open, + Listed: story.Listed, + Tags: tags2, + CreatedDate: story.CreatedDate, + FictionalDate: story.FictionalDate, + UpdatedDate: story.UpdatedDate, + SortByFictionalDate: story.SortByFictionalDate, + } +} + +func (r *storyRepository) stories(stories []psqlcore.Story, tags []psqlcore.CommonTag) []*models.Story { + results := make([]*models.Story, 0, len(stories)) + for _, story := range stories { + results = append(results, r.story(story, tags)) + } + + return results +} diff --git a/database/postgres/tags.go b/database/postgres/tags.go new file mode 100644 index 0000000..51f7f41 --- /dev/null +++ b/database/postgres/tags.go @@ -0,0 +1,46 @@ +package postgres + +import ( + "context" + "database/sql" + "fmt" + "git.aiterp.net/rpdata/api/database/postgres/psqlcore" + "git.aiterp.net/rpdata/api/models" +) + +type tagRepository struct { + db *sql.DB +} + +func (r *tagRepository) Find(ctx context.Context, kind models.TagKind, name string) (*models.Tag, error) { + tag, err := psqlcore.New(r.db).SelectTagsByTag(ctx, fmt.Sprintf("%s:%s", kind, nmae)) + if err != nil { + return nil, err + } + + return &models.Tag{Kind: models.TagKind(tag.Kind), Name: tag.Name}, nil +} + +func (r *tagRepository) List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) { + var tags []string + var err error + if filter.Kind != nil { + tags, err = psqlcore.New(r.db).SelectDistinctTagsByKind(ctx, string(*filter.Kind)) + } else { + tags, err = psqlcore.New(r.db).SelectDistinctTags(ctx) + } + if err != nil { + return nil, err + } + + return models.DecodeTagArray(tags) +} + +func modelsTagsFromDataTags(tags []psqlcore.CommonTag) []*models.Tag { + results := make([]*models.Tag, len(tags)) + for i, tag := range tags { + results[i] = &models.Tag{Kind: models.TagKind(tag.Kind), Name: tag.Name} + } + + return results +} diff --git a/models/tag.go b/models/tag.go index 2c3d05d..93c7209 100644 --- a/models/tag.go +++ b/models/tag.go @@ -55,9 +55,10 @@ func EncodeTagArray(arr []Tag) []string { return result } -func DecodeTagArray(arr []string) ([]Tag, error) { - result := make([]Tag, len(arr)) +func DecodeTagArray(arr []string) ([]*Tag, error) { + result := make([]*Tag, 0, len(arr)) for i, v := range arr { + result = append(result, &Tag{}) err := result[i].Decode(v) if err != nil { return nil, err