rel

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Feb 15, 2021 License: MIT Imports: 16 Imported by: 109

README

REL

GoDoc Build Status Go Report Card Maintainability Codecov Gitter chat

Modern Database Access Layer for Golang.

REL is golang orm-ish database layer for layered architecture. It's testable and comes with its own test library. REL also features extendable query builder that allows you to write query using builder or plain sql.

Features

  • Testable repository with builtin reltest package.
  • Elegant, yet extendable query builder with mix of syntactic sugar.
  • Supports Eager loading.
  • Supports nested transactions.
  • Composite Primary Key.
  • Multi adapter.
  • Soft Deletion.
  • Pagination.
  • Schema Migration.

Install

go get github.com/go-rel/rel

Getting Started

Examples

License

Released under the MIT License

Documentation

Overview

Package rel contains all rel primary APIs, such as Repository.

Index

Constants

View Source
const (
	// BelongsTo association.
	BelongsTo = iota
	// HasOne association.
	HasOne
	// HasMany association.
	HasMany
)

Variables

View Source
var (
	// ErrNotFound returned when records not found.
	ErrNotFound = NotFoundError{}

	// ErrCheckConstraint is an auxiliary variable for error handling.
	// This is only to be used when checking error with errors.Is(err, ErrCheckConstraint).
	ErrCheckConstraint = ConstraintError{Type: CheckConstraint}

	// ErrNotNullConstraint is an auxiliary variable for error handling.
	// This is only to be used when checking error with errors.Is(err, ErrNotNullConstraint).
	ErrNotNullConstraint = ConstraintError{Type: NotNullConstraint}

	// ErrUniqueConstraint is an auxiliary variable for error handling.
	// This is only to be used when checking error with errors.Is(err, ErrUniqueConstraint).
	ErrUniqueConstraint = ConstraintError{Type: UniqueConstraint}

	// ErrPrimaryKeyConstraint is an auxiliary variable for error handling.
	// This is only to be used when checking error with errors.Is(err, ErrPrimaryKeyConstraint).
	ErrPrimaryKeyConstraint = ConstraintError{Type: PrimaryKeyConstraint}

	// ErrForeignKeyConstraint is an auxiliary variable for error handling.
	// This is only to be used when checking error with errors.Is(err, ErrForeignKeyConstraint).
	ErrForeignKeyConstraint = ConstraintError{Type: ForeignKeyConstraint}
)

Setf is an alias for SetFragment

Functions

func DefaultLogger

func DefaultLogger(ctx context.Context, op string, message string) func(err error)

DefaultLogger instrumentation to log queries and rel operation.

func Nullable

func Nullable(dest interface{}) interface{}

Nullable wrap value as a nullable sql.Scanner. If value returned from database is nil, nullable scanner will set dest to zero value.

Types

type Adapter

type Adapter interface {
	Instrumentation(instrumenter Instrumenter)
	Ping(ctx context.Context) error
	Aggregate(ctx context.Context, query Query, mode string, field string) (int, error)
	Query(ctx context.Context, query Query) (Cursor, error)
	Insert(ctx context.Context, query Query, primaryField string, mutates map[string]Mutate) (interface{}, error)
	InsertAll(ctx context.Context, query Query, primaryField string, fields []string, bulkMutates []map[string]Mutate) ([]interface{}, error)
	Update(ctx context.Context, query Query, mutates map[string]Mutate) (int, error)
	Delete(ctx context.Context, query Query) (int, error)

	Begin(ctx context.Context) (Adapter, error)
	Commit(ctx context.Context) error
	Rollback(ctx context.Context) error

	Apply(ctx context.Context, migration Migration) error
}

Adapter interface

type AlterTable

type AlterTable struct {
	Table
}

AlterTable Migrator.

func (*AlterTable) DropColumn

func (at *AlterTable) DropColumn(name string, options ...ColumnOption)

DropColumn from this table.

func (*AlterTable) RenameColumn

func (at *AlterTable) RenameColumn(name string, newName string, options ...ColumnOption)

RenameColumn to a new name.

type AssocMutation

type AssocMutation struct {
	Mutations  []Mutation
	DeletedIDs []interface{} // This is array of single id, and doesn't support composite primary key.
}

AssocMutation represents mutation for association.

type Association

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

Association provides abstraction to work with association of document or collection.

func (Association) Autoload added in v0.10.0

func (a Association) Autoload() bool

Autoload assoc setting when parent is loaded.

func (Association) Autosave added in v0.9.0

func (a Association) Autosave() bool

Autosave setting when parent is created/updated/deleted.

func (Association) Collection

func (a Association) Collection() (*Collection, bool)

Collection returns association target as collection. If association is zero, second return value will be false.

func (Association) Document

func (a Association) Document() (*Document, bool)

Document returns association target as document. If association is zero, second return value will be false.

func (Association) ForeignField

func (a Association) ForeignField() string

ForeignField of the association.

func (Association) ForeignValue

func (a Association) ForeignValue() interface{}

ForeignValue of the association. It'll panic if association type is has many.

func (Association) IsZero

func (a Association) IsZero() bool

IsZero returns true if association is not loaded.

func (Association) ReferenceField

func (a Association) ReferenceField() string

ReferenceField of the association.

func (Association) ReferenceValue

func (a Association) ReferenceValue() interface{}

ReferenceValue of the association.

func (Association) Through added in v0.9.0

func (a Association) Through() string

Through return intermediary association.

func (Association) Type

func (a Association) Type() AssociationType

Type of association.

type AssociationType

type AssociationType uint8

AssociationType defines the type of association in database.

type Cascade

type Cascade bool

Cascade enable or disable updating associations. Default to true.

func (Cascade) Apply

func (c Cascade) Apply(doc *Document, mutation *Mutation)

Apply mutation.

func (Cascade) Build added in v0.10.0

func (c Cascade) Build(query *Query)

Build query.

type ChangeOp

type ChangeOp int

ChangeOp represents type of mutate operation.

const (
	// ChangeInvalidOp operation.
	ChangeInvalidOp ChangeOp = iota
	// ChangeSetOp operation.
	ChangeSetOp
	// ChangeIncOp operation.
	ChangeIncOp
	// ChangeFragmentOp operation.
	ChangeFragmentOp
)

type Changeset

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

