query

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: May 8, 2019 License: MIT Imports: 2 Imported by: 0

README

query

GoDoc Build Status Build Status Coverage Status Codebeat Badge Go Report Card Donate

Simple query language written in Go inspired by Dart's Iterable<E> with cascaded method invocation:

Installation

go get github.com/dmundt/query

Quickstart

See examples at godoc.org.

Example

The following example implements a book database.

Create the tables:

type Author struct {
    AuthorID int
    Name     string
}

type AuthorBook struct {
    AuthorID int
    BookID   int
}

type Book struct {
    BookID int
    Title  string
    Year   int
}

type NameBookID struct {
    Name   string
    BookID int
}

type AuthorTitleYear struct {
    Author string
    Title  string
    Year   int
}

Implement String():

func (a AuthorTitleYear) String() string {
    return fmt.Sprintf("{%v: %v (%v)}", a.Author, a.Title, a.Year)
}

Populate all tables:

func Example() {
    // Populate the autors table:
    authors := []T{
        Author{1, "Austen, Jane"},
        Author{2, "Brontë, Emily"},
        Author{3, "Hunter, Rachel"},
    }

    // Populate the books table:
    books := []T{
        Book{1, "Sense & Sensibility", 1811},
        Book{2, "Pride & Prejudice", 1813},
        Book{3, "Mansfield Park", 1814},
        Book{4, "Emma", 1815},
        Book{5, "Persuasion", 1817},
        Book{6, "Northanger Abbey", 1817},
        Book{7, "Sanditon", 1817},
        Book{8, "Wuthering Heights", 1847},
        Book{9, "Letitia, or, The Castle without a Spectre", 1801},
        Book{10, "The History of the Grubthorpe Family", 1802},
        Book{11, "Letters from Mrs Palmerstone to her Daughter, Inculcating Morality by Entertaining Narratives", 1803},
        Book{12, "The Unexpected Legacy", 1804},
        Book{13, "Family Annals", 1807},
        Book{14, "The Schoolmistress", 1811},
    }

    // Map authors to books.
    author2Books := []T{
        AuthorBook{1, 1},
        AuthorBook{1, 2},
        AuthorBook{1, 3},
        AuthorBook{1, 4},
        AuthorBook{1, 5},
        AuthorBook{1, 6},
        AuthorBook{1, 7},
        AuthorBook{2, 8},
        AuthorBook{3, 9},
        AuthorBook{3, 10},
        AuthorBook{3, 11},
        AuthorBook{3, 12},
        AuthorBook{3, 13},
        AuthorBook{3, 14},
    }

Print all authors, title of their books, published between 1804 and 1815:

    query := From([]T(authors)).
        Join(From(author2Books),
            func(e T) interface{} {
                return e.(Author).AuthorID
            }, func(e T) interface{} {
                return e.(AuthorBook).AuthorID
            }, func(e1, e2 interface{}) interface{} {
                return NameBookID{e1.(Author).Name, e2.(AuthorBook).BookID}
            }).
        Join(From(books),
            func(e T) interface{} {
                return e.(NameBookID).BookID
            }, func(e T) interface{} {
                return e.(Book).BookID
            }, func(e1, e2 interface{}) interface{} {
                return AuthorTitleYear{e1.(NameBookID).Name, e2.(Book).Title, e2.(Book).Year}
            }).
        Sort(
            func(e1, e2 T) bool {
                return e1.(AuthorTitleYear).Year > e2.(AuthorTitleYear).Year
            },
            func(e1, e2 T) bool {
                return e1.(AuthorTitleYear).Author < e2.(AuthorTitleYear).Author
            }).
        Where(
            func(e T) bool {
                return e.(AuthorTitleYear).Year >= 1804
            }, func(e T) bool {
                return e.(AuthorTitleYear).Year <= 1815
            })
    fmt.Printf("%v\n", query)
}

Executing the query yields the following output:

    // Output:
    // [{Austen, Jane: Emma (1815)} {Austen, Jane: Mansfield Park (1814)} {Austen, Jane: Pride & Prejudice (1813)} {Austen, Jane: Sense & Sensibility (1811)} {Hunter, Rachel: The Schoolmistress (1811)} {Hunter, Rachel: Family Annals (1807)} {Hunter, Rachel: The Unexpected Legacy (1804)}]
}

Semantic versioning

Package query uses semantic versioning for satisfying dependency requirements of Go Modules.

License

