在这里插入图片描述
在业务开发过程中除了业务逻辑、数据库之外主要就是数据转换处理过滤等相关的内容,比如合法性验证(可以使用”go-playground/validator“)但是golang在数据处理上面尤其是对结构体上官方几乎没有提供一些便捷的方式,大部分都只能使用for、if等方式自己来处理,也有研发用偷懒的方式全部交给数据库写出了N张表的关联导致了数据库瓶颈等问题。

那么有没有什么更加方便好用的方法来更加方便的处理数据又能够降低代码复杂度,写出更加可读的代码呢?答案是肯定了,比如go-zero中的fx流处理比较适合离线数据统计处理和mr比较适合实时数据并发处理,但今天给大家介绍的主角是“go-linq”通过类ORM的链式方式来进行数据处理。

资料:

golint特性:

  • 用 vanilla Go 编写,没有依赖关系!
  • 使用迭代器模式完成惰性求值
  • 支持并发安全
  • 支持泛型函数,使您的代码更简洁且没有类型断言(但降低性能)
  • 支持数组、切片、映射、字符串、通道和自定义集合

一、🌰开头

首先我们来看一个例子,一个简单的struct获取age大于等于18的ID变成一个slice来给到后面的SQL执行in查询,按照一般的做法就直接开始for循环+if判断+赋值了。

type Person struct {
	ID   int64
	Age  int64
	Name string
}

func getPersonIDList(persons []*Person) []int64 {
	personIDList := make([]int64, 0, len(persons))
	for _, person := range persons {
		if person.Age >= 18 {
			personIDList = append(personIDList, person.ID)
		}
	}
	return personIDList
}

如果是java程序是怎么做的呢? 基于java8的特性可以很方便的通过stream -> filter -> map -> collect来获取出想要的内容也非常简洁

public class Test (
  @Data
  public static class Person (
    private Long id;
    private Integer age;
    private String name;
  }
  
  public List<Long> getPersonIdList(List<Person> personList) {
    return personList.stream()
                     .filter(person -> person.age >= 18)
                     .map(Person::getId).collect(Collectors. toList());
  }
}

导入依赖包

go get github.com/ahmetb/go-linq/v3

如果使用了go-linq也就可以实现类似的效果了,看起来清爽很多

type Person struct {
	ID   int64
	Age  int64
	Name string
}

func getPersonIDList(persons []*Person) (personIDList []int64) {
	linq.From(persons).
		WhereT(func(person *Person) bool { return person.Age >= 18 }).
		SelectT(func(person *Person) int64 { return person.ID }).ToSlice(&personIDList)
	return
}

PS:如果你在意性能并且list比较大就不推荐使用WhereT、SelectT,应该使用Where、Select通过interface来接受入参进行断言避免反射带来的开销,纯性能差距大改造 5 到 10 倍

二、go-linq的功能及场景

官方例子:查找出书最多的作者

	type Book struct {
		id      int
		title   string
		authors []string
	}
	var books []Book
	author := From(books).
		SelectMany( // make a flat array of authors
			func(book interface{}) Query {
				return From(book.(Book).authors)
			}).
		GroupBy( // group by author
			func(author interface{}) interface{} {
				return author // author as key
			}, func(author interface{}) interface{} {
				return author // author as value
			}).
		OrderByDescending( // sort groups by its length
			func(group interface{}) interface{} {
				return len(group.(Group).Group)
			}).
		Select( // get authors out of groups
			func(group interface{}) interface{} {
				return group.(Group).Key
			}).
		First() // take the first author

ForEach

	fruits := []string{"orange", "apple", "lemon", "apple"}
	linq.From(fruits).ForEachIndexedT(func(i int, fruit string) {
		fmt.Println(i, fruit)
	})

Where

	fruits := []string{"apple",
		"passionfruit",
		"banana",
		"mango",
		"orange",
		"blueberry",
		"grape",
		"strawberry"}

	var query []string
	linq.From(fruits).WhereT(func(f string) bool { return len(f) > 6 }).ToSlice(&query)
	fmt.Println(query)

	numbers := []int{0, 30, 20, 15, 90, 85, 40, 75}
	var query2 []int
	linq.From(numbers).WhereIndexedT(func(index int, number int) bool { return number <= index*10 }).ToSlice(&query2)
	fmt.Println(query2)
}

type Product struct {
	Name string
	Code int
}

func Test_Distinct(t *testing.T) {
	ages := []int{21, 46, 46, 55, 17, 22, 55, 55}
	var distinctAges []int
	linq.From(ages).
		OrderBy(
			func(item interface{}) interface{} { return item },
		).
		Distinct().
		ToSlice(&distinctAges)
	fmt.Println(distinctAges)

	products := []Product{
		{Name: "apple", Code: 9},
		{Name: "orange", Code: 4},
		{Name: "apple", Code: 9},
		{Name: "Lemon", Code: 12},
	}

	var noduplicates []Product
	linq.From(products).
		DistinctByT(
			func(item Product) int { return item.Code },
		).
		ToSlice(&noduplicates)
	for _, product := range noduplicates {
		fmt.Printf("%s %d\n", product.Name, product.Code)
	}

Except

	fruits1 := []Product{
		{Name: "apple", Code: 9},
		{Name: "orange", Code: 4},
		{Name: "apple", Code: 9},
		{Name: "Lemon", Code: 12},
	}

	fruits2 := []Product{{Name: "apple", Code: 9}}

	//Order and exclude duplicates.
	var except []Product
	linq.From(fruits1).
		ExceptByT(linq.From(fruits2),
			func(item Product) int { return item.Code },
		).
		ToSlice(&except)

	for _, product := range except {
		fmt.Printf("%s %d\n", product.Name, product.Code)
	}

Intersect

	storel := []Product{
		{Name: "orange", Code: 4},
		{Name: "apple", Code: 9},
	}
	store2 := []Product{
		{Name: "lemon", Code: 12},
		{Name: "apple", Code: 9},
	}

	var duplicates []Product
	linq.From(storel).
		IntersectByT(linq.From(store2),
			func(p Product) int { return p.Code },
		).
		ToSlice(&duplicates)

	for _, p := range duplicates {
		fmt.Println(p.Name, p.Code)
	}

更多的例子可以参考官方的单测实现:

  • https://github.com/ahmetb/go-linq/blob/master/groupjoin_test.go
  • https://github.com/ahmetb/go-linq/blob/master/aggregate_test.go
Logo

秉承“创新、开放、协作、共享”的开源价值观,致力于为大规模开源开放协同创新助力赋能,打造创新成果孵化和新时代开发者培养的开源创新生态!支持公有云使用、私有化部署以及软硬一体化私有部署。

更多推荐