Changeset mutator for structs. This allows REL to efficiently to perform update operation only on updated fields and association. The catch is, enabling changeset will duplicates the original struct values which consumes more memory.

func NewChangeset

func NewChangeset(record interface{}) Changeset

NewChangeset returns new changeset mutator for given record.

func (Changeset) Apply

func (c Changeset) Apply(doc *Document, mut *Mutation)

Apply mutation.

func (Changeset) Changes

func (c Changeset) Changes() map[string]interface{}

Changes returns map of changes.

func (Changeset) FieldChanged

func (c Changeset) FieldChanged(field string) bool

FieldChanged returns true if field exists and it's already changed. returns false otherwise.

type Collection

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

Collection provides an abstraction over reflect to easily works with slice for database purpose.

func NewCollection

func NewCollection(records interface{}, readonly ...bool) *Collection

NewCollection used to create abstraction to work with slice. COllection can be created using interface or reflect.Value.

func (Collection) Add

func (c Collection) Add() *Document

Add new document into collection.

func (Collection) Get

func (c Collection) Get(index int) *Document

Get an element from the underlying slice as a document.

func (Collection) Len

func (c Collection) Len() int

Len of the underlying slice.

func (Collection) PrimaryField

func (c Collection) PrimaryField() string

PrimaryField column name of this document. panic if document uses composite key.

func (Collection) PrimaryFields

func (c Collection) PrimaryFields() []string

PrimaryFields column name of this collection.

func (Collection) PrimaryValue

func (c Collection) PrimaryValue() interface{}

PrimaryValue of this document. panic if document uses composite key.

func (Collection) PrimaryValues

func (c Collection) PrimaryValues() []interface{}

PrimaryValues of collection. Returned value will be interface of slice interface.

func (Collection) ReflectValue

func (c Collection) ReflectValue() reflect.Value

ReflectValue of referenced document.

func (Collection) Reset

func (c Collection) Reset()

Reset underlying slice to be zero length.

func (Collection) Slice

func (c Collection) Slice(i, j int) *Collection

Slice returns a new collection that is a slice of the original collection.s

func (Collection) Swap

func (c Collection) Swap(i, j int)

Swap element in the collection.

func (*Collection) Table

func (c *Collection) Table() string

Table returns name of the table.

func (Collection) Truncate

func (c Collection) Truncate(i, j int)

Truncate collection.

type Column

type Column struct {
	Op        SchemaOp
	Name      string
	Type      ColumnType
	Rename    string
	Unique    bool
	Required  bool
	Unsigned  bool
	Limit     int
	Precision int
	Scale     int
	Default   interface{}
	Options   string
}

Column definition.

type ColumnOption

type ColumnOption interface {
	// contains filtered or unexported methods
}

ColumnOption interface. Available options are: Nil, Unsigned, Limit, Precision, Scale, Default, Comment, Options.

func Default

func Default(def interface{}) ColumnOption

Default allows to set a default value on the column.).

type ColumnType

type ColumnType string

ColumnType definition.

const (
	// ID ColumnType.
	ID ColumnType = "ID"
	// Bool ColumnType.
	Bool ColumnType = "BOOL"
	// SmallInt ColumnType.
	SmallInt ColumnType = "SMALLINT"
	// Int ColumnType.
	Int ColumnType = "INT"
	// BigInt ColumnType.
	BigInt ColumnType = "BIGINT"
	// Float ColumnType.
	Float ColumnType = "FLOAT"
	// Decimal ColumnType.
	Decimal ColumnType = "DECIMAL"
	// String ColumnType.
	String ColumnType = "STRING"
	// Text ColumnType.
	Text ColumnType = "TEXT"
	// Date ColumnType.
	Date ColumnType = "DATE"
	// DateTime ColumnType.
	DateTime ColumnType = "DATETIME"
	// Time ColumnType.
	Time ColumnType = "TIME"
	// Timestamp ColumnType.
	Timestamp ColumnType = "TIMESTAMP"
)

type ConstraintError

type ConstraintError struct {
	Key  string
	Type ConstraintType
	Err  error
}

ConstraintError returned whenever constraint error encountered.

func (ConstraintError) Error

func (ce ConstraintError) Error() string

Error message.

func (ConstraintError) Is

func (ce ConstraintError) Is(target error) bool

Is returns true when target error have the same type and key if defined.

func (ConstraintError) Unwrap

func (ce ConstraintError) Unwrap() error

Unwrap internal error returned by database driver.

type ConstraintType

type ConstraintType int8

ConstraintType defines the type of constraint error.

const (
	// CheckConstraint error type.
	CheckConstraint ConstraintType = iota
	// NotNullConstraint error type.1
	NotNullConstraint
	// UniqueConstraint error type.1
	UniqueConstraint
	// PrimaryKeyConstraint error type.1
	PrimaryKeyConstraint
	// ForeignKeyConstraint error type.1
	ForeignKeyConstraint
)

func (ConstraintType) String

func (ct ConstraintType) String() string

String representation of the constraint type.

type Cursor

type Cursor interface {
	Close() error
	Fields() ([]string, error)
	Next() bool
	Scan(...interface{}) error
	NopScanner() interface{} // TODO: conflict with manual scanners interface
}

Cursor is interface to work with database result (used by adapter).

type Do

type Do func(Repository) error

Do used internally for schema migration.

type Document

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

Document provides an abstraction over reflect to easily works with struct for database purpose.

func NewDocument

func NewDocument(record interface{}, readonly ...bool) *Document

NewDocument used to create abstraction to work with struct. Document can be created using interface or reflect.Value.

func (*Document) Add

func (d *Document) Add() *Document

Add returns this document, this is a noop for compatibility with collection.

func (Document) Association

func (d Document) Association(name string) Association

Association of this document with given name.

func (Document) BelongsTo

func (d Document) BelongsTo() []string

BelongsTo fields of this document.

func (Document) Fields

func (d Document) Fields() []string

Fields returns list of fields available on this document.

func (Document) Flag

func (d Document) Flag(flag DocumentFlag) bool

Flag returns true if struct contains specified flag.

func (*Document) Get

func (d *Document) Get(index int) *Document

Get always returns this document, this is a noop for compatibility with collection.

func (Document) HasMany

func (d Document) HasMany() []string

HasMany fields of this document.

func (Document) HasOne

func (d Document) HasOne() []string

HasOne fields of this document.

func (Document) Index

func (d Document) Index() map[string]int

