parquet--golang使用

article/2025/10/20 23:00:15

github
其实如果不适用一些可视化工具解析parquet文件,不太好看parquet文件内部正常应该是什么样的。但是使用一些可视化工具的话,可以发现,parquet文件会像表格,如excel文件,csv文件那样,排列数据。通过结构体写入的时候也和写入csv文件很像,写一次就是写一行。

安装

go get github.com/xitongsys/parquet-go
type Student struct {Name    string  `parquet:"name=name, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"`Age     int32   `parquet:"name=age, type=INT32, encoding=PLAIN"`Id      int64   `parquet:"name=id, type=INT64"`Weight  float32 `parquet:"name=weight, type=FLOAT"`Sex     bool    `parquet:"name=sex, type=BOOLEAN"`Day     int32   `parquet:"name=day, type=INT32, convertedtype=DATE"`Ignored int32   //without parquet tag and won't write
}这里的结构体中,如果有切片类型的属性,就要使用valuetype标签,因为源码中会判断属性的类型
type Acc struct {Accel []float32 `parquet:"name=accel, valuetype=FLOAT" json:"accel,omitempty"` Gyro  []float32 `parquet:"name=gyro, valuetype=FLOAT" json:"gyro,omitempty"`  Comp  []float32 `parquet:"name=comp, valuetype=FLOAT" json:"comp,omitempty"` 
}
func TestParquetExp(t *testing.T)  {var err errorw, err := os.Create("./flat.parquet")if err != nil {log.Println("Can't create local file", err)return}//writepw, err := writer.NewParquetWriterFromWriter(w, new(Student), 4)//使用4个协程,并发写if err != nil {log.Println("Can't create parquet writer", err)return}pw.RowGroupSize = 128 * 1024 * 1024 //128Mpw.CompressionType = parquet.CompressionCodec_SNAPPYnum := 100for i := 0; i < num; i++ {stu := Student{Name:   "StudentName",Age:    int32(20 + i%5),Id:     int64(i),Weight: float32(50.0 + float32(i)*0.1),Sex:    bool(i%2 == 0),Day:    int32(time.Now().Unix() / 3600 / 24),}if err = pw.Write(stu); err != nil {				//写了100次,也就是100行log.Println("Write error", err)}}if err = pw.WriteStop(); err != nil {log.Println("WriteStop error", err)return}log.Println("Write Finished")w.Close()///readfr, err := local.NewLocalFileReader("./flat.parquet")if err != nil {log.Println("Can't open file")return}pr, err := reader.NewParquetReader(fr, new(Student), 4)if err != nil {log.Println("Can't create parquet reader", err)return}num = int(pr.GetNumRows()) //写了100行,这里就返回100for i := 0; i < num; i++ {//if i%2 == 0 {//	pr.SkipRows(10) //skip 10 rows  0-9,20-29,40-49,60-69,80-89被跳过//	continue//}stus := make([]Student, 2) //这里的切片长度代表一次都多少行if err = pr.Read(&stus); err != nil {log.Println("Read error", err)}log.Println(stus)}pr.ReadStop()fr.Close()}

解析parquet文件
解析parquet文件
在这里插入图片描述

源码

func NewSchemaHandlerFromStruct(obj interface{}) (sh *SchemaHandler, err error)

func NewSchemaHandlerFromStruct(obj interface{}) (sh *SchemaHandler, err error) {defer func() {if r := recover(); r != nil {switch x := r.(type) {case string:err = errors.New(x)case error:err = xdefault:err = errors.New("error occurred")}}}()ot := reflect.TypeOf(obj).Elem()										//获取传入的obj的类型(结构体?切片?)item := NewItem()														//该方法中主要使用的实例item.GoType = GoType													//给item的info属性的属性赋值item.Info.InName = "Parquet_go_root"									//给item的info属性的属性赋值item.Info.ExName = "parquet_go_root"									//给item的info属性的属性赋值item.Info.RepetitionType = parquet.FieldRepetitionType_REQUIRED			//给item的info属性的属性赋值stack := make([]*Item, 1)													//该数组应该是存放obj有几个属性stack[0] = NewItem														//一开始,item是空的,stack也只有一个itemschemaElements := make([]*parquet.SchemaElement, 0)						//存放模板元素的切片infos := make([]*common.Tag, 0)												//应该是每一个属性的信息for len(stack) > 0 {				//stack刚开始长度为1时,主要是在准备将一些东西放进去,如果是obj是结构体,就是要把obj的属性封装为*Item类型的实例放进去,用来处理。之后才是真正处理stack里面的内容ln := len(stack)item = stack[ln-1]stack = stack[:ln-1]    			//1.对于刚开始进入该循环的时候,这里的作用是将stack置空,用来之后存放封装了obj的属性的item  2.之后是真正的便开始处理每个obj的属性var newInfo *common.Tagif item.GoType.Kind() == reflect.Struct {								//obj如果是结构体类型schema := parquet.NewSchemaElement()									//表示模板中的元素。schema.Name = item.Info.InNameschema.RepetitionType = &item.Info.RepetitionTypenumField := int32(item.GoType.NumField())							//obj中的属性个数schema.NumChildren = &numFieldschemaElements = append(schemaElements, schema)						//放一个模板元素进去newInfo = common.NewTag()common.DeepCopy(item.Info, newInfo)									//item这个时候其实只有类型(GoType里面的东西)与传入的obj相关,并且复制了一个新的infoinfos = append(infos, newInfo)      									//把新的info存上for i := int(numField - 1); i >= 0; i-- {								//开始遍历obj的属性f := item.GoType.Field(i)											//获取obj的属性tagStr := f.Tag.Get("parquet")													//获取属性的parquet 标签//ignore item without parquet tagif len(tagStr) <= 0 {												//没有qarquet标签就跳过,忽略numField--continue}newItem := NewItem()//把字符标签“name=comp, type=FLOAT”,转换为*Tag类型的实例(以“,”拆分,再以“=”拆分)InName = {string} "Comp"    ExName = {string} "comp"      Type = {string} "FLOAT"newItem.Info, err = common.StringToTag(tagStr)if err != nil {return nil, fmt.Errorf("failed parse tag: %s", err.Error())}newItem.Info.InName = f.Name 										//将使用反射从结构获取的属性名称赋值给这个item的info属性的InName属性,但是这个属性已经有正确的值了呀newItem.GoType = f.Type                                              //将使用反射从结构获取的属性类型赋值给item的GoType属性if f.Type.Kind() == reflect.Ptr {newItem.GoType = f.Type.Elem()newItem.Info.RepetitionType = parquet.FieldRepetitionType_OPTIONAL}stack = append(stack, newItem)													//将封装了obj一个属性的item存起来}} else if item.GoType.Kind() == reflect.Slice &&item.Info.RepetitionType != parquet.FieldRepetitionType_REPEATED {schema := parquet.NewSchemaElement()schema.Name = item.Info.InNamert1 := item.Info.RepetitionTypeschema.RepetitionType = &rt1var numField int32 = 1schema.NumChildren = &numFieldct1 := parquet.ConvertedType_LISTschema.ConvertedType = &ct1schemaElements = append(schemaElements, schema)newInfo = common.NewTag()common.DeepCopy(item.Info, newInfo)infos = append(infos, newInfo)schema = parquet.NewSchemaElement()schema.Name = "List"rt2 := parquet.FieldRepetitionType_REPEATEDschema.RepetitionType = &rt2schema.NumChildren = &numFieldschemaElements = append(schemaElements, schema)newInfo = common.NewTag()common.DeepCopy(item.Info, newInfo)newInfo.InName, newInfo.ExName = "List", "list"infos = append(infos, newInfo)newItem := NewItem()newItem.Info = common.GetValueTagMap(item.Info) 					//有问题newItem.Info.InName = "Element"newItem.Info.ExName = "element"newItem.GoType = item.GoType.Elem()if newItem.GoType.Kind() == reflect.Ptr {newItem.Info.RepetitionType = parquet.FieldRepetitionType_OPTIONALnewItem.GoType = item.GoType.Elem().Elem()} else {newItem.Info.RepetitionType = parquet.FieldRepetitionType_REQUIRED}stack = append(stack, newItem)} else if item.GoType.Kind() == reflect.Slice &&item.Info.RepetitionType == parquet.FieldRepetitionType_REPEATED {newItem := NewItem()newItem.Info = item.InfonewItem.GoType = item.GoType.Elem()stack = append(stack, newItem)} else if item.GoType.Kind() == reflect.Map {schema := parquet.NewSchemaElement()schema.Name = item.Info.InNamert1 := item.Info.RepetitionTypeschema.RepetitionType = &rt1var numField1 int32 = 1schema.NumChildren = &numField1ct1 := parquet.ConvertedType_MAPschema.ConvertedType = &ct1schemaElements = append(schemaElements, schema)newInfo = common.NewTag()common.DeepCopy(item.Info, newInfo)infos = append(infos, newInfo)schema = parquet.NewSchemaElement()schema.Name = "Key_value"rt2 := parquet.FieldRepetitionType_REPEATEDschema.RepetitionType = &rt2var numField2 int32 = 2schema.NumChildren = &numField2ct2 := parquet.ConvertedType_MAP_KEY_VALUEschema.ConvertedType = &ct2schemaElements = append(schemaElements, schema)newInfo = common.NewTag()common.DeepCopy(item.Info, newInfo)newInfo.InName, newInfo.ExName = "Key_value", "key_value"infos = append(infos, newInfo)newItem := NewItem()newItem.Info = common.GetValueTagMap(item.Info)newItem.GoType = item.GoType.Elem()if newItem.GoType.Kind() == reflect.Ptr {newItem.Info.RepetitionType = parquet.FieldRepetitionType_OPTIONALnewItem.GoType = item.GoType.Elem().Elem()} else {newItem.Info.RepetitionType = parquet.FieldRepetitionType_REQUIRED}stack = append(stack, newItem)newItem = NewItem()newItem.Info = common.GetKeyTagMap(item.Info)newItem.GoType = item.GoType.Key()newItem.Info.RepetitionType = parquet.FieldRepetitionType_REQUIREDstack = append(stack, newItem)} else {schema, err := common.NewSchemaElementFromTagMap(item.Info)if err != nil {return nil, fmt.Errorf("failed to create schema from tag map: %s", err.Error())}schemaElements = append(schemaElements, schema)newInfo = common.NewTag()common.DeepCopy(item.Info, newInfo)infos = append(infos, newInfo)}}res := NewSchemaHandlerFromSchemaList(schemaElements)res.Infos = infosres.CreateInExMap()return res, nil
}

http://chatgpt.dhexx.cn/article/E1CVEkKN.shtml

相关文章

Parquet

动机 创建Parquet是利用压缩性,高效的列式存储来在Haddop生态圈任何项目中应用. 记住Parquet是构建在复杂嵌套的数据结构, 并且使用记录分解和集成的算法在Dremely论文中描述.我们相信这种方法是更强大的的可以非常简单的使嵌套命令空间的扁平化. Parquet构建可以非常高效的…

Parquet 存储格式

1.介绍 Apache Parquet 是 Hadoop 生态圈中一种新型列式存储格式&#xff0c;它可以兼容 Hadoop 生态圈中大多数计算框架(Mapreduce、Spark 等)&#xff0c;被多种查询引擎支持&#xff08;Hive、Impala、Drill 等&#xff09;&#xff0c;并且它是语言和平台无关的。 2.特点…

parquet 简介

参考文章&#xff1a;parquet 简介 Parquet原理 【2019-05-29】Parquet 简介 Apache Parquet是一种能够有效存储嵌套数据的列式存储格式。 面向分析型业务的列式存储格式 由 Twitter 和 Cloudera 合作开发&#xff0c;2015 年 5 月从 Apache 的孵化器里毕业成为 Apache 顶…

Parquet文件详解

1、parquet文件简介 Apache Parquet是Apache Hadoop生态系统的一种免费的开源面向列的数据存储格式。 它类似于Hadoop中可用的其他列存储文件格式&#xff0c;如RCFile格式和ORC格式。 Apache Parquet 是由 Twitter 和 Cloudera 最先发起并合作开发的列存项目&#xff0c;也是…

Gson解析json数据

gson是谷歌推出的&#xff0c;除此之外还有阿里的FastJson&#xff0c;官方json和jackjson。下面通过一个实例来讲解使用gson来解析json数据&#xff1a; 1.先做好准备工作&#xff0c;在网上下载Gson的jar包&#xff0c;放到工程的libs(没有此目录的话自己建一个)目录下: ht…

Android Gson解析json

前言&#xff1a; 解析json的库有很多&#xff0c;如&#xff1a;JSON-Java、Gson、Jackson、FastJson…而Gson是谷歌的&#xff0c;相信自有它的好处 简介 用于json与java对象之间的转换通过 序列化和反序列化 实现功能强大&#xff0c;稳定性也好 使用 Gson提供了两个方…

Android 使用 Gson 解析 json 数据及生成

1.导入 Gson 包 第一种导入Gson 包的方式 在 app 文件下的 build.gradle 文件 导入 gson:2.9.1 包 implementation com.google.code.gson:gson:2.9.1第二种导入Gson 包的方式 直接去下载最新的 Gson 包 下载链接&#xff1a;gson.jar 选择最新的包进行下载 将下载的 gson…

用Gson解析json

首先我们需要导入gson的jar包&#xff0c;因为gson解析方法不是java官方的而是谷歌提供的。 一.把json数据转成java对象 首先因为已经手动导入了jar包&#xff0c;现在只需创建解析器对象&#xff0c;当然首先得有一个json类型的文件地址&#xff0c;和文件输出流 第二步调用…

Android --Gson解析json数据

Android --Gson解析json数据 private void analyseJson() throws Exception {InputStream isgetAssets().open("dataTest.json");ByteArrayOutputStream baosnew ByteArrayOutputStream();byte[] bytesnew byte[1024];int len;while ((lenis.read(bytes))!-1){baos.…

Gson解析json字符串

Gson 怎样使用gson把一个json字符串解析成一个jsonObject对象 因此我要把上面的fastjson转换成是gson&#xff0c;如下图&#xff1a; JsonObject object new JsonParser().parse(result).getAsJsonObject();怎样从gson中取出键的值 使用gson把json字符串转换成一个list集合 …

使用Gson解析Json数据

目录 一、Gson介绍 二、使用方法 完整代码&#xff1a; MainActivity: 布局&#xff1a; 运行结果&#xff1a; 一、Gson介绍 Gson是Google提供的一个Java库&#xff0c;用于将Java对象转换为JSON格式数据或将JSON格式数据转换为Java对象。 常用方法&#xff1a; 方法名…

用GSON解析Json格式数据

GSON是谷歌提供的开源库&#xff0c;用来解析Json格式的数据&#xff0c;非常好用。如果要使用GSON的话&#xff0c;则要先下载gson-2.2.4.jar这个文件&#xff0c;如果是在Android项目中使用&#xff0c;则在Android项目的libs目录下添加这个文件即可&#xff1b;如果是在Java…

Gson解析JSON

1.介绍 Gson是Google提供的处理JSON数据的Java类库&#xff0c;主要用于转换Java对象和JSON对象。 2.依赖 <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency><groupId>com.google.code.gson</groupId><artifac…

python 学习笔记—— #(井号)的作用

在Python语言中&#xff0c;经常看到#后面跟着一些文字。#的作用就是注释&#xff0c;用于解释代码是怎样的逻辑或者作用&#xff0c;方便自己或者别的程序员阅读代码时能够理解代码的意义。 例如 &#xff1a; 我们可以看到# &#xff08;井号&#xff09;跟着的文字是不会被程…

vue 输入网址后,url中自动出现井号#,如何去除

问题描述&#xff1a; 解决方法&#xff1a; 1.打开 2.找到 3.删除Hash 4.成功

URL中#号(井号)的作用

今天又看到了一篇非常好的来自HTTPWatch的文章&#xff0c;不得不推荐给大家。 1. 井号在URL中指定的是页面中的一个位置 井号作为页面定位符出现在URL中&#xff0c;比如&#xff1a;http://www.httpwatch.com/features.htm#print &#xff0c;此URL表示在页面features.htm中p…

StartUML 画类图操作大全

文章目录 基本用法新建项目添加模型添加类图修改项目、模型、类属性配置项目Profile添加类图添加类属性其余Tool的用法 进阶用法枚举方法/属性的类型方法返回值方法/属性的属性方法/属性的静态属性 最后 基本用法 新建项目 打开StartUML&#xff0c;选择Empty Project&#xff…

StarUML使用教程【画类图】

1.打开StarUML 打开后&#xff0c;默认就是画类图的模式 默认创建了一个untitled项目 2.修改工程名字 ** 鼠标双击** 【Untitled】 在这里进行填写相关信息 修改后&#xff0c;就变成了你修改的那个名字 3.创建类图 右键 右侧的名字&#xff0c;选择Add Diagram—>Class…

Idea怎么实现画类图

1.【file】-【Settings】-【Tools】-【Diagrams】-勾选Java Class Diagram的选项 2.右击类&#xff0c;【Diagrams】-【Show Diagram】

使用mermaid画类图

使用mermaid画类图 文章目录 使用mermaid画类图1.类与类之间的关系2.各种关系的强弱顺序 1.类与类之间的关系 mermaid类图关系语法 泛化&#xff08;Inheritance&#xff09; 一种继承关系&#xff0c;表示一般与特殊的关系&#xff0c;它指定了子类如何特化父类的所有特征和…