data_base

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 8 Imported by: 0

README

Data Base

import "github.com/Compogo/data_base"

Package data_base предоставляет типобезопасные, расширяемые и производительные компоненты для работы с реляционными базами данных в Go.

Основные возможности:

  • Универсальные компоненты для CRUD операций: Finder, Saver, Deleter и др.
  • Типобезопасные фильтры и сортировка с поддержкой распространённых операторов.
  • Поддержка пагинации через компонент Pager.
  • Гибкое преобразование между моделями и SQL-записями через функции-мапперы.

Все компоненты используют стандартный интерфейс dbClient.Client, что упрощает тестирование и позволяет легко подменять реализацию хранения данных.

Index

Variables

var FilterTypeUndefined = errors.New("undefined")

IdNotBeZeroError возвращается компонентом Updater (и другими операциями обновления), если переданная модель имеет нулевой идентификатор (GetId() == 0).

Такая проверка позволяет избежать случайного обновления всех записей в таблице (например, при WHERE id = 0) и даёт явный сигнал о некорректном вызове.

Пример:

user := &User{Name: "Alice"} // ID не установлен
_, err := updater.Update(ctx, user)
if errors.Is(err, data_base.IdNotBeZeroError) {
    // Обработка ошибки: ID обязателен для обновления
}
var IdNotBeZeroError = errors.New("id must not be null")

func NewFilter

func NewFilter(filter *repository.Filter) (goqu.Expression, error)

NewFilter преобразует Filter из пакета repository в goqu.Expression, который можно использовать для построения SQL-запросов. Поддерживает операторы: =, !=, >, >=, <, <=, LIKE, IN.

Возвращает ошибку FilterTypeUndefined, если переданный оператор не поддерживается.

Пример:

filter := repository.NewFilter("name", "Alice", repository.Eq)
expr, err := data_base.NewFilter(filter)

func NewSort

func NewSort(sort *repository.Sort) exp.OrderedExpression

NewSort преобразует Sort из пакета repository в goqu.OrderedExpression для сортировки результатов запроса.

Поддерживает направления: ASC (по умолчанию) и DESC.

Пример:

sort := &repository.Sort{ColumnName: "created_at", Direction: repository.DESC}
expr := data_base.NewSort(sort)

type BulkInserter

BulkInserter предназначен для массовой вставки записей (INSERT) одной операцией. Принимает срез указателей на модели и преобразует их в записи с помощью ModelToRecordFunc.

Значительно повышает производительность при вставке большого количества записей, сокращая количество сетевых вызовов к базе данных.

Пример:

bulk := &data_base.BulkInserter[User]{
    tableName: "users",
    modelToRecord: toRecord,
    db: db,
    gen: gen,
}
users := []*User{&User{Name: "A"}, &User{Name: "B"}}
err := bulk.BulkInsert(ctx, users...)
type BulkInserter[T any] struct {
    // contains filtered or unexported fields
}

func (*BulkInserter[T]) BulkInsert
func (b *BulkInserter[T]) BulkInsert(ctx context.Context, models ...*T) error

type Counter

Counter предназначен для подсчёта количества записей в таблице, удовлетворяющих переданным фильтрам.

Игнорирует значения NULL в указанной колонке для подсчёта. Для nil-фильтров возвращает общее количество записей в таблице.

Пример:

counter := data_base.NewCounter(db, gen, "users", "id")
filters := []*repository.Filter{repository.NewFilter("active", "true", repository.Eq)}
count, err := counter.Count(ctx, filters...)
type Counter struct {
    // contains filtered or unexported fields
}

func NewCounter
func NewCounter(db dbClient.Client, gen *goqu.DialectWrapper, tableName string, counterColName string) *Counter

func (*Counter) Count
func (c *Counter) Count(ctx context.Context, filters ...*repository.Filter) (uint64, error)

type Deleter

Deleter предназначен для удаления записей из таблицы по переданным фильтрам. Для выполнения физического удаления (DELETE) используйте фильтры. Для мягкого удаления рекомендуется использовать Updater.

Возвращает ошибку, если удаление не удалось выполнить.

Пример:

deleter := data_base.NewDeleter[*User](db, gen, "users")
filters := []*repository.Filter{repository.NewFilter("id", 123, repository.Eq)}
err := deleter.Delete(ctx, filters...)
type Deleter[T any] struct {
    // contains filtered or unexported fields
}