Index returns map of column name and it's struct index.

func (*Document) Len

func (d *Document) Len() int

Len always returns 1 for document, this is a noop for compatibility with collection.

func (Document) Persisted

func (d Document) Persisted() bool

Persisted returns true if document primary key is not zero.

func (Document) Preload added in v0.10.0

func (d Document) Preload() []string

Preload fields of this document.

func (Document) PrimaryField

func (d Document) PrimaryField() string

PrimaryField column name of this document. panic if document uses composite key.

func (Document) PrimaryFields

func (d Document) PrimaryFields() []string

PrimaryFields column name of this document.

func (Document) PrimaryValue

func (d Document) PrimaryValue() interface{}

PrimaryValue of this document. panic if document uses composite key.

func (Document) PrimaryValues

func (d Document) PrimaryValues() []interface{}

PrimaryValues of this document.

func (Document) ReflectValue

func (d Document) ReflectValue() reflect.Value

ReflectValue of referenced document.

func (Document) Reset

func (d Document) Reset()

Reset this document, this is a noop for compatibility with collection.

func (Document) Scanners

func (d Document) Scanners(fields []string) []interface{}

Scanners returns slice of sql.Scanner for given fields.

func (Document) SetValue

func (d Document) SetValue(field string, value interface{}) bool

SetValue of the field, it returns false if field does not exist, or it's not assignable.

func (Document) Table

func (d Document) Table() string

Table returns name of the table.

func (Document) Type

func (d Document) Type(field string) (reflect.Type, bool)

Type returns reflect.Type of given field. if field does not exist, second returns value will be false.

func (Document) Value

func (d Document) Value(field string) (interface{}, bool)

Value returns value of given field. if field does not exist, second returns value will be false.

type DocumentFlag

type DocumentFlag int8

DocumentFlag stores information about document as a flag.

const (
	// Invalid flag.
	Invalid DocumentFlag = 1 << iota
	// HasCreatedAt flag.
	HasCreatedAt
	// HasUpdatedAt flag.
	HasUpdatedAt
	// HasDeletedAt flag.
	HasDeletedAt
)

func (DocumentFlag) Is

func (df DocumentFlag) Is(flag DocumentFlag) bool

Is returns true if it's defined.

type ErrorFunc

type ErrorFunc func(error) error

ErrorFunc allows conversion REL's error to Application custom errors.

func (ErrorFunc) Apply

func (ef ErrorFunc) Apply(doc *Document, mutation *Mutation)

Apply mutation.

type FilterOp

type FilterOp int

FilterOp defines enumeration of all supported filter types.

const (
	// FilterAndOp is filter type for and operator.
	FilterAndOp FilterOp = iota
	// FilterOrOp is filter type for or operator.
	FilterOrOp
	// FilterNotOp is filter type for not operator.
	FilterNotOp

	// FilterEqOp is filter type for equal comparison.
	FilterEqOp
	// FilterNeOp is filter type for not equal comparison.
	FilterNeOp

	// FilterLtOp is filter type for less than comparison.
	FilterLtOp
	// FilterLteOp is filter type for less than or equal comparison.
	FilterLteOp
	// FilterGtOp is filter type for greater than comparison.
	FilterGtOp
	// FilterGteOp is filter type for greter than or equal comparison.
	FilterGteOp

	// FilterNilOp is filter type for nil check.
	FilterNilOp
	// FilterNotNilOp is filter type for not nil check.
	FilterNotNilOp

	// FilterInOp is filter type for inclusion comparison.
	FilterInOp
	// FilterNinOp is filter type for not inclusion comparison.
	FilterNinOp

	// FilterLikeOp is filter type for like comparison.
	FilterLikeOp
	// FilterNotLikeOp is filter type for not like comparison.
	FilterNotLikeOp

	// FilterFragmentOp is filter type for custom filter.
	FilterFragmentOp
)

type FilterQuery

type FilterQuery struct {
	Type  FilterOp
	Field string
	Value interface{}
	Inner []FilterQuery
}

FilterQuery defines details of a coundition type.

func And

func And(inner ...FilterQuery) FilterQuery

And compares other filters using and.

func Eq

func Eq(field string, value interface{}) FilterQuery

Eq expression field equal to value.

func FilterFragment

func FilterFragment(expr string, values ...interface{}) FilterQuery

FilterFragment add custom filter.

func Gt

func Gt(field string, value interface{}) FilterQuery

Gt compares that left value is greater than to right value.

func Gte

func Gte(field string, value interface{}) FilterQuery

Gte compares that left value is greater than or equal to right value.

func In

func In(field string, values ...interface{}) FilterQuery

In check whethers value of the field is included in values.

func InInt

func InInt(field string, values []int) FilterQuery

InInt check whethers integer values of the field is included.

func InString

func InString(field string, values []string) FilterQuery

InString check whethers string values of the field is included.

func InUint

func InUint(field string, values []uint) FilterQuery

InUint check whethers unsigned integer values of the field is included.

func Like

func Like(field string, pattern string) FilterQuery

Like compares value of field to match string pattern.

func Lt

func Lt(field string, value interface{}) FilterQuery

Lt compares that left value is less than to right value.

func Lte

func Lte(field string, value interface{}) FilterQuery

Lte compares that left value is less than or equal to right value.

func Ne

func Ne(field string, value interface{}) FilterQuery

Ne compares that left value is not equal to right value.

func Nil

func Nil(field string) FilterQuery

Nil check whether field is nil.

func Nin

func Nin(field string, values ...interface{}) FilterQuery

Nin check whethers value of the field is not included in values.

func NinInt

func NinInt(field string, values []int) FilterQuery

NinInt check whethers integer values of the is not included.

func NinString

func NinString(field string, values []string) FilterQuery

NinString check whethers string values of the is not included.

func NinUint

func NinUint(field string, values []uint) FilterQuery

NinUint check whethers unsigned integer values of the is not included.

func Not

func Not(inner ...FilterQuery) FilterQuery

Not wraps filters using not. It'll negate the filter type if possible.

func NotLike

func NotLike(field string, pattern string) FilterQuery

NotLike compares value of field to not match string pattern.

func NotNil

func NotNil(field string) FilterQuery

NotNil check whether field is not nil.

func Or

func Or(inner ...FilterQuery) FilterQuery

Or compares other filters using and.

func (FilterQuery) And

