rel

package module
v0.0.0-...-c1bc1c9 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2014 License: MIT Imports: 8 Imported by: 1

README

rel

Build Status

Relational Algebra in Go. Go's interfaces & duck typing are used to provide an extensible ORM that is capable of query rewrite, and can perform relational operations both in native go and on source dbms's. Go's concurrency mechanisms (will) allow for fine control of the inherent parallelism in relational operations. It is my hope that this package will produce some interesting approaches to implement relational expressions. This package is currently experimental and its interfaces may change.

This implements most of the traditional elements of relational algebra, including project, restrict, join, set difference, and union. It also implements some of the common non-relational operations, including groupby, and map. To learn more about relational algebra, C. J. Date's Database in Depth is a great place to start, and it is used as the source of terminology in the rel package.

Please note that relational algebra is not SQL. In particular, NULL is not a part of relational algebra, and all relations are distinct.

The semantics of this package are very similar to Microsoft's LINQ, although the syntax is somewhat different. rel provides a uniform interface to many different types of data sources. This isn't LINQ though - it is a library, and it is not integrated with the language, which means rel has a significant performance cost relative to normal go code that doesn't use reflection. It also reduces the type safety. At some point in the future, it might include code generation along the same lines as the gen package (http://clipperhouse.github.io/gen/) and the megajson package (https://github.com/benbjohnson/megajson).

Installation

This package can be installed with the go get command:

go get github.com/jonlawlor/rel

API Documentation

http://godoc.org/github.com/jonlawlor/rel

Project Priorities

  • Faithful representation of relational algebra
  • Extensibility
  • Developer friendliness
  • Performance

Thanks

  • Andrew Janke
  • Ben Johnson
  • Egon Elbre

