Quickstart - fanliao/go-plinq GitHub Wiki

Here is a crash course go-plinq! Here is what we are going to cover:

  1. From-Where-Select
  2. Lazy executing
  3. Channel as data source or output
  4. GroupBy and Ordering
  5. Aggregate operations
  6. Set Operations
  7. Joins

Let's create the object model, function and a few initial data:

type User struct {
	id  int
	name string
	age int
	role *Role
}
type Role struct {
	name  string
    id int
}

administrator := Role{"Administrator",   10}
customer := Role{"Customer",    20}
staff := Role{"Staff", 30}
nothing := Role{"nothing", 40}

henry := User{2, "Henry", 18, &customer}
rock := User{1, "Rock",   20, &customer}
jack := User{3, "Jack",   25, &administrator}
stone := User{4, "Stone",   38, &staff}
vic := User{5, "Vic",   22, &staff}

roles := []*Role{&administrator, &customer, &staff, &nothing}
users := []*User{&jack, &rock, &henry, &stone, &vic}

usersChan := func() chan *User{
    ch := make(chan *User)
	go func(){
		for _, v := range users{
			ch <- v
		}
		close(ch)
	}()
	return ch
}

1. From-Where-Select

Query: Find names of user who is Customer. You can use both slice and channel as data source.

isCustomer := func(u interface{}) (bool){
     return u.(*User).role.name == "Customer"
}
getName := func(u interface{}) interface{}{
     return u.(*User).name
}
names, err := From(users).Where(isCustomer).Select(getName).Results()
// return: ["Rock", "Henry"]

As you see, Results function returns an additional error parameter and this error will be carried on as the final error in err variable. Here, names variable has type of []interface{}, you can assert type to string and use it.

2. Lazy executing

Note that you can pass a point of slice to From(), the query will be executed until Results() be called

pUsers := &users
q := From(pUsers).Where(isCustomer).Select(getName)

*pUsers = append(*pUsers, &User{1, "Hal",  100, &customer})
names, err := q.Results()
// return: ["Rock", "Henry", "Hal"]

You can also set data source before calling Results() or ToChan() method

q := NewQuery().Where(isCustomer).Select(getName)

names, err := q.SetDataSource(users).Results()
// return: ["Rock", "Henry"]

names, err = q.SetDataSource(usersChan()).Results()
// return: ["Rock", "Henry"]

3. Channel as data source or output

You can use channel as data source.

names, err := From(usersChan()).Where(isCustomer).Select(getName).Results()
// return: ["Rock", "Henry"]

You can also use channel as output.

q := From(usersChan()).Where(isCustomer).Select(getName)
namesChan, errChan, err := q.ToChan()

rs := make([]interface{}, 0, 1)
for v := range namesChan {
	rs = append(rs, v)
}
// rs is: ["Rock", "Henry"]

if e, ok := <-errChan; ok {
	fmt.Println("get error:", e)
}

You can also use channel as the parameter of Join, Concat, Union

4. GroupBy and Ordering

Query: Group user by role id

groupKeySelector := func(u interface{}) interface{}{
     return u.(*User).role.id
}
groups, err := plinq.From(users).GroupBy(groupKeySelector).Results()
// return: {10, [&jack]}, {20 , [&rock, &henry]}, {30 , [&stone, &vic]}

for _, o := range groups {
	kv := o.(*plinq.KeyValue)
	fmt.Print("{", kv.Key, ",", kv.Value, "}")
}

Query: Order user by role id

orderUserById := func(v1 interface{}, v2 interface{}) int {
	u1, u2 := v1.(*User), v2.(*User)
	if u1.id < u2.id {
		return -1
	} else if u1.id == u2.id {
		return 0
	} else {
		return 1
	}
}
ordereds, err := From(users).OrderBy(orderUserById).Results()
//return [&rock, &henry, &jack, &stone, &vic]

5. Aggregate operations

Query: Find age of youngest user

userAge := func(u interface{})interface{}{ 
    return u.(*User).age
}
minAge, err := From(users).Aggregate(Min(userAge))
// return: 18

You can also get a few aggregate values once.

minAge, err := From(users).Select(userAge).Aggregate(Min(), Max(), Count())
// return: [18, 38, 5]

6. Set Operations

Assume we have two users

Users1 := []*User{&rock, &jack, &vic}
Users2 := []*User{&henry, &stone, &vic, &vic}

Query: Find members of both users. Then find only user1 members. Then find all members.

bothUsers, err := From(Users1).Intersect(Users2).Results()
//return: [&vic]

onlyUsers1, err := From(Users1).Except(Users2).Results()
//return: [&rock, &jack]

allUsers, err := From(Users1).Union(Users2).Results()
//return: [&rock, &jack, &vic, &henry, &stone]

Query: Return each user only once.

users, err := From(Users2).Distinct().Results() 
// {&henry, &stone, &vic}

7. Joins

Query: Find role names with user name.

In this query, we will join roles and users slices. We can make roles the outer collection and users the inner collection. Query will join on Role.id and will return a namePair of <userName, roleName>.

This is a typical equi-join operation.

type namePair struct {userName, roleName string}

outerKeySel := func(v interface{}) interface{} { return v.(*Role).id } 
innerKeySel := func(v interface{}) interface{} { return v.(*User).role.id }
resultSel   := func(this, that interface{}) interface{} { 
	return namePair{this.(*Role).name, that.(*User).name} 
}

pairs, err := From(roles).Join(users, outerKeySel, innerKeySel, resultSel).Results()
// return: 
// [{"Customer", "Rock" }, {"Customer", "Henry",}, {"Administrator", "Jack"}
//   , {"Staff", "Stone"},  {"Staff", "Vic"}] 

You can also left join

resultSel := func(this, that interface{}) interface{} { 
	if that == nil{
		return namePair{this.(*Role).name, ""} 
	} else {
		return namePair{this.(*Role).name, that.(*User).name} 
	}
}

pairs, err := From(roles).LeftJoin(users, outerKeySel, innerKeySel, resultSel).Results()
// return: 
// [{"Customer", "Rock" }, {"Customer", "Henry",}, {"Administrator", "Jack"}, 
//  {"Staff", "Stone"},  {"Staff", "Vic"},   {"Nothing", ""}] 

Query: Find number of user by each role.

In this query we will do a group-join, again based on Role.id, outer collection is roles and inner collection is users. Returned pair will be &lt;<name, countOfUser>&gt;

type pair struct {
    name string
    countOfUser int
}
outerKeySel := func(v interface{}) interface{} { return v.(*Role).id } 
innerKeySel := func(v interface{}) interface{} { return v.(*User).role.id }
resultSel   := func(outer interface{}, inners []interface{}) interface{} {
    return pair{outer.(*Role).name, len(inners)}
}

pairs, err := From(roles).GroupJoin(users, outerKeySel, innerKeySel, resultSel).Results()
// return: 
//     [{"Administrator", 1}, {"Customer", 2}, {"Staff", 2}]

You can also left join

q := From(roles).LeftGroupJoin(users, outerKeySel, innerKeySel, resultSel)
pairs, err := q.Results()
// return: 
//     [{"Customer", 2}, {"Administrator", 1}, {"Staff", 2}, {"Nothing", 0}]

Next: Query Functions