func NewDeleter
func NewDeleter[T any](db dbClient.Client, gen *goqu.DialectWrapper, tableName string) *Deleter[T]

func (*Deleter[T]) Delete
func (d *Deleter[T]) Delete(ctx context.Context, filters ...*repository.Filter) error

type Finder

Finder предназначен для поиска записей в базе данных с поддержкой фильтрации и сортировки. Возвращает срез указателей на модели типа T.

Нулевое значение структуры использовать нельзя. Для создания экземпляра используйте функцию NewFinder, передавая в неё экземпляр клиента БД, генератор SQL, функцию для маппинга строки результата в модель и название таблицы.

Пример:

rowMapper := func(rows *sql.Rows) (*User, error) {
    var u User
    err := rows.Scan(&u.ID, &u.Name, &u.Email)
    return &u, err
}

finder := data_base.NewFinder(db, goqu.Dialect("postgres"), rowMapper, "users", "id", "name", "email")
filters := []*repository.Filter{repository.NewFilter("name", "Alice", repository.Eq)}
users, err := finder.Find(ctx, nil, filters...)
type Finder[T any] struct {
    // contains filtered or unexported fields
}

func NewFinder
func NewFinder[T any](db dbClient.Client, gen *goqu.DialectWrapper, rowToModel RowToModelFunc[T], tableName string, columns ...any) *Finder[T]