func (fq FilterQuery) And(filters ...FilterQuery) FilterQuery

And wraps filters using and.

func (FilterQuery) AndEq

func (fq FilterQuery) AndEq(field string, value interface{}) FilterQuery

AndEq append equal expression using and.

func (FilterQuery) AndFragment

func (fq FilterQuery) AndFragment(expr string, values ...interface{}) FilterQuery

AndFragment append fragment using and.

func (FilterQuery) AndGt

func (fq FilterQuery) AndGt(field string, value interface{}) FilterQuery

AndGt append greater than expression using and.

func (FilterQuery) AndGte

func (fq FilterQuery) AndGte(field string, value interface{}) FilterQuery

AndGte append greater than or equal expression using and.

func (FilterQuery) AndIn

func (fq FilterQuery) AndIn(field string, values ...interface{}) FilterQuery

AndIn append is in expression using and.

func (FilterQuery) AndLike

func (fq FilterQuery) AndLike(field string, pattern string) FilterQuery

AndLike append like expression using and.

func (FilterQuery) AndLt

func (fq FilterQuery) AndLt(field string, value interface{}) FilterQuery

AndLt append lesser than expression using and.

func (FilterQuery) AndLte

func (fq FilterQuery) AndLte(field string, value interface{}) FilterQuery

AndLte append lesser than or equal expression using and.

func (FilterQuery) AndNe

func (fq FilterQuery) AndNe(field string, value interface{}) FilterQuery

AndNe append not equal expression using and.

func (FilterQuery) AndNil

func (fq FilterQuery) AndNil(field string) FilterQuery

AndNil append is nil expression using and.

func (FilterQuery) AndNin

func (fq FilterQuery) AndNin(field string, values ...interface{}) FilterQuery

AndNin append is not in expression using and.

func (FilterQuery) AndNotLike

func (fq FilterQuery) AndNotLike(field string, pattern string) FilterQuery

AndNotLike append not like expression using and.

func (FilterQuery) AndNotNil

func (fq FilterQuery) AndNotNil(field string) FilterQuery

AndNotNil append is not nil expression using and.

func (FilterQuery) Build

func (fq FilterQuery) Build(query *Query)

Build Filter query.

func (FilterQuery) None

func (fq FilterQuery) None() bool

None returns true if no filter is specified.

func (FilterQuery) Or

func (fq FilterQuery) Or(filter ...FilterQuery) FilterQuery

Or wraps filters using or.

func (FilterQuery) OrEq

func (fq FilterQuery) OrEq(field string, value interface{}) FilterQuery

OrEq append equal expression using or.

func (FilterQuery) OrFragment

func (fq FilterQuery) OrFragment(expr string, values ...interface{}) FilterQuery

OrFragment append fragment using or.

func (FilterQuery) OrGt

func (fq FilterQuery) OrGt(field string, value interface{}) FilterQuery

OrGt append greater than expression using or.

func (FilterQuery) OrGte

func (fq FilterQuery) OrGte(field string, value interface{}) FilterQuery

OrGte append greater than or equal expression using or.

func (FilterQuery) OrIn

func (fq FilterQuery) OrIn(field string, values ...interface{}) FilterQuery

OrIn append is in expression using or.

func (FilterQuery) OrLike

func (fq FilterQuery) OrLike(field string, pattern string) FilterQuery

OrLike append like expression using or.

func (FilterQuery) OrLt

func (fq FilterQuery) OrLt(field string, value interface{}) FilterQuery

OrLt append lesser than expression using or.

func (FilterQuery) OrLte

func (fq FilterQuery) OrLte(field string, value interface{}) FilterQuery

OrLte append lesser than or equal expression using or.

func (FilterQuery) OrNe

func (fq FilterQuery) OrNe(field string, value interface{}) FilterQuery

OrNe append not equal expression using or.

func (FilterQuery) OrNil

func (fq FilterQuery) OrNil(field string) FilterQuery

OrNil append is nil expression using or.

func (FilterQuery) OrNin

func (fq FilterQuery) OrNin(field string, values ...interface{}) FilterQuery

OrNin append is not in expression using or.

func (FilterQuery) OrNotLike

func (fq FilterQuery) OrNotLike(field string, pattern string) FilterQuery

OrNotLike append not like expression using or.

func (FilterQuery) OrNotNil

func (fq FilterQuery) OrNotNil(field string) FilterQuery

OrNotNil append is not nil expression using or.

type ForeignKeyReference

type ForeignKeyReference struct {
	Table    string
	Columns  []string
	OnDelete string
	OnUpdate string
}

ForeignKeyReference definition.

type GroupQuery

type GroupQuery struct {
	Fields []string
	Filter FilterQuery
}

GroupQuery defines group clause of the query.

func NewGroup

func NewGroup(fields ...string) GroupQuery

NewGroup query.

func (GroupQuery) Build

func (gq GroupQuery) Build(query *Query)

Build query.

func (GroupQuery) Having

func (gq GroupQuery) Having(filters ...FilterQuery) GroupQuery

Having appends filter for group query with and operand.

func (GroupQuery) OrHaving

func (gq GroupQuery) OrHaving(filters ...FilterQuery) GroupQuery

OrHaving appends filter for group query with or operand.

func (GroupQuery) OrWhere

func (gq GroupQuery) OrWhere(filters ...FilterQuery) GroupQuery

OrWhere is alias for OrHaving.

func (GroupQuery) Where

func (gq GroupQuery) Where(filters ...FilterQuery) GroupQuery

Where is alias for having.

type Index

type Index struct {
	Op       SchemaOp
	Table    string
	Name     string
	Unique   bool
	Columns  []string
	Optional bool
	Options  string
}

Index definition.

type IndexOption

type IndexOption interface {
	// contains filtered or unexported methods
}

IndexOption interface. Available options are: Comment, Options.

type Instrumenter

type Instrumenter func(ctx context.Context, op string, message string) func(err error)

Instrumenter defines function type that can be used for instrumetation. This function should return a function with no argument as a callback for finished execution.

func (Instrumenter) Observe

func (i Instrumenter) Observe(ctx context.Context, op string, message string) func(err error)

Observe operation.

type Iterator

type Iterator interface {
	io.Closer
	Next(record interface{}) error
}

Iterator allows iterating through all record in database in batch.

type IteratorOption

type IteratorOption interface {
	// contains filtered or unexported methods
}

IteratorOption is used to configure iteration behaviour, such as batch size, start id and finish id.

func BatchSize

func BatchSize(size int) IteratorOption

BatchSize specifies the size of iterator batch. Defaults to 1000.

func Finish

func Finish(id ...interface{}) IteratorOption

Finish specifies the primary value to finish at (inclusive).

func Start

func Start(id ...interface{}) IteratorOption

Start specifies the primary value to start from (inclusive).

type JoinQuery

type JoinQuery struct {
	Mode      string
	Table     string
	From      string
	To        string
	Arguments []interface{}
}

JoinQuery defines join clause in query.

func NewFullJoin

func NewFullJoin(table string) JoinQuery

NewFullJoin with given table.

func NewFullJoinOn

func NewFullJoinOn(table string, from string, to string) JoinQuery

NewFullJoinOn table with given field.

func NewInnerJoin

func NewInnerJoin(table string) JoinQuery

NewInnerJoin with given table.

func NewInnerJoinOn

func NewInnerJoinOn(table string, from string, to string) JoinQuery

NewInnerJoinOn table with given field.

func NewJoin

func NewJoin(table string) JoinQuery

NewJoin with given table.

func NewJoinFragment

func NewJoinFragment(expr string, args ...interface{}) JoinQuery

NewJoinFragment defines a join clause using raw query.

func NewJoinOn

func NewJoinOn(table string, from string, to string) JoinQuery

NewJoinOn table with given field.

func NewJoinWith

func NewJoinWith(mode string, table string, from string, to string) JoinQuery

NewJoinWith query with custom join mode, table and field.

func NewLeftJoin

func NewLeftJoin(table string) JoinQuery

NewLeftJoin with given table.

func NewLeftJoinOn

func NewLeftJoinOn(table string, from string, to string) JoinQuery

NewLeftJoinOn table with given field.

func NewRightJoin

func NewRightJoin(table string) JoinQuery

NewRightJoin with given table.

func NewRightJoinOn

func NewRightJoinOn(table string, from string, to string) JoinQuery

NewRightJoinOn table with given field.

func (JoinQuery) Build

func (jq JoinQuery) Build(query *Query)

Build query.

type Key

type Key struct {
	Op        SchemaOp
	Name      string
	Type      KeyType
	Columns   []string
	Rename    string
	Reference ForeignKeyReference
	Options   string
}

Key definition.

type KeyOption

type KeyOption interface {
	// contains filtered or unexported methods
}

KeyOption interface. Available options are: Comment, Options.

type KeyType

type KeyType string

KeyType definition.

const (
	// PrimaryKey KeyType.
	PrimaryKey KeyType = "PRIMARY KEY"
	// ForeignKey KeyType.
	ForeignKey KeyType = "FOREIGN KEY"
	// UniqueKey KeyType.
	UniqueKey = "UNIQUE"
)

type Limit

type Limit int

Limit options. When passed as query, it limits returned result from database. When passed as column option, it sets the maximum size of the string/text/binary/integer columns.

func (Limit) Build

func (l Limit) Build(query *Query)

Build query.

type Lock

type Lock string

Lock query. This query will be ignored if used outside of transaction.

func ForUpdate

func ForUpdate() Lock

ForUpdate lock query.

func (Lock) Build

func (l Lock) Build(query *Query)

Build query.

type Map

type Map map[string]interface{}

Map can be used as mutation for repository insert or update operation. This allows inserting or updating only on specified field. Insert/Update of has one or belongs to can be done using other Map as a value. Insert/Update of has many can be done using slice of Map as a value. Map is intended to be used internally within application, and not to be exposed directly as an APIs.

func (Map) Apply

func (m Map) Apply(doc *Document, mutation *Mutation)

Apply mutation.

type Migration

type Migration interface {
	// contains filtered or unexported methods
}

Migration definition.

type Mutate

type Mutate struct {
	Type  ChangeOp
	Field string
	Value interface{}
}

Mutate stores mutation instruction.

func Dec

func Dec(field string) Mutate

Dec create a mutate using deccrement operation.

func DecBy

func DecBy(field string, n int) Mutate

DecBy create a mutate using decrement operation with custom decrement value.

func Inc

func Inc(field string) Mutate

Inc create a mutate using increment operation.

func IncBy

func IncBy(field string, n int) Mutate

IncBy create a mutate using increment operation with custom increment value.

func Set

func Set(field string, value interface{}) Mutate

Set create a mutate using set operation.

func SetFragment

func SetFragment(raw string, args ...interface{}) Mutate

SetFragment create a mutate operation using randoc fragment operation. Only available for Update.

func (Mutate) Apply

func (m Mutate) Apply(doc *Document, mutation *Mutation)

Apply mutation.

type Mutation

type Mutation struct {
	Mutates   map[string]Mutate
	Assoc     map[string]AssocMutation
	Unscoped  Unscoped
	Reload    Reload
	Cascade   Cascade
	ErrorFunc ErrorFunc
}

Mutation represents value to be inserted or updated to database. It's not safe to be used multiple time. some operation my alter mutation data.

func Apply

func Apply(doc *Document, mutators ...Mutator) Mutation

Apply using given mutators.

func (*Mutation) Add

func (m *Mutation) Add(mut Mutate)

Add a mutate.

func (*Mutation) IsAssocEmpty

func (m *Mutation) IsAssocEmpty() bool

IsAssocEmpty returns true if no assoc's mutation is defined.

func (*Mutation) IsEmpty

func (m *Mutation) IsEmpty() bool

IsEmpty returns true if no mutates operation and assoc's mutation is defined.

func (*Mutation) IsMutatesEmpty

func (m *Mutation) IsMutatesEmpty() bool

IsMutatesEmpty returns true if no mutates operation is defined.

func (*Mutation) SetAssoc

func (m *Mutation) SetAssoc(field string, muts ...Mutation)

SetAssoc mutation.

func (*Mutation) SetDeletedIDs

func (m *Mutation) SetDeletedIDs(field string, ids []interface{})

SetDeletedIDs mutation. nil slice will clear association.

type Mutator

type Mutator interface {
	Apply(doc *Document, mutation *Mutation)
}

Mutator is interface for a record mutator.

type Name

type Name string

Name option for defining custom index name.

type NotFoundError

type NotFoundError struct{}

NotFoundError returned whenever Find returns no result.

func (NotFoundError) Error

func (nfe NotFoundError) Error() string

Error message.

type Offset

type Offset int

Offset Query.

func (Offset) Build

func (o Offset) Build(query *Query)

Build query.

type OnDelete

type OnDelete string

OnDelete option for foreign key.

type OnUpdate

type OnUpdate string

OnUpdate option for foreign key.

type Optional

type Optional bool

Optional option. when used with create table, will create table only if it's not exists. when used with drop table, will drop table only if it's exists.

type Options

type Options string

Options options for table, column and index.

type Precision

type Precision int

Precision defines the precision for the decimal fields, representing the total number of digits in the number.

type Preload added in v0.10.0

type Preload string

Preload query.

func (Preload) Build added in v0.10.0

func (p Preload) Build(query *Query)

Build query.

type Querier

type Querier interface {
	Build(*Query)
}

Querier interface defines contract to be used for query builder.

type Query

type Query struct {
	Table         string
	SelectQuery   SelectQuery
	JoinQuery     []JoinQuery
	WhereQuery    FilterQuery
	GroupQuery    GroupQuery
	SortQuery     []SortQuery
	OffsetQuery   Offset
	LimitQuery    Limit
	LockQuery     Lock
	SQLQuery      SQLQuery
	UnscopedQuery Unscoped
	ReloadQuery   Reload
	CascadeQuery  Cascade
	PreloadQuery  []string
	// contains filtered or unexported fields
}

Query defines information about query generated by query builder.

func Build

func Build(table string, queriers ...Querier) Query

Build for given table using given queriers.

func From

func From(table string) Query

From create a query with chainable syntax, using from as the starting point.

func Join

func Join(table string) Query

Join create a query with chainable syntax, using join as the starting point.

func JoinOn

func JoinOn(table string, from string, to string) Query

JoinOn create a query with chainable syntax, using join as the starting point.

func JoinWith

func JoinWith(mode string, table string, from string, to string) Query

JoinWith create a query with chainable syntax, using join as the starting point.

func Joinf

func Joinf(expr string, args ...interface{}) Query

Joinf create a query with chainable syntax, using join as the starting point.

func Select

func Select(fields ...string) Query

Select query create a query with chainable syntax, using select as the starting point.

func Where

func Where(filters ...FilterQuery) Query

Where create a query with chainable syntax, using where as the starting point.

func (Query) Build

func (q Query) Build(query *Query)

Build query.

func (Query) Cascade added in v0.10.0

func (q Query) Cascade(c bool) Query

Cascade enable/disable autoload association on Find and FindAll query.

func (Query) Distinct

func (q Query) Distinct() Query

Distinct sets select query to be distinct.

func (Query) From

func (q Query) From(table string) Query

From set the table to be used for query.

func (Query) Group

func (q Query) Group(fields ...string) Query

Group query.

func (Query) Having

func (q Query) Having(filters ...FilterQuery) Query

Having query.

func (Query) Havingf

func (q Query) Havingf(expr string, args ...interface{}) Query

Havingf create having query using a raw query.

func (Query) Join

func (q Query) Join(table string) Query

Join current table with other table.

func (Query) JoinOn

func (q Query) JoinOn(table string, from string, to string) Query

JoinOn current table with other table.

func (Query) JoinWith

func (q Query) JoinWith(mode string, table string, from string, to string) Query

JoinWith current table with other table with custom join mode.

func (Query) Joinf

func (q Query) Joinf(expr string, args ...interface{}) Query

Joinf create join query using a raw query.

func (Query) Limit

func (q Query) Limit(limit int) Query

Limit result returned by database.

func (Query) Lock

func (q Query) Lock(lock string) Query

Lock query expression.

func (Query) Offset

func (q Query) Offset(offset int) Query

Offset the result returned by database.

func (Query) OrHaving

func (q Query) OrHaving(filters ...FilterQuery) Query

OrHaving query.

func (Query) OrHavingf

func (q Query) OrHavingf(expr string, args ...interface{}) Query

OrHavingf create having query using a raw query.

func (Query) OrWhere

func (q Query) OrWhere(filters ...FilterQuery) Query

OrWhere query.

func (Query) OrWheref

func (q Query) OrWheref(expr string, args ...interface{}) Query

OrWheref create where query using a raw query.

func (Query) Preload added in v0.10.0

func (q Query) Preload(field string) Query

Preload field association.

func (Query) Reload

func (q Query) Reload() Query

Reload force reloading association on preload.

func (Query) Select

func (q Query) Select(fields ...string) Query

Select filter fields to be selected from database.

func (Query) Sort

func (q Query) Sort(fields ...string) Query

Sort query.

func (Query) SortAsc

func (q Query) SortAsc(fields ...string) Query

SortAsc query.

func (Query) SortDesc

func (q Query) SortDesc(fields ...string) Query

SortDesc query.

func (Query) Unscoped

func (q Query) Unscoped() Query

Unscoped allows soft-delete to be ignored.

func (Query) Where

func (q Query) Where(filters ...FilterQuery) Query

Where query.

func (Query) Wheref

func (q Query) Wheref(expr string, args ...interface{}) Query

Wheref create where query using a raw query.

type Raw

type Raw string

Raw string

type Reload

type Reload bool

Reload force reload after insert/update. Default to false.

func (Reload) Apply

func (r Reload) Apply(doc *Document, mutation *Mutation)

Apply mutation.

func (Reload) Build

func (r Reload) Build(query *Query)

Build query.

type Repository

type Repository interface {
	// Adapter used in this repository.
	Adapter(ctx context.Context) Adapter

	// Instrumentation defines callback to be used as instrumenter.
	Instrumentation(instrumenter Instrumenter)

	// Ping database.
	Ping(ctx context.Context) error

	// Iterate through a collection of records from database in batches.
	// This function returns iterator that can be used to loop all records.
	// Limit, Offset and Sort query is automatically ignored.
	Iterate(ctx context.Context, query Query, option ...IteratorOption) Iterator

	// Aggregate over the given field.
	// Supported aggregate: count, sum, avg, max, min.
	// Any select, group, offset, limit and sort query will be ignored automatically.
	// If complex aggregation is needed, consider using All instead.
	Aggregate(ctx context.Context, query Query, aggregate string, field string) (int, error)

	// MustAggregate over the given field.
	// Supported aggregate: count, sum, avg, max, min.
	// Any select, group, offset, limit and sort query will be ignored automatically.
	// If complex aggregation is needed, consider using All instead.
	// It'll panic if any error eccured.
	MustAggregate(ctx context.Context, query Query, aggregate string, field string) int

	// Count records that match the query.
	Count(ctx context.Context, collection string, queriers ...Querier) (int, error)

	// MustCount records that match the query.
	// It'll panic if any error eccured.
	MustCount(ctx context.Context, collection string, queriers ...Querier) int

	// Find a record that match the query.
	// If no result found, it'll return not found error.
	Find(ctx context.Context, record interface{}, queriers ...Querier) error

	// MustFind a record that match the query.
	// If no result found, it'll panic.
	MustFind(ctx context.Context, record interface{}, queriers ...Querier)

	// FindAll records that match the query.
	FindAll(ctx context.Context, records interface{}, queriers ...Querier) error

	// MustFindAll records that match the query.
	// It'll panic if any error eccured.
	MustFindAll(ctx context.Context, records interface{}, queriers ...Querier)

	// FindAndCountAll records that match the query.
	// This is a convenient method that combines FindAll and Count. It's useful when dealing with queries related to pagination.
	// Limit and Offset property will be ignored when performing count query.
	FindAndCountAll(ctx context.Context, records interface{}, queriers ...Querier) (int, error)

	// MustFindAndCountAll records that match the query.
	// This is a convenient method that combines FindAll and Count. It's useful when dealing with queries related to pagination.
	// Limit and Offset property will be ignored when performing count query.
	// It'll panic if any error eccured.
	MustFindAndCountAll(ctx context.Context, records interface{}, queriers ...Querier) int

	// Insert a record to database.
	Insert(ctx context.Context, record interface{}, mutators ...Mutator) error

	// MustInsert an record to database.
	// It'll panic if any error occurred.
	MustInsert(ctx context.Context, record interface{}, mutators ...Mutator)

	// InsertAll records.
	InsertAll(ctx context.Context, records interface{}) error

	// MustInsertAll records.
	// It'll panic if any error occurred.
	MustInsertAll(ctx context.Context, records interface{})

	// Update a record in database.
	// It'll panic if any error occurred.
	Update(ctx context.Context, record interface{}, mutators ...Mutator) error

	// MustUpdate a record in database.
	// It'll panic if any error occurred.
	MustUpdate(ctx context.Context, record interface{}, mutators ...Mutator)

	// UpdateAll records tha match the query.
	UpdateAll(ctx context.Context, query Query, mutates ...Mutate) error

	// MustUpdateAll records that match the query.
	// It'll panic if any error occurred.
	MustUpdateAll(ctx context.Context, query Query, mutates ...Mutate)

	// Delete a record.
	Delete(ctx context.Context, record interface{}, options ...Cascade) error

	// MustDelete a record.
	// It'll panic if any error eccured.
	MustDelete(ctx context.Context, record interface{}, options ...Cascade)

	// DeleteAll records that match the query.
	DeleteAll(ctx context.Context, query Query) error

	// MustDeleteAll records that match the query.
	// It'll panic if any error eccured.
	MustDeleteAll(ctx context.Context, query Query)

	// Preload association with given query.
	// If association is already loaded, this will do nothing.
	// To force preloading even though association is already loaeded, add `Reload(true)` as query.
	Preload(ctx context.Context, records interface{}, field string, queriers ...Querier) error

	// MustPreload association with given query.
	// It'll panic if any error occurred.
	MustPreload(ctx context.Context, records interface{}, field string, queriers ...Querier)

	// Transaction performs transaction with given function argument.
	// Transaction scope/connection is automatically passed using context.
	Transaction(ctx context.Context, fn func(ctx context.Context) error) error
}

Repository for interacting with database.

func New

func New(adapter Adapter) Repository

New create new repo using adapter.

type Required

type Required bool

Required disallows nil values in the column.

type SQLQuery

type SQLQuery struct {
	Statement string
	Values    []interface{}
}

SQLQuery allows querying using native query supported by database.

func SQL

func SQL(statement string, values ...interface{}) SQLQuery

SQL Query.

func (SQLQuery) Build

func (sq SQLQuery) Build(query *Query)

Build Raw Query.

type Scale

type Scale int

Scale Defines the scale for the decimal fields, representing the number of digits after the decimal point.

type Schema

type Schema struct {
	Migrations []Migration
}

Schema builder.

func (*Schema) AddColumn

func (s *Schema) AddColumn(table string, name string, typ ColumnType, options ...ColumnOption)

AddColumn with name and type.

func (*Schema) AlterTable

func (s *Schema) AlterTable(name string, fn func(t *AlterTable), options ...TableOption)

AlterTable with name and its definition.

func (*Schema) CreateIndex

func (s *Schema) CreateIndex(table string, name string, column []string, options ...IndexOption)

CreateIndex for columns on a table.

func (*Schema) CreateTable

func (s *Schema) CreateTable(name string, fn func(t *Table), options ...TableOption)

CreateTable with name and its definition.

func (*Schema) CreateTableIfNotExists

func (s *Schema) CreateTableIfNotExists(name string, fn func(t *Table), options ...TableOption)

CreateTableIfNotExists with name and its definition.

func (*Schema) CreateUniqueIndex

func (s *Schema) CreateUniqueIndex(table string, name string, column []string, options ...IndexOption)

CreateUniqueIndex for columns on a table.

func (*Schema) Do

func (s *Schema) Do(fn Do)

Do migration using golang codes.

func (*Schema) DropColumn

func (s *Schema) DropColumn(table string, name string, options ...ColumnOption)

DropColumn by name.

func (*Schema) DropIndex

func (s *Schema) DropIndex(table string, name string, options ...IndexOption)

DropIndex by name.

func (*Schema) DropTable

func (s *Schema) DropTable(name string, options ...TableOption)

DropTable by name.

func (*Schema) DropTableIfExists

func (s *Schema) DropTableIfExists(name string, options ...TableOption)

DropTableIfExists by name.

func (*Schema) Exec

func (s *Schema) Exec(raw Raw)

Exec queries.

func (*Schema) RenameColumn

func (s *Schema) RenameColumn(table string, name string, newName string, options ...ColumnOption)

RenameColumn by name.

func (*Schema) RenameTable