Package query is covered by a MIT license.

Documentation

Overview

Package query provides primitives for querying slices and user-defined collections.

Example
package main

import (
	"fmt"
)

type Author struct {
	AuthorID int
	Name     string
}

type AuthorBook struct {
	AuthorID int
	BookID   int
}

type Book struct {
	BookID int
	Title  string
	Year   int
}

type NameBookID struct {
	Name   string
	BookID int
}

type AuthorTitleYear struct {
	Author string
	Title  string
	Year   int
}

func (a AuthorTitleYear) String() string {
	return fmt.Sprintf("{%v: %v (%v)}", a.Author, a.Title, a.Year)
}

func main() {
	// Authors table:
	authors := []T{
		Author{1, "Austen, Jane"},
		Author{2, "Brontë, Emily"},
		Author{3, "Hunter, Rachel"},
	}

	// Books table:
	books := []T{
		Book{1, "Sense & Sensibility", 1811},
		Book{2, "Pride & Prejudice", 1813},
		Book{3, "Mansfield Park", 1814},
		Book{4, "Emma", 1815},
		Book{5, "Persuasion", 1817},
		Book{6, "Northanger Abbey", 1817},
		Book{7, "Sanditon", 1817},
		Book{8, "Wuthering Heights", 1847},
		Book{9, "Letitia, or, The Castle without a Spectre", 1801},
		Book{10, "The History of the Grubthorpe Family", 1802},
		Book{11, "Letters from Mrs Palmerstone to her Daughter, Inculcating Morality by Entertaining Narratives", 1803},
		Book{12, "The Unexpected Legacy", 1804},
		Book{13, "Family Annals", 1807},
		Book{14, "The Schoolmistress", 1811},
	}

	// Authors to books table:
	author2Books := []T{
		AuthorBook{1, 1},
		AuthorBook{1, 2},
		AuthorBook{1, 3},
		AuthorBook{1, 4},
		AuthorBook{1, 5},
		AuthorBook{1, 6},
		AuthorBook{1, 7},
		AuthorBook{2, 8},
		AuthorBook{3, 9},
		AuthorBook{3, 10},
		AuthorBook{3, 11},
		AuthorBook{3, 12},
		AuthorBook{3, 13},
		AuthorBook{3, 14},
	}

	// Print all authors, title of their books, published between 1804 and 1815:
	query := From([]T(authors)).
		Join(From(author2Books),
			func(e T) interface{} {
				return e.(Author).AuthorID
			}, func(e T) interface{} {
				return e.(AuthorBook).AuthorID
			}, func(e1, e2 interface{}) interface{} {
				return NameBookID{e1.(Author).Name, e2.(AuthorBook).BookID}
			}).
		Join(From(books),
			func(e T) interface{} {
				return e.(NameBookID).BookID
			}, func(e T) interface{} {
				return e.(Book).BookID
			}, func(e1, e2 interface{}) interface{} {
				return AuthorTitleYear{e1.(NameBookID).Name, e2.(Book).Title, e2.(Book).Year}
			}).
		Sort(
			func(e1, e2 T) bool {
				return e1.(AuthorTitleYear).Year > e2.(AuthorTitleYear).Year
			},
			func(e1, e2 T) bool {
				return e1.(AuthorTitleYear).Author < e2.(AuthorTitleYear).Author
			}).
		Where(
			func(e T) bool {
				return e.(AuthorTitleYear).Year >= 1804
			}, func(e T) bool {
				return e.(AuthorTitleYear).Year <= 1815
			})
	fmt.Printf("%v\n", query)

}
Output:
[{Austen, Jane: Emma (1815)} {Austen, Jane: Mansfield Park (1814)} {Austen, Jane: Pride & Prejudice (1813)} {Austen, Jane: Sense & Sensibility (1811)} {Hunter, Rachel: The Schoolmistress (1811)} {Hunter, Rachel: Family Annals (1807)} {Hunter, Rachel: The Unexpected Legacy (1804)}]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ToSlice

func ToSlice(q *Query) []interface{}

ToSlice iterates over a collection and saves the results in the slice pointed by v. It overwrites the existing slice, starting from index 0.

Types

type Iterator

type Iterator func() (elem T, ok bool)

Iterator is an alias for function which iterates over slices.

type Query

type Query struct {
	Iterate func() Iterator
}

Query is the type returned from query functions. It can be iterated manually.

func From

func From(a []T) *Query