func (*Finder[T]) Find
func (f *Finder[T]) Find(ctx context.Context, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type Identifier

Identifier определяет контракт для моделей, имеющих числовой идентификатор. Используется компонентами Saver, Inserter и Updater для определения типа операции и установки сгенерированных ID.

Пример реализации:

type User struct {
    ID uint64
}
func (u *User) GetId() uint64 { return u.ID }
func (u *User) SetId(id uint64) { u.ID = id }
type Identifier interface {
    GetId() uint64
    SetId(uint64)
}

type Inserter

Inserter предназначен только для вставки новых записей (INSERT). После успешной вставки автоматически устанавливает сгенерированный ID модели через SetId().

В отличие от Saver, не выполняет обновление при наличии ID. Полезен для сценариев, где нужно гарантировать создание новой записи.

Пример:

inserter := data_base.NewInserter(db, gen, toRecord, "users")
user := &User{Name: "Bob", Email: "bob@example.com"}
insertedUser, err := inserter.Insert(ctx, user)
type Inserter[T Identifier] struct {
    // contains filtered or unexported fields
}

func NewInserter
func NewInserter[T Identifier](db dbClient.Client, gen *goqu.DialectWrapper, modelToRecord ModelToRecordFunc[T], tableName string) *Inserter[T]

func (*Inserter[T]) Insert
func (i *Inserter[T]) Insert(ctx context.Context, model *T) (*T, error)

type ModelToRecordFunc

ModelToRecordFunc — сигнатура функции для преобразования указателя на модель, используется в запись для SQL-запроса (goqu.Record). Используется компонентами Saver, Inserter, Updater и BulkInserter.

Позволяет гибко контролировать, какие поля и как сохраняются в базе данных.

type ModelToRecordFunc[T any] func(*T) goqu.Record

type Pager

Pager предназначен для постраничного получения данных с фильтрацией и сортировкой. Является расширением Finder и использует тот же механизм маппинга строк в модели.

Поддерживает все виды фильтров и сортировок из пакета repository. Для работы с пагинацией используйте структуру repository.Page.

Пример:

pager := data_base.NewPager(db, gen, rowMapper, "users", "id", "name")
page := &repository.Page{Number: 0, Limit: 10}
users, err := pager.Page(ctx, page, sorts, filters...)
type Pager[T any] struct {
    // contains filtered or unexported fields
}

func NewPager
func NewPager[T any](db dbClient.Client, gen *goqu.DialectWrapper, rowToModel RowToModelFunc[T], tableName string, columns ...any) *Pager[T]

func (*Pager[T]) Page
func (p *Pager[T]) Page(ctx context.Context, page *repository.Page, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type RowToModelFunc

RowToModelFunc — сигнатура функции для преобразования строки результата SQL-запроса (*sql.Rows), используется в экземпляр модели типа T. Используется компонентами Finder и Pager.

Возвращает указатель на модель и ошибку, если сканирование не удалось.

type RowToModelFunc[T any] func(*sql.Rows) (*T, error)

type Saver

Saver реализует паттерн "сохранить или обновить" (UPSERT), автоматически определяя операцию по значению идентификатора модели.

Если GetId() возвращает 0, выполняется вставка (INSERT) новой записи, после чего модели автоматически устанавливается сгенерированный ID через SetId(). Если GetId() не равен 0, выполняется обновление (UPDATE) существующей записи.

Требует, чтобы тип T реализовывал интерфейс Identifier. Для работы необходима функция, преобразующая модель в goqu.Record.

Пример:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

saver := data_base.NewSaver(db, gen, toRecord, "users", "id")
user := &User{Name: "Alice", Email: "alice@example.com"}
savedUser, err := saver.Save(ctx, user) // user.ID будет установлен после вставки
type Saver[T Identifier] struct {
    // contains filtered or unexported fields
}

func NewSaver
func NewSaver[T Identifier](db dbClient.Client, gen *goqu.DialectWrapper, modelToRecord ModelToRecordFunc[T], tableName string, idColName string) *Saver[T]

func (*Saver[T]) Save
func (s *Saver[T]) Save(ctx context.Context, model *T) (*T, error)

type Updater

Updater предназначен для обновления существующих записей в таблице. В отличие от Saver, Updater не выполняет вставку новых записей и требует, чтобы переданная модель имела заполненный идентификатор (GetId() != 0).

Если идентификатор модели равен 0, метод Update возвращает ошибку IdNotBeZeroError. Это предотвращает случайное выполнение UPDATE без условия WHERE.

Компонент использует функцию ModelToRecordFunc для преобразования модели в запись (goqu.Record) и выполняет обновление по указанному полю идентификатора.

Пример использования:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

updater := data_base.NewUpdater(
    db,
    goqu.Dialect("postgres"),
    toRecord,
    "users",
    "id",
)

user := &User{Id: 123, Name: "Updated Name"}
updatedUser, err := updater.Update(ctx, user)
if err != nil {
    // Обработка ошибки
}
type Updater[T Identifier] struct {
    // contains filtered or unexported fields
}

func NewUpdater
func NewUpdater[T Identifier](db dbClient.Client, gen *goqu.DialectWrapper, modelToRecord ModelToRecordFunc[T], tableName string, idColName string) *Updater[T]

func (*Updater[T]) Update
func (u *Updater[T]) Update(ctx context.Context, model *T) (*T, error)

Generated by gomarkdoc

Documentation

Overview

Package data_base предоставляет типобезопасные, расширяемые и производительные компоненты для работы с реляционными базами данных в Go.

Основные возможности:

  • Универсальные компоненты для CRUD операций: Finder, Saver, Deleter и др.
  • Типобезопасные фильтры и сортировка с поддержкой распространённых операторов.
  • Поддержка пагинации через компонент Pager.
  • Гибкое преобразование между моделями и SQL-записями через функции-мапперы.

Все компоненты используют стандартный интерфейс dbClient.Client, что упрощает тестирование и позволяет легко подменять реализацию хранения данных.

Index

Constants

This section is empty.

Variables

View Source
var FilterTypeUndefined = errors.New("undefined")
View Source
var IdNotBeZeroError = errors.New("id must not be null")

IdNotBeZeroError возвращается компонентом Updater (и другими операциями обновления), если переданная модель имеет нулевой идентификатор (GetId() == 0).

Такая проверка позволяет избежать случайного обновления всех записей в таблице (например, при WHERE id = 0) и даёт явный сигнал о некорректном вызове.

Пример:

user := &User{Name: "Alice"} // ID не установлен
_, err := updater.Update(ctx, user)
if errors.Is(err, data_base.IdNotBeZeroError) {
    // Обработка ошибки: ID обязателен для обновления
}

Functions

func NewFilter

func NewFilter(filter *repository.Filter) (goqu.Expression, error)

NewFilter преобразует Filter из пакета repository в goqu.Expression, который можно использовать для построения SQL-запросов. Поддерживает операторы: =, !=, >, >=, <, <=, LIKE, IN.

Возвращает ошибку FilterTypeUndefined, если переданный оператор не поддерживается.

Пример:

filter := repository.NewFilter("name", "Alice", repository.Eq)
expr, err := data_base.NewFilter(filter)

func NewSort

func NewSort(sort *repository.Sort) exp.OrderedExpression

NewSort преобразует Sort из пакета repository в goqu.OrderedExpression для сортировки результатов запроса.

Поддерживает направления: ASC (по умолчанию) и DESC.

Пример:

sort := &repository.Sort{ColumnName: "created_at", Direction: repository.DESC}
expr := data_base.NewSort(sort)

Types

type BulkInserter

type BulkInserter[T any] struct {
	// contains filtered or unexported fields
}

BulkInserter предназначен для массовой вставки записей (INSERT) одной операцией. Принимает срез указателей на модели и преобразует их в записи с помощью ModelToRecordFunc.

Значительно повышает производительность при вставке большого количества записей, сокращая количество сетевых вызовов к базе данных.

Пример:

bulk := &data_base.BulkInserter[User]{
    tableName: "users",
    modelToRecord: toRecord,
    db: db,
    gen: gen,
}
users := []*User{&User{Name: "A"}, &User{Name: "B"}}
err := bulk.BulkInsert(ctx, users...)

func (*BulkInserter[T]) BulkInsert

func (b *BulkInserter[T]) BulkInsert(ctx context.Context, models ...*T) error

type Counter

type Counter struct {
	// contains filtered or unexported fields
}

Counter предназначен для подсчёта количества записей в таблице, удовлетворяющих переданным фильтрам.

Игнорирует значения NULL в указанной колонке для подсчёта. Для nil-фильтров возвращает общее количество записей в таблице.

Пример:

counter := data_base.NewCounter(db, gen, "users", "id")
filters := []*repository.Filter{repository.NewFilter("active", "true", repository.Eq)}
count, err := counter.Count(ctx, filters...)

func NewCounter

func NewCounter(db dbClient.Client, gen *goqu.DialectWrapper, tableName string, counterColName string) *Counter

func (*Counter) Count

func (c *Counter) Count(ctx context.Context, filters ...*repository.Filter) (uint64, error)

type Deleter

type Deleter[T any] struct {
	// contains filtered or unexported fields
}

Deleter предназначен для удаления записей из таблицы по переданным фильтрам. Для выполнения физического удаления (DELETE) используйте фильтры. Для мягкого удаления рекомендуется использовать Updater.

Возвращает ошибку, если удаление не удалось выполнить.

Пример:

deleter := data_base.NewDeleter[*User](db, gen, "users")
filters := []*repository.Filter{repository.NewFilter("id", 123, repository.Eq)}
err := deleter.Delete(ctx, filters...)

func NewDeleter

func NewDeleter[T any](db dbClient.Client, gen *goqu.DialectWrapper, tableName string) *Deleter[T]

func (*Deleter[T]) Delete

func (d *Deleter[T]) Delete(ctx context.Context, filters ...*repository.Filter) error

type Finder

type Finder[T any] struct {
	// contains filtered or unexported fields
}

Finder предназначен для поиска записей в базе данных с поддержкой фильтрации и сортировки. Возвращает срез указателей на модели типа T.

Нулевое значение структуры использовать нельзя. Для создания экземпляра используйте функцию NewFinder, передавая в неё экземпляр клиента БД, генератор SQL, функцию для маппинга строки результата в модель и название таблицы.

Пример:

rowMapper := func(rows *sql.Rows) (*User, error) {
    var u User
    err := rows.Scan(&u.ID, &u.Name, &u.Email)
    return &u, err
}

finder := data_base.NewFinder(db, goqu.Dialect("postgres"), rowMapper, "users", "id", "name", "email")
filters := []*repository.Filter{repository.NewFilter("name", "Alice", repository.Eq)}
users, err := finder.Find(ctx, nil, filters...)

func NewFinder

func NewFinder[T any](db dbClient.Client, gen *goqu.DialectWrapper, rowToModel RowToModelFunc[T], tableName string, columns ...any) *Finder[T]

func (*Finder[T]) Find

func (f *Finder[T]) Find(ctx context.Context, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type Identifier

type Identifier interface {
	GetId() uint64
	SetId(uint64)
}

Identifier определяет контракт для моделей, имеющих числовой идентификатор. Используется компонентами Saver, Inserter и Updater для определения типа операции и установки сгенерированных ID.

Пример реализации:

type User struct {
    ID uint64
}
func (u *User) GetId() uint64 { return u.ID }
func (u *User) SetId(id uint64) { u.ID = id }

type Inserter

type Inserter[T Identifier] struct {
	// contains filtered or unexported fields
}

Inserter предназначен только для вставки новых записей (INSERT). После успешной вставки автоматически устанавливает сгенерированный ID модели через SetId().

В отличие от Saver, не выполняет обновление при наличии ID. Полезен для сценариев, где нужно гарантировать создание новой записи.

Пример:

inserter := data_base.NewInserter(db, gen, toRecord, "users")
user := &User{Name: "Bob", Email: "bob@example.com"}
insertedUser, err := inserter.Insert(ctx, user)

func NewInserter

func NewInserter[T Identifier](db dbClient.Client, gen *goqu.DialectWrapper, modelToRecord ModelToRecordFunc[T], tableName string) *Inserter[T]

func (*Inserter[T]) Insert

func (i *Inserter[T]) Insert(ctx context.Context, model *T) (*T, error)

type ModelToRecordFunc

type ModelToRecordFunc[T any] func(*T) goqu.Record

ModelToRecordFunc — сигнатура функции для преобразования указателя на модель, используется в запись для SQL-запроса (goqu.Record). Используется компонентами Saver, Inserter, Updater и BulkInserter.

Позволяет гибко контролировать, какие поля и как сохраняются в базе данных.

type Pager

type Pager[T any] struct {
	// contains filtered or unexported fields
}

Pager предназначен для постраничного получения данных с фильтрацией и сортировкой. Является расширением Finder и использует тот же механизм маппинга строк в модели.

Поддерживает все виды фильтров и сортировок из пакета repository. Для работы с пагинацией используйте структуру repository.Page.

Пример:

pager := data_base.NewPager(db, gen, rowMapper, "users", "id", "name")
page := &repository.Page{Number: 0, Limit: 10}
users, err := pager.Page(ctx, page, sorts, filters...)

func NewPager

func NewPager[T any](db dbClient.Client, gen *goqu.DialectWrapper, rowToModel RowToModelFunc[T], tableName string, columns ...any) *Pager[T]

func (*Pager[T]) Page

func (p *Pager[T]) Page(ctx context.Context, page *repository.Page, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type RowToModelFunc

type RowToModelFunc[T any] func(*sql.Rows) (*T, error)

RowToModelFunc — сигнатура функции для преобразования строки результата SQL-запроса (*sql.Rows), используется в экземпляр модели типа T. Используется компонентами Finder и Pager.

Возвращает указатель на модель и ошибку, если сканирование не удалось.

type Saver

type Saver[T Identifier] struct {
	// contains filtered or unexported fields
}

Saver реализует паттерн "сохранить или обновить" (UPSERT), автоматически определяя операцию по значению идентификатора модели.

Если GetId() возвращает 0, выполняется вставка (INSERT) новой записи, после чего модели автоматически устанавливается сгенерированный ID через SetId(). Если GetId() не равен 0, выполняется обновление (UPDATE) существующей записи.

Требует, чтобы тип T реализовывал интерфейс Identifier. Для работы необходима функция, преобразующая модель в goqu.Record.

Пример:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

saver := data_base.NewSaver(db, gen, toRecord, "users", "id")
user := &User{Name: "Alice", Email: "alice@example.com"}
savedUser, err := saver.Save(ctx, user) // user.ID будет установлен после вставки

func NewSaver

func NewSaver[T Identifier](db dbClient.Client, gen *goqu.DialectWrapper, modelToRecord ModelToRecordFunc[T], tableName string, idColName string) *Saver[T]

func (*Saver[T]) Save

func (s *Saver[T]) Save(ctx context.Context, model *T) (*T, error)

type Updater

type Updater[T Identifier] struct {
	// contains filtered or unexported fields
}

Updater предназначен для обновления существующих записей в таблице. В отличие от Saver, Updater не выполняет вставку новых записей и требует, чтобы переданная модель имела заполненный идентификатор (GetId() != 0).

Если идентификатор модели равен 0, метод Update возвращает ошибку IdNotBeZeroError. Это предотвращает случайное выполнение UPDATE без условия WHERE.

Компонент использует функцию ModelToRecordFunc для преобразования модели в запись (goqu.Record) и выполняет обновление по указанному полю идентификатора.

Пример использования:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

updater := data_base.NewUpdater(
    db,
    goqu.Dialect("postgres"),
    toRecord,
    "users",
    "id",
)

user := &User{Id: 123, Name: "Updated Name"}
updatedUser, err := updater.Update(ctx, user)
if err != nil {
    // Обработка ошибки
}

func NewUpdater

func NewUpdater[T Identifier](db dbClient.Client, gen *goqu.DialectWrapper, modelToRecord ModelToRecordFunc[T], tableName string, idColName string) *Updater[T]

func (*Updater[T]) Update

func (u *Updater[T]) Update(ctx context.Context, model *T) (*T, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL