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 ¶
- func AttributeMap(fn1, fn2 []Attribute) map[Attribute]FieldIndex
- func Card(r Relation) (i int)
- func CombineTuples(ltup, rtup reflect.Value, ltyp reflect.Type, fMap map[Attribute]FieldIndex) reflect.Value
- func CombineTuples2(to *reflect.Value, from reflect.Value, fMap map[Attribute]FieldIndex)
- func Deg(r Relation) int
- func EnsureChan(ch reflect.Type, zero interface{}) error
- func EnsureGroupFunc(gfcn reflect.Type, inSuper, outSuper interface{}) (inTup, outTup reflect.Type, err error)
- func EnsureMap(m reflect.Type, zero interface{}) error
- func EnsureMapFunc(mfcn reflect.Type, inSuper interface{}) (inTup, outTup reflect.Type, err error)
- func EnsureSameDomain(sub, dom []Attribute) (err error)
- func EnsureSlice(sl reflect.Type, zero interface{}) error
- func EnsureSubDomain(sub, dom []Attribute) (err error)
- func FieldMap(e1, e2 reflect.Type) map[Attribute]FieldIndex
- func FieldTypes(e reflect.Type) []reflect.Type
- func GoString(r Relation) string
- func HeadingString(r Relation) string
- func IsSubDomain(sub, dom []Attribute) bool
- func OrderCandidateKeys(ckeys CandKeys)
- func PartialEquals(tup1 reflect.Value, tup2 reflect.Value, fmap map[Attribute]FieldIndex) bool
- func PartialProject(tup reflect.Value, ltyp, rtyp reflect.Type, ...) (reflect.Value, reflect.Value)
- func PrettyPrint(r Relation) string
- func SubsetCandidateKeys(cKeys1 [][]Attribute, names1 []Attribute, fMap map[Attribute]FieldIndex) [][]Attribute
- type AdHoc
- type AndPred
- type Attribute
- type AttributeSubsetError
- type CandKeys
- type ContainerError
- type DegreeError
- type DomainMismatchError
- type EQPred
- type ElemError
- type FieldIndex
- type GEPred
- type GTPred
- type InDomainError
- type LEPred
- type LTPred
- type NEPred
- type NotPred
- type NumInError
- type NumOutError
- type OrPred
- type OutDomainError
- type Predicate
- type Relation
- func New(v interface{}, ckeystr [][]string) Relation
- func NewDiff(r1, r2 Relation) Relation
- func NewGroupBy(r1 Relation, t2, gfcn interface{}) Relation
- func NewJoin(r1, r2 Relation, zero interface{}) Relation
- func NewMap(r1 Relation, mfcn interface{}, ckeystr [][]string) Relation
- func NewProject(r1 Relation, z2 interface{}) Relation
- func NewRename(r1 Relation, z2 interface{}) Relation
- func NewRestrict(r1 Relation, p Predicate) Relation
- func NewUnion(r1, r2 Relation) Relation
- type XorPred
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 ¶
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 ¶
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 EnsureChan ¶
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 ¶
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 ¶
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 ¶
EnsureSameDomain returns an error if the inputs do not have the same domain.
func EnsureSlice ¶
EnsureSlice returns an error if the the input is not a slice with elements of the specified type.
func EnsureSubDomain ¶
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 ¶
FieldTypes takes a reflect.Type of a struct and returns field types in order
func GoString ¶
GoString returns a string representation of the relation that should evaluate to a relation with identical tuples as the source.
func HeadingString ¶
HeadingString is a string representation of the attributes of a relation formatted like "{foo, bar}"
func IsSubDomain ¶
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 ¶
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 ¶
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.
type AndPred ¶
AndPred represents a logical and predicate
type Attribute ¶
type Attribute string
Attribute represents a particular attribute's name in a relation
func FieldNames ¶
FieldNames takes a reflect.Type of a struct and returns field names in order
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 ¶
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.
type ContainerError ¶
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 ¶
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 (==)
type ElemError ¶
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.
type FieldIndex ¶
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 (>=)
type GTPred ¶
type GTPred struct {
// contains filtered or unexported fields
}
GTPred is a representation of greater than (>)
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 (<=)
type LTPred ¶
type LTPred struct {
// contains filtered or unexported fields
}
LTPred is a representation of less than (<)
type NEPred ¶
type NEPred struct {
// contains filtered or unexported fields
}
NEPred represents a not equal to (!=) operation
type NotPred ¶
type NotPred struct {
P Predicate
}
NotPred represents a logical not of a 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 ¶
OrPred represents a logical or 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 ¶
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 ¶
NewDiff creates a new relation by set minusing the two inputs. It should be used to implement new Relations.
func NewGroupBy ¶
NewGroupBy creates a new relation by grouping and applying a user defined function. It should be used to implement new Relations.
func NewJoin ¶
NewJoin creates a new relation by performing a natural join on the inputs. It should be used to implement new Relations.
func NewMap ¶
NewMap creates a new relation by applying a function to tuples in the source. It should be used to implement new Relations.
func NewProject ¶
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 ¶
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 ¶
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.