func (s *Schema) RenameTable(name string, newName string, options ...TableOption)

RenameTable by name.

func (Schema) String

func (s Schema) String() string

String returns schema operation.

type SchemaOp

type SchemaOp uint8

SchemaOp type.

const (
	// SchemaCreate operation.
	SchemaCreate SchemaOp = iota
	// SchemaAlter operation.
	SchemaAlter
	// SchemaRename operation.
	SchemaRename
	// SchemaDrop operation.
	SchemaDrop
)

func (SchemaOp) String

func (s SchemaOp) String() string

type SelectQuery

type SelectQuery struct {
	OnlyDistinct bool
	Fields       []string
}

SelectQuery defines select clause of the query.

func NewSelect

func NewSelect(fields ...string) SelectQuery

NewSelect query.

func (SelectQuery) Distinct

func (sq SelectQuery) Distinct() SelectQuery

Distinct select query.

type SortQuery

type SortQuery struct {
	Field string
	Sort  int
}

SortQuery defines sort information of query.

func NewSortAsc

func NewSortAsc(field string) SortQuery

NewSortAsc sorts field with ascending sort.

func NewSortDesc

func NewSortDesc(field string) SortQuery

NewSortDesc sorts field with descending sort.

func (SortQuery) Asc

func (sq SortQuery) Asc() bool

Asc returns true if sort is ascending.

func (SortQuery) Build

func (sq SortQuery) Build(query *Query)

Build sort query.

func (SortQuery) Desc

func (sq SortQuery) Desc() bool

Desc returns true if s is descending.

type Structset

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

Structset can be used as mutation for repository insert or update operation. This will save every field in struct and it's association as long as it's loaded. This is the default mutator used by repository.

func NewStructset

func NewStructset(record interface{}, skipZero bool) Structset

NewStructset from a struct.

func (Structset) Apply

func (s Structset) Apply(doc *Document, mut *Mutation)

Apply mutation.

type Table

type Table struct {
	Op          SchemaOp
	Name        string
	Rename      string
	Definitions []TableDefinition
	Optional    bool
	Options     string
}

Table definition.

func (*Table) BigInt

func (t *Table) BigInt(name string, options ...ColumnOption)

BigInt defines a column with name and BigInt type.

func (*Table) Bool

func (t *Table) Bool(name string, options ...ColumnOption)

Bool defines a column with name and Bool type.

func (*Table) Column

func (t *Table) Column(name string, typ ColumnType, options ...ColumnOption)

Column defines a column with name and type.

func (*Table) Date

func (t *Table) Date(name string, options ...ColumnOption)

Date defines a column with name and Date type.

func (*Table) DateTime

func (t *Table) DateTime(name string, options ...ColumnOption)

DateTime defines a column with name and DateTime type.

func (*Table) Decimal

func (t *Table) Decimal(name string, options ...ColumnOption)

Decimal defines a column with name and Decimal type.

func (*Table) Float

func (t *Table) Float(name string, options ...ColumnOption)

Float defines a column with name and Float type.

func (*Table) ForeignKey

func (t *Table) ForeignKey(column string, refTable string, refColumn string, options ...KeyOption)

ForeignKey defines foreign key index.

func (*Table) Fragment

func (t *Table) Fragment(fragment string)

Fragment defines anything using sql fragment.

func (*Table) ID

func (t *Table) ID(name string, options ...ColumnOption)

ID defines a column with name and ID type. the resulting database type will depends on database.

func (*Table) Int

func (t *Table) Int(name string, options ...ColumnOption)

Int defines a column with name and Int type.

func (*Table) PrimaryKey

func (t *Table) PrimaryKey(column string, options ...KeyOption)

PrimaryKey defines a primary key for table.

func (*Table) PrimaryKeys

func (t *Table) PrimaryKeys(columns []string, options ...KeyOption)

PrimaryKeys defines composite primary keys for table.

func (*Table) SmallInt added in v0.10.0

func (t *Table) SmallInt(name string, options ...ColumnOption)

SmallInt defines a column with name and Small type.

func (*Table) String

func (t *Table) String(name string, options ...ColumnOption)

String defines a column with name and String type.

func (*Table) Text

func (t *Table) Text(name string, options ...ColumnOption)

Text defines a column with name and Text type.

func (*Table) Time

func (t *Table) Time(name string, options ...ColumnOption)

Time defines a column with name and Time type.

func (*Table) Timestamp

func (t *Table) Timestamp(name string, options ...ColumnOption)

Timestamp defines a column with name and Timestamp type.

func (*Table) Unique

func (t *Table) Unique(columns []string, options ...KeyOption)

Unique defines an unique key for columns.

type TableDefinition

type TableDefinition interface {
	// contains filtered or unexported methods
}

TableDefinition interface.

type TableOption

type TableOption interface {
	// contains filtered or unexported methods
}

TableOption interface. Available options are: Comment, Options.

type Unique

type Unique bool

Unique set column as unique.

type Unscoped

type Unscoped bool

Unscoped query.

func (Unscoped) Apply

func (u Unscoped) Apply(doc *Document, mutation *Mutation)

Apply mutation.

func (Unscoped) Build

func (u Unscoped) Build(query *Query)

Build query.

type Unsigned

type Unsigned bool

Unsigned sets integer column to be unsigned.

Directories

Path Synopsis
adapter
mysql
Package mysql wraps mysql driver as an adapter for REL.
Package mysql wraps mysql driver as an adapter for REL.
postgres
Package postgres wraps postgres (pq) driver as an adapter for REL.
Package postgres wraps postgres (pq) driver as an adapter for REL.
specs
Package specs defines test specifications for rel's adapter.
Package specs defines test specifications for rel's adapter.
sql
Package sql is general sql adapter that wraps database/sql.
Package sql is general sql adapter that wraps database/sql.
sqlite3
Package sqlite3 wraps go-sqlite3 driver as an adapter for rel.
Package sqlite3 wraps go-sqlite3 driver as an adapter for rel.
cmd
rel
Package group is syntatic sugar for building group query.
Package group is syntatic sugar for building group query.
Package join is syntatic sugar for building join query.
Package join is syntatic sugar for building join query.
Package sort is syntatic sugar for building sort query.
Package sort is syntatic sugar for building sort query.
Package where is syntatic sugar for building where query.
Package where is syntatic sugar for building where query.

Jump to

Keyboard shortcuts

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