Quickstart - fanliao/go-plinq GitHub Wiki
Here is a crash course go-plinq
!
Here is what we are going to cover:
- From-Where-Select
- Lazy executing
- Channel as data source or output
- GroupBy and Ordering
- Aggregate operations
- Set Operations
- 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, ¬hing}
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 <<name, countOfUser>>
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}]