TODOs

  • Reach 100% test coverage (currently 85%)
  • Implement benchmarks in both "normal" rel reflection and native equivalents to determine reflection overhead
  • Implement sub packages for other data sources, such as json or gob. A distributed relational algebra?
  • Implement non relational operations like order?
  • Hook up chan_mem to some kind of copying mechanism
  • Should attributes have an associated type, or just a name like it is now?
  • Rewrite Predicate and Attribute interface (http://www.reddit.com/r/golang/comments/29ng75/tired_of_lightweight_simple_orms_youre_in_luck/cimwcqn)
  • Refactor reflection on tuples to a new type, instead of having reflect.ValueOf and reflect.TypeOf calls everywhere.

Errors & Cancellation

There are two ways (other than program termination) that relational queries can be terminated early: errors, which propogates "downstream" from the tuple sender, and cancellation, which propagates "upstream" from the tuple receiver.

There are 2 types of errors that can be handled: errors in relational construction, like projecting a relation to a set of tuples that are not a subset of the original relation, and errors during computation, like when a data source unexpectedly disconnects. There are two types of error handling available to us: either panic (and maybe recover) which is expensive, or having an Err() method of relations, which returns an error. If no error has been encountered, then Err should return nil, otherwise an error. The rel package tries to find errors early, and avoid panic as much as possible. Having 2-arg outputs is not conducive to the method chaining currently used. The Err() method way of handling errors is also used in the sql package's Scanner, so there is some precedent.

Therefore, you should check for errors in the following places:

  1. during derived relational construction, if one of the source relations is an error, then that relation will be returned instead of the compound relation. In the case that two relations are provided and both are errors, then the first will be returned.
  2. after the chan used in TupleChan, if the source(s) of tuples are closed, then you should check for an error.
  3. if you implement your own relation, you should check for an Err() after a source closes, or set it if you have encountered one (and close the results channel).

Cancellation is handled in the TupleChan method. If a caller no longer wants any results, they should close the cancel channel, which will then stop tuples from being sent by the TupleChan method, which will also relay the cancellation up to any sources of tuples that it is consuming. It will not close the results channel.

Documentation

Overview

Package rel implements relational algebra, a set of operations on sets of tuples which result in relations, as defined by E. F. Codd.

Basics

What folows is a brief introduction to relational algebra. For a more complete introduction, please read C. J. Date's book "Database in Depth". This package uses the same terminology.

Relations are sets of named tuples with identical attributes. The primative operations which define the relational algebra are:

Union, which adds two sets together.

Diff, which removes all elements from one set which exist in another.

Restrict, which removes values from a relation that do not satisfy a predicate.

Project, which removes zero or more attributes from the tuples the relation is defined on.

Rename, which changes the names of the attributes in a relation.

Join, which can multiply two relations together (which may have different types of tuples) by returning all combinations of tuples in the two relations where all attributes in one relation are equal to the attributes in the other where the names are the same. This is sometimes called a natural join.

This package represents tuples as structs with no unexported or anonymous fields. The fields of the struct are the attributes of the tuple it represents.

Attributes are strings with some additional methods that are useful for constructing predicates and candidate keys. They have to be valid field names in go.

Predicates are functions which take a tuple and return a boolean, and are used as an input for Restrict expressions.

Candidate keys are the sets of attributes which define unique tuples in a relation. Every relation has at least one candidate key, because every relation only contains unique tuples. Some relations may contains several candidate keys.

Relations in this package can be either literal, such as a relation from a map of tuples, or an expression of other relations, such as a join between two source relations.

Literal Relations can be defined using the rel.New function. Given a slice, map, or channel of tuples, the New function constructs a new "essential" relation, with those values as tuples. Other packages can create essential relations from other sources of data, such as the github.com/jonlawlor/relcsv package, or the github.com/jonlawlor/relsql package.

Relational Expressions are generated when one of the methods Project, Restrict, Union, Diff, Join, Rename, Map, or GroupBy. During their construction, the rel package checks to see if they can be distributed over the source relations that they are being called on, and if so, it attempts to push the expressions down the tree of relations as far as they can go, with the end goal of getting pushed all the way to the "essential" source relations. In this way, relational expressions can (hopefully) reduce the amount of computation done in total and / or done in the go runtime.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AttributeMap

func AttributeMap(fn1, fn2 []Attribute) map[Attribute]FieldIndex

AttributeMap creates a map from positions of one set of attributes to another. The returned map's values have two fields i,j , which indicate the location of the field name in the input types if the field is absent from either of the inputs, it is not returned.

func Card

func Card(r Relation) (i int)

Card returns the cardinality of the relation note: this consumes the values of the relation's tuples and can be an expensive operation.

func CombineTuples

func CombineTuples(ltup, rtup reflect.Value, ltyp reflect.Type, fMap map[Attribute]FieldIndex) reflect.Value

CombineTuples takes the values in rtup and ltup and creates a new tuple that takes fields from the right tuple if possible, otherwise takes fields from the left tuple.

func CombineTuples2

func CombineTuples2(to *reflect.Value, from reflect.Value, fMap map[Attribute]FieldIndex)

CombineTuples2 takes the values in from and assigns them to the fields in to with the same names. TODO(jonlawlor): figure out how to combine with CombineTuples, or rename this func. Very ugly.

func Deg

func Deg(r Relation) int

Deg returns the degree of the relation

func EnsureChan

func EnsureChan(ch reflect.Type, zero interface{}) error

EnsureChan returns an error if the the input is not a channel with elements of the specified type.

func EnsureGroupFunc

func EnsureGroupFunc(gfcn reflect.Type, inSuper, outSuper interface{}) (inTup, outTup reflect.Type, err error)

EnsureGroupFunc returns an error if the input is not a function with only one input and one output, where the input and output are subdomains of given tuples.

func EnsureMap

func EnsureMap(m reflect.Type, zero interface{}) error

EnsureMap returns an error if the the input is not a map with key elements of the specified type, and value elements of type struct{}

func EnsureMapFunc

func EnsureMapFunc(mfcn reflect.Type, inSuper interface{}) (inTup, outTup reflect.Type, err error)

EnsureMapFunc returns an error if the input is not a function with only one input and one output, where the input is a subdomain of given tuple.

func EnsureSameDomain

func EnsureSameDomain(sub, dom []Attribute) (err error)

EnsureSameDomain returns an error if the inputs do not have the same domain.

func EnsureSlice

func EnsureSlice(sl reflect.Type, zero interface{}) error

EnsureSlice returns an error if the the input is not a slice with elements of the specified type.

func EnsureSubDomain

func EnsureSubDomain(sub, dom []Attribute) (err error)

EnsureSubDomain returns an error if the input sub is not a subdomain of input dom.

func FieldMap

func FieldMap(e1, e2 reflect.Type) map[Attribute]FieldIndex

FieldMap creates a map from fields of one struct type to the fields of another the returned map's values have two fields i,j , which indicate the location of the field name in the input types if the field is absent from either of the inputs, it is not returned.

func FieldTypes

func FieldTypes(e reflect.Type) []reflect.Type

FieldTypes takes a reflect.Type of a struct and returns field types in order

func GoString

func GoString(r Relation) string

GoString returns a string representation of the relation that should evaluate to a relation with identical tuples as the source.

func HeadingString

func HeadingString(r Relation) string

HeadingString is a string representation of the attributes of a relation formatted like "{foo, bar}"

func IsSubDomain

func IsSubDomain(sub, dom []Attribute) bool

IsSubDomain returns true if the attributes in sub are all members of dom, otherwise false this would be faster if []Attributes were always ordered TODO(jonlawlor): make this a method of []Attribute?

func OrderCandidateKeys

func OrderCandidateKeys(ckeys CandKeys)

OrderCandidateKeys sorts candidate keys by number of attributes and then alphabetically.

func PartialEquals

func PartialEquals(tup1 reflect.Value, tup2 reflect.Value, fmap map[Attribute]FieldIndex) bool

PartialEquals returns true when two tuples have equal values in the attributes with the same names.

func PartialProject

func PartialProject(tup reflect.Value, ltyp, rtyp reflect.Type, lFieldMap, rFieldMap map[Attribute]FieldIndex) (reflect.Value, reflect.Value)

PartialProject takes the attributes of the input tup, and then for the attributes that are in ltyp but not in rtyp, put those values into ltup, and put zero values into ltup for the values that are in rtyp. For the rtup, put only values which are in rtyp. The reason we have to put zero values is that we can't make derived types. returns the results as an interface instead of as reflect.Value's

func PrettyPrint

func PrettyPrint(r Relation) string

PrettyPrint returns a human readable table of the tuples in the relation.

func SubsetCandidateKeys

func SubsetCandidateKeys(cKeys1 [][]Attribute, names1 []Attribute, fMap map[Attribute]FieldIndex) [][]Attribute

SubsetCandidateKeys subsets candidate keys so they only include given fields

Types

type AdHoc

type AdHoc struct {
	// f is the function which takes a tuple and returns a boolean indicating
	// that the tuple passes the predicate
	F interface{}
}

AdHoc is a Predicate that can implement any function on a tuple. The rewrite engine will be able to infer which attributes it requires to be evaluated, but nothing beyond that, which will prevent it from being moved into source queries in e.g. sql. For those kind of predicates, non AdHoc predicates will be required. I expect that this will typically be constructed with anonymous functions.

func (AdHoc) And

func (p1 AdHoc) And(p2 Predicate) AndPred

And predicate

func (AdHoc) Domain

func (p1 AdHoc) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (AdHoc) EvalFunc

func (p1 AdHoc) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (AdHoc) Or

func (p1 AdHoc) Or(p2 Predicate) OrPred

Or predicate

func (AdHoc) String

func (p1 AdHoc) String() string

String representation of AdHoc

func (AdHoc) Xor

func (p1 AdHoc) Xor(p2 Predicate) XorPred

Xor predicate

type AndPred

type AndPred struct {
	P1 Predicate
	P2 Predicate
}

AndPred represents a logical and predicate

func (AndPred) And

func (p1 AndPred) And(p2 Predicate) AndPred

And predicate

func (AndPred) Domain

func (p1 AndPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (AndPred) EvalFunc

func (p1 AndPred) EvalFunc(e reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (AndPred) Or

func (p1 AndPred) Or(p2 Predicate) OrPred

Or predicate

func (AndPred) String

func (p1 AndPred) String() string

String representation of And

func (AndPred) Xor

func (p1 AndPred) Xor(p2 Predicate) XorPred

Xor predicate

type Attribute

type Attribute string

Attribute represents a particular attribute's name in a relation

func FieldNames

func FieldNames(e reflect.Type) []Attribute

FieldNames takes a reflect.Type of a struct and returns field names in order

func Heading

func Heading(r Relation) []Attribute

Heading is a slice containing the attributes of the input Relation.

func (Attribute) EQ

func (att1 Attribute) EQ(v interface{}) EQPred

EQ Equal to (==)

func (Attribute) GE

func (att1 Attribute) GE(v interface{}) GEPred

GE Greater than or equal to (>=)

func (Attribute) GT

func (att1 Attribute) GT(v interface{}) GTPred

GT Greater than (>)

func (Attribute) LE

func (att1 Attribute) LE(v interface{}) LEPred

LE Less than or equal to (<=)

func (Attribute) LT

func (att1 Attribute) LT(v interface{}) LTPred

LT Less than (<)

func (Attribute) NE

func (att1 Attribute) NE(v interface{}) NEPred

NE Not equal to (!=)

type AttributeSubsetError

type AttributeSubsetError domainError

AttributeSubsetError represents an error that occurs when a method on a relation is called with a set of tuples that are not a subset of an expected type.

func (*AttributeSubsetError) Error

func (e *AttributeSubsetError) Error() string

type CandKeys

type CandKeys [][]Attribute

CandKeys is a set of candidate keys they should be unique and sorted

func DefaultKeys

func DefaultKeys(z interface{}) CandKeys

DefaultKeys provides the default candidate key for a relation This is used when no candidate keys are provided. note that this will not be sorted correctly

func String2CandKeys

func String2CandKeys(ckeystrs [][]string) CandKeys

String2CandKeys converts a slice of string slices into a set of candidate keys. The result is not sorted, so OrderCandidateKeys should be called afterwards if the input is not already sorted in the same way.

func (CandKeys) Len

func (cks CandKeys) Len() int

definitions for the candidate key sorting

func (CandKeys) Less

func (cks CandKeys) Less(i, j int) bool

func (CandKeys) Swap

func (cks CandKeys) Swap(i, j int)

type ContainerError

type ContainerError struct {
	Expected reflect.Kind
	Found    reflect.Kind
}

ContainerError represents an error that occurs when the wrong kind of container is given to a TupleChan, TupleSlice, or TupleMap method, as indicated by the name of the method.

func (*ContainerError) Error

func (e *ContainerError) Error() string

type DegreeError

type DegreeError struct {
	Expected int
	Found    int
}

DegreeError represents an error that occurs when the input tuples to a relational operation do not have the same degree as expected. This only occurs in rename operations.

func (*DegreeError) Error

func (e *DegreeError) Error() string

type DomainMismatchError

type DomainMismatchError domainError

DomainMismatchError represents an error that occurs when two tuples have a different set of attributes.

func (*DomainMismatchError) Error

func (e *DomainMismatchError) Error() string

type EQPred

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

EQPred is a representation of equal to (==)

func (EQPred) And

func (p1 EQPred) And(p2 Predicate) AndPred

And predicate

func (EQPred) Domain

func (p1 EQPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (EQPred) EvalFunc

func (p1 EQPred) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (EQPred) Or

func (p1 EQPred) Or(p2 Predicate) OrPred

Or predicate

func (EQPred) String

func (p1 EQPred) String() string

String representation of EQ

func (EQPred) Xor

func (p1 EQPred) Xor(p2 Predicate) XorPred

Xor predicate

type ElemError

type ElemError struct {
	Expected reflect.Type
	Found    reflect.Type
}

ElemError represents an error that occurs when the wrong kind of element is provided to a container given to TupleChan, TupleSlice, or TupleMap methods of relations.

func (*ElemError) Error

func (e *ElemError) Error() string

type FieldIndex

type FieldIndex struct {
	I int
	J int
}

FieldIndex is used to map between attributes in different relations that have the same name

type GEPred

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

GEPred is a representation of greater than or equal to (>=)

func (GEPred) And

func (p1 GEPred) And(p2 Predicate) AndPred

And predicate

func (GEPred) Domain

func (p1 GEPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (GEPred) EvalFunc

func (p1 GEPred) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (GEPred) Or

func (p1 GEPred) Or(p2 Predicate) OrPred

Or predicate

func (GEPred) String

func (p1 GEPred) String() string

String representation of GE (>=)

func (GEPred) Xor

func (p1 GEPred) Xor(p2 Predicate) XorPred

Xor predicate

type GTPred

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

GTPred is a representation of greater than (>)

func (GTPred) And

func (p1 GTPred) And(p2 Predicate) AndPred

And predicate

func (GTPred) Domain

func (p1 GTPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (GTPred) EvalFunc

func (p1 GTPred) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (GTPred) Or

func (p1 GTPred) Or(p2 Predicate) OrPred

Or predicate

func (GTPred) String

func (p1 GTPred) String() string

String representation of GT

func (GTPred) Xor

func (p1 GTPred) Xor(p2 Predicate) XorPred

Xor predicate

type InDomainError

type InDomainError domainError

InDomainError represents an error that occurs when the input tuples of a function are not subdomains of the expected domain

func (*InDomainError) Error

func (e *InDomainError) Error() string

type LEPred

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

LEPred is a representation of less than or equal to (<=)

func (LEPred) And

func (p1 LEPred) And(p2 Predicate) AndPred

And predicate

func (LEPred) Domain

func (p1 LEPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (LEPred) EvalFunc

func (p1 LEPred) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (LEPred) Or

func (p1 LEPred) Or(p2 Predicate) OrPred

Or predicate

func (LEPred) String

func (p1 LEPred) String() string

String representation of LE

func (LEPred) Xor

func (p1 LEPred) Xor(p2 Predicate) XorPred

Xor predicate

type LTPred

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

LTPred is a representation of less than (<)

func (LTPred) And

func (p1 LTPred) And(p2 Predicate) AndPred

And predicate

func (LTPred) Domain

func (p1 LTPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (LTPred) EvalFunc

func (p1 LTPred) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (LTPred) Or

func (p1 LTPred) Or(p2 Predicate) OrPred

Or predicate

func (LTPred) String

func (p1 LTPred) String() string

String representation of LT

func (LTPred) Xor

func (p1 LTPred) Xor(p2 Predicate) XorPred

Xor predicate

type NEPred

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

NEPred represents a not equal to (!=) operation

func (NEPred) And

func (p1 NEPred) And(p2 Predicate) AndPred

And predicate

func (NEPred) Domain

func (p1 NEPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (NEPred) EvalFunc

func (p1 NEPred) EvalFunc(e1 reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (NEPred) Or

func (p1 NEPred) Or(p2 Predicate) OrPred

Or predicate

func (NEPred) String

func (p1 NEPred) String() string

String representation of NEPred (!-)

func (NEPred) Xor

func (p1 NEPred) Xor(p2 Predicate) XorPred

Xor predicate

type NotPred

type NotPred struct {
	P Predicate
}

NotPred represents a logical not of a predicate

func Not

func Not(p Predicate) NotPred

Not predicate

func (NotPred) And

func (p1 NotPred) And(p2 Predicate) AndPred

And predicate

func (NotPred) Domain

func (p1 NotPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (NotPred) EvalFunc

func (p1 NotPred) EvalFunc(e reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (NotPred) Or

func (p1 NotPred) Or(p2 Predicate) OrPred

Or predicate

func (NotPred) String

func (p1 NotPred) String() string

String representation of Not

func (NotPred) Xor

func (p1 NotPred) Xor(p2 Predicate) XorPred

Xor predicate

type NumInError

type NumInError funcArityError

NumInError represents an error that occurs when the wrong number of inputs to a function are provided to groupby or map

func (*NumInError) Error

func (e *NumInError) Error() string

type NumOutError

type NumOutError funcArityError

NumOutError represents an error that occurs when the wrong number of outputs to a function are provided to groupby or map

func (*NumOutError) Error

func (e *NumOutError) Error() string

type OrPred

type OrPred struct {
	P1 Predicate
	P2 Predicate
}

OrPred represents a logical or predicate

func (OrPred) And

func (p1 OrPred) And(p2 Predicate) AndPred

And predicate

func (OrPred) Domain

func (p1 OrPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (OrPred) EvalFunc

func (p1 OrPred) EvalFunc(e reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (OrPred) Or

func (p1 OrPred) Or(p2 Predicate) OrPred

Or predicate

func (OrPred) String

func (p1 OrPred) String() string

String representation of Or

func (OrPred) Xor

func (p1 OrPred) Xor(p2 Predicate) XorPred

Xor predicate

type OutDomainError

type OutDomainError domainError

OutDomainError represents an error that occurs when the output tuples of a function are not subdomains of the expected domain

func (*OutDomainError) Error

func (e *OutDomainError) Error() string

type Predicate

type Predicate interface {
	// EvalFunc returns a function which can evalutes a predicate on an input
	// tuple
	EvalFunc(e reflect.Type) func(t interface{}) bool

	// Domain is the type of input that is required to evalute the predicate
	// this might have to be a recursive type instead of reflect.Type?
	Domain() []Attribute

	// And two predicates
	And(p2 Predicate) AndPred

	// Or two predicates
	Or(p2 Predicate) OrPred

	// Xor two predicates
	Xor(p2 Predicate) XorPred

	String() string
}

Predicate is the type of func that takes a tuple and returns bool and is used for restrict. It should always be a func with input of a subdomain of the relation, with one bool output.

type Relation

type Relation interface {
	// Zero is the zero value for the tuple.  It provides a blank tuple,
	// similar to how a zero value is defined in the reflect package.  It will
	// have default values for each of its fields.
	Zero() interface{}

	// CKeys is the set of candidate keys for the Relation.  They will be
	// sorted from smallest key size to largest, and each key will be
	// sorted alphabetically.
	CKeys() CandKeys

	// TupleChan takes a channel with the same element type as Zero, and
	// concurrently sends the results of the relational operation over it.  You
	// should check the Err function before invoking this function to determine
	// if you can expect results, because if the relational operation is
	// malformed, then the TupleChan function will do nothing.  It returns a
	// cancel chan<- struct{} which can be used to terminate the calculations
	// before they have completed, by closing the cancel channel.  See
	// http://blog.golang.org/pipelines - specifically the section on explicit
	// cancellation - for a more in depth examination of how to use it.
	//
	// If you provide a non channel input or a channel with an element type
	// that does not match the Zero type, this method will result in a non nil
	// Err() return.
	TupleChan(interface{}) (cancel chan<- struct{})

	// Project reduces the set of attributes used in the tuples of the
	// relation to a new given type, z2.
	//
	// If the input attributes z2 are not a subset of the attributes of the
	// source relation, then the resulting Relation will have non nill Err().
	Project(z2 interface{}) Relation

	// Restrict reduces the set of tuples in the relation to only those where
	// the predicate evaluates to true.
	//
	// If the input Predicate depends on attributes that do not exist in the
	// source relation, then the Relation result will have non nill Err().
	Restrict(p Predicate) Relation

	// Rename changes the names of attributes in a relation.  The new names
	// should be provided in the same order as the corresponding old names.
	//
	// If the input is not a struct, or if it has a different size than the
	// source's Zero, then the Relation result of this method will have non
	// nil Err().
	Rename(z2 interface{}) Relation

	// Map applies an input map function to each of the tuples in the source
	// relation.  Because map can transform any of the attributes in the
	// tuples, you have to provide a new set of candidate keys to this
	// operation.
	//
	// If the input candidate keys contain attributes that do not exist in
	// the resulting relation, or if the input mfcn is not a function, or if
	// it does not take tuples that are a subdomain of the source relation's,
	// or if it does not result in tuples, then the resulting Relation will
	// have non-nil Err().
	Map(mfcn interface{}, ckeystr [][]string) Relation

	// Union combines two relations into one relation, using a set union
	// operation.  If the two relations do not have the same Zero type, then
	// the resulting Relation will have a non nil Err().
	Union(r2 Relation) Relation

	// Diff removes values from one relation that match values in another.  If
	// the two relations do not have the same Zero type, then the resulting
	// Relation will have a non nil Err().
	Diff(r2 Relation) Relation

	// Join combines two relations by combining tuples between the two if the
	// tuples have identical values in the attributes that share the same
	// names.  This is also called an "equi-join" or natural join.  It is a
	// generalization of set intersection.  The second input, z3, should be a
	// blank structure with the attributes that the join will return.
	//
	// If z3 is not a struct, or if it contains attributes that do not exist
	// in the source relations, then the Err() field will be set.
	Join(r2 Relation, z3 interface{}) Relation

	// GroupBy provides arbitary aggregation of the tuples in the source
	// relation.  The
	// t2 is the resulting tuple type, gfcn is a function which takes as input
	// a channel of a subdomain of the tuples in the source relation, and then
	// produces result tuples that are a subdomain of the t2 tuple.  The
	// attributes that are in t2 that are not a part of the result tuples must
	// also exist in the source relation's tuples, and they are used to
	// determine unique groups.
	//
	// If t2 is not a blank example tuple struct, or if gfcn is not a function
	// which takes as input a channel with element type a subdomain of the
	// source relation, or if the result of the function is not a tuple
	// subdomain of t2, then the Err() result will be set.
	GroupBy(t2, gfcn interface{}) Relation

	// String provides a short relational algebra representation of the
	// relation.  It is particularly useful to determine which rewrite
	// rules have been applied.
	String() string

	// GoString provides a string which represents the tuples and the way
	// they are being transformed in go.  The result should be a string of
	// valid go code that will replicate the results of the input Relation.
	GoString() string

	// Err returns the first error that was encountered while either creating
	// the Relation, during a parent's evaluation, or during its own
	// evaluation.  There are two times when the Err() result may be non-nil:
	// either immediately after construction, or during the evaluation of the
	// TupleChan method.  If the Err() method does not return nil, then the
	// TupleChan method will never return any tuples, and further relational
	// operations will not be evaluated.
	Err() error
}

Relation is a set of tuples with named attributes. See "A Relational Model of Data for Large Shared Data Banks" by Codd and "An introduction to Database Systems" by Date for the background for relational algebra.

Example (Diff)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
)

type supplierTup struct {
	SNO    int
	SName  string
	Status int
	City   string
}

func main() {
	// the type of the tuples in the relation
	// defined elsewhere
	// type supplierTup struct {
	//	SNO    int
	//	SName  string
	//	Status int
	//	City   string
	// }
	r1 := rel.New([]supplierTup{
		{1, "Smith", 20, "London"},
		{2, "Jones", 10, "Paris"},
		{3, "Blake", 30, "Paris"},
		{4, "Clark", 20, "London"},
		{5, "Adams", 30, "Athens"},
		{6, "Coppola Ristorante", 1, "New Providence"},
	}, [][]string{
		[]string{"SNO"}, // the candidat key
	})
	r2 := rel.New([]supplierTup{
		{1, "Smith", 20, "London"},
		{2, "Jones", 10, "Paris"},
		{3, "Blake", 30, "Paris"},
		{4, "Clark", 20, "London"},
		{5, "Adams", 30, "Athens"},
	}, [][]string{
		[]string{"SNO"}, // the candidat key
	})

	r3 := r1.Diff(r2)
	fmt.Println(r3)
	fmt.Println(rel.PrettyPrint(r3))
	// in this case there is a single tuple so no ordering is needed
	
Output:

Example (GroupBy)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
	"sort"
)

type PNO struct {
	PNO int
	Qty int
}

type PNOs []PNO

func (tups PNOs) Len() int           { return len(tups) }
func (tups PNOs) Swap(i, j int)      { tups[i], tups[j] = tups[j], tups[i] }
func (tups PNOs) Less(i, j int) bool { return tups[i].PNO < tups[j].PNO }

func main() {

	// the type of the tuples in the input relation
	type orderTup struct {
		PNO int
		SNO int
		Qty int
	}

	r1 := rel.New([]orderTup{
		{1, 1, 300},
		{1, 2, 200},
		{1, 3, 400},
		{1, 4, 200},
		{1, 5, 100},
		{1, 6, 100},
		{2, 1, 300},
		{2, 2, 400},
		{3, 2, 200},
		{4, 2, 200},
		{4, 4, 300},
		{4, 5, 400},
	}, [][]string{
		[]string{"PNO", "SNO"},
	})

	// this is the type of the resulting tuples.  Because PNO is not a part of
	// the RETURN of the groupFcn, it is used to determine the unique groups of
	// the resulting relation.
	// defined elsewhere...
	// type PNO struct {
	// 	PNO int
	// 	Qty int
	// }

	// this is (in this case) both the type of the tuples that get accumulated,
	// and also the resulting type of the accumulation.
	type valTup struct {
		Qty int
	}

	// a function which sums the quantities of orders
	groupFcn := func(val <-chan valTup) valTup {
		res := valTup{}
		for vi := range val {
			res.Qty += vi.Qty
		}
		return res
	}

	r2 := r1.GroupBy(PNO{}, groupFcn)
	// order the output and stick it back into a slice
	// this is really just to get the output into a consistent order.  If you
	// don't care about the order, you don't have to do this.  Currently
	// ordering is not part of the rel package (it isn't a part of relational
	// algebra!)
	res := PNOs{}
	t := make(chan PNO)
	r2.TupleChan(t)
	for v := range t {
		res = append(res, v)
	}
	sort.Sort(res)

	r3 := rel.New(res, [][]string{
		[]string{"PNO"}, // the candidate key
	})

	fmt.Printf("%v\n", r2)
	fmt.Println(rel.PrettyPrint(r3))

	// defined elsewhere...
	// type PNOs []PNO
	//
	// func (tups PNOs) Len() int      { return len(tups) }
	// func (tups PNOs) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
	// func (tups PNOs) Less(i, j int) bool { return tups[i].PNO < tups[j].PNO }

}
Output:

Relation(PNO, SNO, Qty).GroupBy({PNO, Qty}->{Qty})
 +------+-------+
 |  PNO |   Qty |
 +------+-------+
 |    1 |  1300 |
 |    2 |   700 |
 |    3 |   200 |
 |    4 |   900 |
 +------+-------+
Example (Join)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
	"sort"
)

type supplierTup struct {
	SNO    int
	SName  string
	Status int
	City   string
}

type joinTup struct {
	PNO    int
	SNO    int
	Qty    int
	SName  string
	Status int
	City   string
}

type joinTups []joinTup

func (tups joinTups) Len() int      { return len(tups) }
func (tups joinTups) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
func (tups joinTups) Less(i, j int) bool {
	return tups[i].PNO < tups[j].PNO || (tups[i].PNO == tups[j].PNO && tups[i].SNO < tups[j].SNO)
}

func main() {
	// suppliers relation, with candidate keys {SNO}
	// the {SName} key is also possible to use
	// type supplierTup struct {
	// 	SNO    int
	// 	SName  string
	// 	Status int
	// 	City   string
	// }

	suppliers := rel.New([]supplierTup{
		{1, "Smith", 20, "London"},
		{2, "Jones", 10, "Paris"},
		{3, "Blake", 30, "Paris"},
		{4, "Clark", 20, "London"},
		{5, "Adams", 30, "Athens"},
	}, [][]string{
		[]string{"SNO"},
	})

	type orderTup struct {
		PNO int
		SNO int
		Qty int
	}

	orders := rel.New([]orderTup{
		{1, 1, 300},
		{1, 2, 200},
		{1, 3, 400},
		{1, 4, 200},
		{1, 5, 100},
		{1, 6, 100},
		{2, 1, 300},
		{2, 2, 400},
		{3, 2, 200},
		{4, 2, 200},
		{4, 4, 300},
		{4, 5, 400},
	}, [][]string{
		[]string{"PNO", "SNO"},
	})

	// the type of the resulting tuples
	// defined elsewhere...
	// type joinTup struct {
	// 	PNO    int
	// 	SNO    int
	// 	Qty    int
	// 	SName  string
	// 	Status int
	// 	City   string
	// }

	partsSuppliers := orders.Join(suppliers, joinTup{})

	// order the output and stick it back into a slice
	// this is really just to get the output into a consistent order.  If you
	// don't care about the order, you don't have to do this.  Currently
	// ordering is not part of the rel package (it isn't a part of relational
	// algebra!)
	res := joinTups{}
	t := make(chan joinTup)
	partsSuppliers.TupleChan(t)
	for v := range t {
		res = append(res, v)
	}
	sort.Sort(res)

	partsSuppliersOrdered := rel.New(res, [][]string{
		[]string{"PNO", "SNO"}, // the candidate key
	})

	fmt.Printf("%v\n", partsSuppliers)
	fmt.Println(rel.PrettyPrint(partsSuppliersOrdered))

	// defined elsewhere...
	// type joinTups []joinTup
	//
	// func (tups joinTups) Len() int      { return len(tups) }
	// func (tups joinTups) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
	// func (tups joinTups) Less(i, j int) bool {
	// 	return tups[i].PNO < tups[j].PNO || (tups[i].PNO == tups[j].PNO && tups[i].SNO < tups[j].SNO)
	// }

}
Output:

Relation(PNO, SNO, Qty) ⋈ Relation(SNO, SName, Status, City)
 +------+------+------+--------+---------+---------+
 |  PNO |  SNO |  Qty |  SName |  Status |    City |
 +------+------+------+--------+---------+---------+
 |    1 |    1 |  300 |  Smith |      20 |  London |
 |    1 |    2 |  200 |  Jones |      10 |   Paris |
 |    1 |    3 |  400 |  Blake |      30 |   Paris |
 |    1 |    4 |  200 |  Clark |      20 |  London |
 |    1 |    5 |  100 |  Adams |      30 |  Athens |
 |    2 |    1 |  300 |  Smith |      20 |  London |
 |    2 |    2 |  400 |  Jones |      10 |   Paris |
 |    3 |    2 |  200 |  Jones |      10 |   Paris |
 |    4 |    2 |  200 |  Jones |      10 |   Paris |
 |    4 |    4 |  300 |  Clark |      20 |  London |
 |    4 |    5 |  400 |  Adams |      30 |  Athens |
 +------+------+------+--------+---------+---------+
Example (Map)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
	"sort"
)

type qtyDouble struct {
	PNO  int
	SNO  int
	Qty1 int
	Qty2 int
}

type qtyDoubles []qtyDouble

func (tups qtyDoubles) Len() int      { return len(tups) }
func (tups qtyDoubles) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
func (tups qtyDoubles) Less(i, j int) bool {
	return tups[i].PNO < tups[j].PNO || (tups[i].PNO == tups[j].PNO && tups[i].SNO < tups[j].SNO)
}

func main() {
	type orderTup struct {
		PNO int
		SNO int
		Qty int
	}

	r1 := rel.New([]orderTup{
		{1, 1, 300},
		{1, 2, 200},
		{1, 3, 400},
		{1, 4, 200},
		{1, 5, 100},
		{1, 6, 100},
		{2, 1, 300},
		{2, 2, 400},
		{3, 2, 200},
		{4, 2, 200},
		{4, 4, 300},
		{4, 5, 400},
	}, [][]string{
		[]string{"PNO", "SNO"},
	})

	// defined elsewhere...
	// type qtyDouble struct {
	//	PNO  int
	//	SNO  int
	//	Qty1 int
	//	Qty2 int
	//}
	mapFcn := func(tup1 orderTup) qtyDouble {
		return qtyDouble{tup1.PNO, tup1.SNO, tup1.Qty, tup1.Qty * 2}
	}

	// an arbitrary function could modify any of the columns, which means
	// we need to explain what the new Keys (if any) will be afterwards
	mapKeys := [][]string{
		[]string{"PNO", "SNO"},
	}

	r2 := r1.Map(mapFcn, mapKeys)
	// order the output and stick it back into a slice
	// this is really just to get the output into a consistent order.  If you
	// don't care about the order, you don't have to do this.  Currently
	// ordering is not part of the rel package (it isn't a part of relational
	// algebra!)
	res := qtyDoubles{}
	t := make(chan qtyDouble)
	r2.TupleChan(t)
	for v := range t {
		res = append(res, v)
	}
	sort.Sort(res)

	r3 := rel.New(res, [][]string{
		[]string{"PNO", "SNO"}, // the candidate key
	})

	fmt.Printf("%v\n", r2)
	fmt.Println(rel.PrettyPrint(r3))

	// defined elsewhere...
	// type qtyDoubles []qtyDouble
	//
	// func (tups qtyDoubles) Len() int      { return len(tups) }
	// func (tups qtyDoubles) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
	// func (tups qtyDoubles) Less(i, j int) bool {
	// 	return tups[i].PNO < tups[j].PNO || (tups[i].PNO == tups[j].PNO && tups[i].SNO < tups[j].SNO)
	//}

}
Output:

Relation(PNO, SNO, Qty).Map({PNO, SNO, Qty}->{PNO, SNO, Qty1, Qty2})
 +------+------+-------+-------+
 |  PNO |  SNO |  Qty1 |  Qty2 |
 +------+------+-------+-------+
 |    1 |    1 |   300 |   600 |
 |    1 |    2 |   200 |   400 |
 |    1 |    3 |   400 |   800 |
 |    1 |    4 |   200 |   400 |
 |    1 |    5 |   100 |   200 |
 |    1 |    6 |   100 |   200 |
 |    2 |    1 |   300 |   600 |
 |    2 |    2 |   400 |   800 |
 |    3 |    2 |   200 |   400 |
 |    4 |    2 |   200 |   400 |
 |    4 |    4 |   300 |   600 |
 |    4 |    5 |   400 |   800 |
 +------+------+-------+-------+
Example (ProjectDistinct)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
	"sort"
)

type PNOSNO struct {
	PNO int
	SNO int
}

type PNOSNOs []PNOSNO

func (tups PNOSNOs) Len() int      { return len(tups) }
func (tups PNOSNOs) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
func (tups PNOSNOs) Less(i, j int) bool {
	return tups[i].PNO < tups[j].PNO || (tups[i].PNO == tups[j].PNO && tups[i].SNO < tups[j].SNO)
}

func main() {
	// the type of the tuples in the input relation

	type orderTup struct {
		PNO int
		SNO int
		Qty int
	}

	r1 := rel.New([]orderTup{
		{1, 1, 300},
		{1, 2, 200},
		{1, 3, 400},
		{1, 4, 200},
		{1, 5, 100},
		{1, 6, 100},
		{2, 1, 300},
		{2, 2, 400},
		{3, 2, 200},
		{4, 2, 200},
		{4, 4, 300},
		{4, 5, 400},
	}, [][]string{
		[]string{"PNO", "SNO"},
	})

	// the type of the tuples in the output relation
	// it is distinct because it contains attributes that are a subset of
	// one of the candidate keys.
	// defined elsewhere:
	// type PNOSNO struct {
	//	PNO int
	// 	SNO int
	// }
	r2 := r1.Project(PNOSNO{})

	// order the output and stick it back into a slice
	// this is really just to get the output into a consistent order.  If you
	// don't care about the order, you don't have to do this.  Currently
	// ordering is not part of the rel package (it isn't a part of relational
	// algebra!)
	res := PNOSNOs{}
	t := make(chan PNOSNO)
	r2.TupleChan(t)
	for v := range t {
		res = append(res, v)
	}

	// defined elsewhere...
	// type PNOSNOs []PNOSNO
	//
	// func (tups PNOSNOs) Len() int           { return len(tups) }
	// func (tups PNOSNOs) Swap(i, j int)      { tups[i], tups[j] = tups[j], tups[i] }
	// func (tups PNOSNOs) Less(i, j int) bool { return tups[i].PNO < tups[j].PNO || (tups[i].PNO == tups[j].PNO && tups[i].SNO < tups[j].SNO) }

	sort.Sort(res)
	r3 := rel.New(res, [][]string{
		[]string{"SNO", "PNO"}, // the candidate key
	})

	fmt.Printf("%v\n", r2)
	fmt.Println(rel.PrettyPrint(r3))
}
Output:

π{PNO, SNO}(Relation(PNO, SNO, Qty))
 +------+------+
 |  PNO |  SNO |
 +------+------+
 |    1 |    1 |
 |    1 |    2 |
 |    1 |    3 |
 |    1 |    4 |
 |    1 |    5 |
 |    1 |    6 |
 |    2 |    1 |
 |    2 |    2 |
 |    3 |    2 |
 |    4 |    2 |
 |    4 |    4 |
 |    4 |    5 |
 +------+------+
Example (ProjectNonDistinct)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
	"sort"
)

type PNOQty struct {
	PNO int
	Qty int
}

type PNOQtys []PNOQty

func (tups PNOQtys) Len() int      { return len(tups) }
func (tups PNOQtys) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
func (tups PNOQtys) Less(i, j int) bool {
	if tups[i].PNO < tups[j].PNO {
		return true
	} else if tups[i].PNO == tups[j].PNO && tups[i].Qty < tups[j].Qty {
		return true
	}
	return false
}

func main() {
	// the type of the tuples in the input relation

	type orderTup struct {
		PNO int
		SNO int
		Qty int
	}

	r1 := rel.New([]orderTup{
		{1, 1, 300},
		{1, 2, 200},
		{1, 3, 400},
		{1, 4, 200},
		{1, 5, 100},
		{1, 6, 100},
		{2, 1, 300},
		{2, 2, 400},
		{3, 2, 200},
		{4, 2, 200},
		{4, 4, 300},
		{4, 5, 400},
	}, [][]string{
		[]string{"PNO", "SNO"},
	})

	// the type of the tuples in the output relation
	// it is not distinct because it does not contain attributes that are a
	// subset of one of the candidate keys.
	// defined elsewhere...
	// type PNOQty struct {
	//	PNO int
	//	Qty int
	//}
	r2 := r1.Project(PNOQty{})

	// order the output and stick it back into a slice
	// this is really just to get the output into a consistent order.  If you
	// don't care about the order, you don't have to do this.  Currently
	// ordering is not part of the rel package (it isn't a part of relational
	// algebra!)
	res := PNOQtys{}
	t := make(chan PNOQty)
	r2.TupleChan(t)
	for v := range t {
		res = append(res, v)
	}
	sort.Sort(res)

	r3 := rel.New(res, [][]string{
		[]string{"PNO", "Qty"}, // the candidate key
	})

	fmt.Printf("%v\n", r2)
	fmt.Println(rel.PrettyPrint(r3))

	// defined elsewhere...
	// type PNOQtys []PNOQty
	//
	// func (tups PNOQtys) Len() int           { return len(tups) }
	// func (tups PNOQtys) Swap(i, j int)      { tups[i], tups[j] = tups[j], tups[i] }
	// func (tups PNOQtys) Less(i, j int) bool {
	// 	if tups[i].PNO < tups[j].PNO {
	// 		return true
	// 	} else if tups[i].PNO == tups[j].PNO && tups[i].Qty < tups[j].Qty {
	// 		return true
	// 	}
	// 	return false
	// }

}
Output:

π{PNO, Qty}(Relation(PNO, SNO, Qty))
 +------+------+
 |  PNO |  Qty |
 +------+------+
 |    1 |  100 |
 |    1 |  200 |
 |    1 |  300 |
 |    1 |  400 |
 |    2 |  300 |
 |    2 |  400 |
 |    3 |  200 |
 |    4 |  200 |
 |    4 |  300 |
 |    4 |  400 |
 +------+------+
Example (Rename)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
)

func main() {
	type orderTup struct {
		PNO int
		SNO int
		Qty int
	}

	r1 := rel.New([]orderTup{
		{1, 1, 300},
		{1, 2, 200},
		{1, 3, 400},
		{1, 4, 200},
		{1, 5, 100},
		{1, 6, 100},
		{2, 1, 300},
		{2, 2, 400},
		{3, 2, 200},
		{4, 2, 200},
		{4, 4, 300},
		{4, 5, 400},
	}, [][]string{
		[]string{"PNO", "SNO"},
	})

	// the type of the tuples in the output relation
	// in this case the position of the fields is significant.  They correspond
	// to the fields in orderTup.
	type titleCaseTup struct {
		Pno int
		Sno int
		Qty int
	}

	r2 := r1.Rename(titleCaseTup{})
	fmt.Println(r2)
	fmt.Println(rel.PrettyPrint(r2))
	// currenty rename does not result in a non deterministic ordering

}
Output:

ρ{Pno, Sno, Qty}/{PNO, SNO, Qty}(Relation(PNO, SNO, Qty))
 +------+------+------+
 |  Pno |  Sno |  Qty |
 +------+------+------+
 |    1 |    1 |  300 |
 |    1 |    2 |  200 |
 |    1 |    3 |  400 |
 |    1 |    4 |  200 |
 |    1 |    5 |  100 |
 |    1 |    6 |  100 |
 |    2 |    1 |  300 |
 |    2 |    2 |  400 |
 |    3 |    2 |  200 |
 |    4 |    2 |  200 |
 |    4 |    4 |  300 |
 |    4 |    5 |  400 |
 +------+------+------+
Example (Restrict)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
)

func main() {
	// the type of the tuples in the relation
	type supplierTup struct {
		SNO    int
		SName  string
		Rating int
		City   string
	}
	r1 := rel.New([]supplierTup{
		{1, "Smith", 3, "London"},
		{2, "Jones", 1, "Paris"},
		{3, "Blake", 3, "Paris"},
		{4, "Clark", 2, "London"},
		{5, "Adams", 3, "Athens"},
		{6, "Coppola Ristorante", 5, "New Providence"},
	}, [][]string{
		[]string{"SNO"}, // the candidat key
	})

	// chose records with rating greater than 4
	r2 := r1.Restrict(rel.Attribute("Rating").GT(4))
	fmt.Println(r2)
	fmt.Println(rel.PrettyPrint(r2))

}
Output:

σ{Rating > 4}(Relation(SNO, SName, Rating, City))
 +------+---------------------+---------+-----------------+
 |  SNO |               SName |  Rating |            City |
 +------+---------------------+---------+-----------------+
 |    6 |  Coppola Ristorante |       5 |  New Providence |
 +------+---------------------+---------+-----------------+
Example (Union)
package main

import (
	"fmt"
	"github.com/jonlawlor/rel"
	"sort"
)

type supplierTup struct {
	SNO    int
	SName  string
	Status int
	City   string
}

type suppliers []supplierTup

func (tups suppliers) Len() int           { return len(tups) }
func (tups suppliers) Swap(i, j int)      { tups[i], tups[j] = tups[j], tups[i] }
func (tups suppliers) Less(i, j int) bool { return tups[i].SNO < tups[j].SNO }

func main() {
	// the type of the tuples in the relation
	// defined elsewhere...
	// type supplierTup struct {
	// 	SNO    int
	// 	SName  string
	// 	Status int
	// 	City   string
	// }
	r1 := rel.New([]supplierTup{
		{1, "Smith", 20, "London"},
		{2, "Jones", 10, "Paris"},
		{3, "Blake", 30, "Paris"},
	}, [][]string{
		[]string{"SNO"}, // the candidat key
	})
	r2 := rel.New([]supplierTup{
		{4, "Clark", 20, "London"},
		{5, "Adams", 30, "Athens"},
		{6, "Coppola Ristorante", 1, "New Providence"},
	}, [][]string{
		[]string{"SNO"}, // the candidat key
	})
	r3 := r1.Union(r2)

	// order the output and stick it back into a slice
	// this is really just to get the output into a consistent order.  If you
	// don't care about the order, you don't have to do this.  Currently
	// ordering is not part of the rel package (it isn't a part of relational
	// algebra!)
	res := suppliers{}
	t := make(chan supplierTup)
	r3.TupleChan(t)
	for v := range t {
		res = append(res, v)
	}

	// defined elsewhere...
	// type suppliers []supplierTup
	//
	// func (tups suppliers) Len() int { return len(tups) }
	// func (tups suppliers) Swap(i, j int) { tups[i], tups[j] = tups[j], tups[i] }
	// func (tups suppliers) Less(i, j int) bool { return tups[i].SNO < tups[j].SNO }

	sort.Sort(res)
	r4 := rel.New(res, [][]string{
		[]string{"SNO"}, // the candidate key
	})

	fmt.Printf("%v\n", r3)
	fmt.Println(rel.PrettyPrint(r4))
}
Output:

Relation(SNO, SName, Status, City) ∪ Relation(SNO, SName, Status, City)
 +------+---------------------+---------+-----------------+
 |  SNO |               SName |  Status |            City |
 +------+---------------------+---------+-----------------+
 |    1 |               Smith |      20 |          London |
 |    2 |               Jones |      10 |           Paris |
 |    3 |               Blake |      30 |           Paris |
 |    4 |               Clark |      20 |          London |
 |    5 |               Adams |      30 |          Athens |
 |    6 |  Coppola Ristorante |       1 |  New Providence |
 +------+---------------------+---------+-----------------+

func New

func New(v interface{}, ckeystr [][]string) Relation

New creates a new Relation from a []struct, map[struct] or chan struct.

If the input v is not a []struct, map[struct], or a chan struct, then this function will panic. If the input candidate keys are not a subset of the attributes of the input relation, then the Err() method of the resulting Relation will be non-nil.

func NewDiff

func NewDiff(r1, r2 Relation) Relation

NewDiff creates a new relation by set minusing the two inputs. It should be used to implement new Relations.

func NewGroupBy

func NewGroupBy(r1 Relation, t2, gfcn interface{}) Relation

NewGroupBy creates a new relation by grouping and applying a user defined function. It should be used to implement new Relations.

func NewJoin

func NewJoin(r1, r2 Relation, zero interface{}) Relation

NewJoin creates a new relation by performing a natural join on the inputs. It should be used to implement new Relations.

func NewMap

func NewMap(r1 Relation, mfcn interface{}, ckeystr [][]string) Relation

NewMap creates a new relation by applying a function to tuples in the source. It should be used to implement new Relations.

func NewProject

func NewProject(r1 Relation, z2 interface{}) Relation

NewProject creates a new relation expression with less than or equal degree t2 has to be a new type which is a subdomain of r. It should be used to implement new Relations.

func NewRename

func NewRename(r1 Relation, z2 interface{}) Relation

NewRename creates a new relation with new column names z2 has to be a struct with the same number of fields as the input relation. It should be used to implement new Relations.

func NewRestrict

func NewRestrict(r1 Relation, p Predicate) Relation

NewRestrict creates a new relation expression with less than or equal cardinality. p has to be a predicate of a subdomain of the input relation. It should be used to implement new Relations.

func NewUnion

func NewUnion(r1, r2 Relation) Relation

NewUnion creates a new relation by unioning the bodies of both inputs. It should be used to implement new Relations.

type XorPred

type XorPred struct {
	P1 Predicate
	P2 Predicate
}

XorPred represents a logical xor predicate

func (XorPred) And

func (p1 XorPred) And(p2 Predicate) AndPred

And predicate

func (XorPred) Domain

func (p1 XorPred) Domain() []Attribute

Domain is the type of input that is required to evalute the predicate

func (XorPred) EvalFunc

func (p1 XorPred) EvalFunc(e reflect.Type) func(t interface{}) bool

EvalFunc returns a function which evalutes a predicate on an input tuple

func (XorPred) Or

func (p1 XorPred) Or(p2 Predicate) OrPred

Or predicate

func (XorPred) String

func (p1 XorPred) String() string

String representation of Xor

func (XorPred) Xor

func (p1 XorPred) Xor(p2 Predicate) XorPred

Xor predicate

Jump to

Keyboard shortcuts

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