From initializes a query with passed slice as the source.

Example
q := From([]T{1, 3, 5, 7, 9})
fmt.Printf("Got from query: %v\n", q)
Output:
Got from query: [1 3 5 7 9]

func (*Query) Any

func (q *Query) Any(f ...func(e T) bool) bool

Any checks whether any element of this collection satisfies all predicates.

Checks every element in iteration order, and returns true if any of them make test return true, otherwise returns false.

Example (GreatherThan)
isEven := func(e T) bool {
	return e.(int)&1 == 0
}
greaterThan0 := func(e T) bool {
	return e.(int) > 0
}
q := From([]T{1, 3, 6, 7, 9})
v := q.Any(isEven, greaterThan0)
fmt.Printf("An even number > 3 was found: %v\n", v)
Output:
An even number > 3 was found: true
Example (Odd)
isEven := func(e T) bool {
	return e.(int)&1 == 0
}
greaterThan0 := func(e T) bool {
	return e.(int) > 0
}
q := From([]T{1, 3, 5, 7, 9})
v := q.Any(isEven, greaterThan0)
fmt.Printf("An even number > 3 was found: %v\n", v)
Output:
An even number > 3 was found: false

func (*Query) At

func (q *Query) At(i int) (elem T)

At returns the ith element.

The index i must be non-negative and less than length. Index zero represents the first element (so Query.At(0) is equivalent to Query.First()).

May iterate through the elements in iteration order, ignoring the first i elements and then returning the next.

Example (Found)
v := From([]T{1, 2, 3, 4, 5}).At(3)
fmt.Printf("Element at index 5: %v\n", v)
Output:
Element at index 5: 4
Example (NotFound)
v := From([]T{1, 2, 3, 4, 5}).At(15)
fmt.Printf("Element at index 15: %v\n", v)
Output:
Element at index 15: <nil>

func (*Query) Contains

func (q *Query) Contains(e T) bool

Contains returns true if the collection contains an element equal to element. This operation will check each element in order for being equal to element, unless it has a more efficient way to find an element equal to element.

Example (Found)
v := From([]T{1, 2, 3, 4, 5}).Contains(42)
fmt.Printf("Contains 12: %v\n", v)
Output:
Contains 12: false
Example (NotFound)
v := From([]T{1, 2, 3, 4, 5}).Contains(3)
fmt.Printf("Contains 6: %v\n", v)
Output:
Contains 6: true

func (*Query) Every

func (q *Query) Every(f ...func(e T) bool) bool

Every checks whether every element of this collection satisfies all tests. Checks every element in iteration order, and returns false if any of them make test return false, otherwise returns true.

Example (AllEven)
q := From([]T{1, 3, 5, 7, 9})
v := q.Every(func(e T) bool {
	return e.(int)%2 != 1
})
fmt.Printf("All numbers are even: %v\n", v)
Output:
All numbers are even: false
Example (AllOdd)
q := From([]T{1, 3, 5, 7, 9})
v := q.Every(func(e T) bool {
	return e.(int)%2 != 0
})
fmt.Printf("All numbers are odd: %v\n", v)
Output:
All numbers are odd: true

func (*Query) Expand

func (q *Query) Expand(f func(e T) []T) *Query

Expand expands each element of this Query into zero or more elements.

The resulting Query runs through the elements returned by f for each element of this, in iteration order.

The returned Query is lazy, and calls f for each element of this every time it's iterated.

Example (Duplicate)
q := From([]T{1, 2, 3, 4, 5})
v := q.Expand(func(e T) []T {
	return []T{e, e}
})
fmt.Printf("All elements are duplicated: %v\n", v)
Output:
All elements are duplicated: [1 1 2 2 3 3 4 4 5 5]
Example (Identity)
q := From([]T{1, 2, 3, 4, 5})
v := q.Expand(func(e T) []T {
	return []T{e}
})
fmt.Printf("All elements are identical: %v\n", v)
Output:
All elements are identical: [1 2 3 4 5]
Example (Null)
q := From([]T{1, 2, 3, 4, 5})
v := q.Expand(func(e T) []T {
	return []T{}
})
fmt.Printf("All elements are nulled: %v\n", v)
Output:
All elements are nulled: []
Example (Odd)
// Remove even numbers:
q := From([]T{1, 2, 3, 4, 5})
v := q.Expand(func(e T) []T {
	if e.(int)%2 == 0 {
		return []T{}
	}
	return []T{e}
})
fmt.Printf("All elements are odd: %v\n", v)
Output:
All elements are odd: [1 3 5]

func (*Query) First

func (q *Query) First() (first T)

First returns the first element.

Example (Empty)
v := From([]T{}).First()
fmt.Printf("First element: %v", v)
Output:
First element: <nil>
Example (Found)
v := From([]T{1, 2, 3, 4, 5}).First()
fmt.Printf("First element: %v", v)
Output:
First element: 1

func (*Query) Fold

func (q *Query) Fold(v T, f func(v, e T) interface{}) interface{}

Fold reduces a collection to a single value by iteratively combining each element of the collection with an existing value.

Uses v as the initial value, then iterates through the elements and updates the value with each element using the combine function.

Example (Sum)
// Calculating the sum of an query:
sum := func(v, e T) interface{} {
	return v.(int) + e.(int)
}
v := From([]T{1, 2, 3}).Fold(0, sum)
fmt.Printf("Folded elements to sum: %v", v)
Output:
Folded elements to sum: 6

func (*Query) ForEach

func (q *Query) ForEach(f func(e T))

ForEach applies the function f to each element of this collection in iteration order.

Example (Append)
v := []T{}
From([]T{1, 3, 5, 7, 9}).
	ForEach(func(e T) {
		v = append(v, e)
	})
fmt.Printf("For each: %v", v)
Output:
For each: [1 3 5 7 9]
Example (Count)
v := 0
From([]T{1, 3, 5, 7, 9}).
	ForEach(func(e T) {
		v++
	})
fmt.Printf("For each: %v", v)
Output:
For each: 5
Example (Sum)
v := 0
From([]T{1, 2, 3}).
	ForEach(func(e T) {
		v = v + e.(int)
	})
fmt.Printf("For each: %v", v)
Output:
For each: 6

func (*Query) IsEmpty

func (q *Query) IsEmpty() bool

IsEmpty returns true if there are no elements in this collection.

Example (Empty)
v := From([]T{}).IsEmpty()
fmt.Printf("Empty query: %v\n", v)
Output:
Empty query: true
Example (NotEmpty)
v := From([]T{1}).IsEmpty()
fmt.Printf("Empty query: %v\n", v)
Output:
Empty query: false

func (*Query) Join

func (q *Query) Join(inner *Query,
	outKeySel func(e T) interface{},
	innKeySel func(e T) interface{},
	resultSel func(o, i interface{}) interface{}) *Query

Join correlates the elements of two collection based on matching keys.

A join refers to the operation of correlating the elements of two sources of information based on a common key. Join brings the two information sources and the keys by which they are matched together in one method call.

Join preserves the order of the elements of outer collection, and for each of these elements, the order of the matching elements of inner.

Example (Inner)
v := From([]T{1, 2, 3, 4, 5}).
	Join(From([]T{3, 4, 5, 6, 7}),
		// Outer key selector:
		func(e T) interface{} {
			return e
		},
		// Inner key selector:
		func(e T) interface{} {
			return e
		},
		// Result selector:
		func(o, i interface{}) interface{} {
			return []T{o, i}
		})
fmt.Printf("Inner join: %v\n", v)
Output:
Inner join: [[3 3] [4 4] [5 5]]

func (*Query) Last

func (q *Query) Last() (last T)

Last returns the last element.

Example (Empty)
v := From([]T{}).Last()
fmt.Printf("Last element: %v", v)
Output:
Last element: <nil>
Example (Found)
v := From([]T{1, 2, 3, 4, 5}).Last()
fmt.Printf("Last element: %v", v)
Output:
Last element: 5

func (*Query) MapTo

func (q *Query) MapTo(f func(e T) T) *Query

MapTo returns a new lazy Query with elements that are created by calling f on each element of this Query in iteration order.

This method returns a view of the mapped elements. As long as the returned Iterable is not iterated over, the supplied function f will not be invoked. The transformed elements will not be cached. Iterating multiple times over the returned Iterable will invoke the supplied function f multiple times on the same element.

Methods on the returned query are allowed to omit calling f on any element where the result isn't needed.

Example (Add)
// Add a number to every slice collection element:
add := func(e T) T {
	return e.(int) + 10
}
q := From([]T{1, 2, 3, 4, 5})
v := q.MapTo(add)
fmt.Printf("Map q to v: %v\n", v)
Output:
Map q to v: [11 12 13 14 15]
Example (AddToEven)
// Add a number to every even slice collection element:
add := func(e T) T {
	if e.(int)%2 == 0 {
		return e.(int) + 10
	}
	return e.(int)
}
q := From([]T{1, 2, 3, 4, 5})
v := q.MapTo(add)
fmt.Printf("Map q to v: %v\n", v)
Output:
Map q to v: [1 12 3 14 5]

func (*Query) Reduce

func (q *Query) Reduce(f func(v, e T) interface{}) interface{}

Reduce reduces a collection to a single value by iteratively combining elements of the collection using the provided function.

The iterable must have at least one element. If it has only one element, that element is returned.

Otherwise this method starts with the first element from the iterator, and then combines it with the remaining elements in iteration order, as if by:

v := q.First()
q.Skip(1).ForEach(func(e T) {
	v = comb(v, e)
})
return v
Example (Sum)
// Calculating the sum of an query:
sum := func(v, e T) interface{} {
	return v.(int) + e.(int)
}
v := From([]T{1, 2, 3}).Reduce(sum)
fmt.Printf("Reduced elements to sum: %v", v)
Output:
Reduced elements to sum: 6

func (*Query) Skip

func (q *Query) Skip(n int) *Query

Skip returns an Query that provides all but the first n elements.

When the returned query is iterated, it starts iterating over this, first skipping past the initial n elements. If this has fewer than n elements, then the resulting Query is empty. After that, the remaining elements are iterated in the same order as in this query.

The n must not be negative.

Example (Empty)
v := From([]T{1, 2, 3, 4, 5}).Skip(42)
fmt.Printf("Skipped 5 elements: %v", v)
Output:
Skipped 5 elements: []
Example (Found)
v := From([]T{1, 2, 3, 4, 5}).Skip(2)
fmt.Printf("Skipped 5 elements: %v", v)
Output:
Skipped 5 elements: [3 4 5]

func (*Query) Sort

func (q *Query) Sort(f ...func(e, f T) bool) *Query

Sort sorts the elements of a collection in predicate order. Elements are sorted according to a key while keeping the original order of equal elements.

Example (Decreasing)
decreasing := func(e, f T) bool {
	return e.(int) > f.(int)
}
q := From([]T{4, 3, 7, 2, 5, 9, 1, 6, 8})
fmt.Println(q.Sort(decreasing))
Output:
[9 8 7 6 5 4 3 2 1]
Example (Increasing)
increasing := func(e, f T) bool {
	return e.(int) < f.(int)
}
q := From([]T{4, 3, 7, 2, 5, 9, 1, 6, 8})
fmt.Println(q.Sort(increasing))
Output:
[1 2 3 4 5 6 7 8 9]

func (*Query) String

func (q *Query) String() string

String converts the query to a string.

func (*Query) Take

func (q *Query) Take(n int) *Query

Take returns a lazy query of the n first elements of this query.

The returned Query may contain fewer than n elements, if this contains fewer than n elements.

The elements can be computed by stepping through iterator until n elements have been seen.

The n must not be negative.

Example (All)
v := From([]T{1, 2, 3, 4, 5}).Take(42)
fmt.Printf("Taken elements: %v", v)
Output:
Taken elements: [1 2 3 4 5]
Example (Some)
v := From([]T{1, 2, 3, 4, 5}).Take(3)
fmt.Printf("Taken elements: %v", v)
Output:
Taken elements: [1 2 3]

func (*Query) Where

func (q *Query) Where(f ...func(e T) bool) *Query

Where returns a new lazy Query with all elements that satisfy all predicate tests.

The matching elements have the same order in the returned iterable as they have in iterator.

This function returns a view of the mapped elements. As long as the returned Iterable is not iterated over, the supplied function test will not be invoked. Iterating will not cache results, and thus iterating multiple times over the returned Query may invoke the supplied function test multiple times on the same element.

Example (GreaterThan)
where := func(e T) bool {
	return e.(int) > 3
}
v := From([]T{1, 2, 3, 4, 5}).Where(where)
fmt.Printf("Where: %v\n", v)
Output:
Where: [4 5]
Example (LessThan)
where := func(e T) bool {
	return e.(int) > 3
}
v := From([]T{1, 2, 3}).Where(where)
fmt.Printf("Where: %v\n", v)
Output:
Where: []

type T

type T interface{}

T is an interface that has to be implemented by a custom collection in order to work with Query.

Jump to

Keyboard shortcuts

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