embulk-parser-firebase_avro 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +27 -0
- data/.gitignore +80 -0
- data/.scalafmt.conf +2 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/build.gradle +81 -0
- data/build.sbt +29 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +6 -0
- data/gradlew +169 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/parser/firebase_avro.rb +3 -0
- data/project/build.properties +1 -0
- data/project/plugins.sbt +3 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/FirebaseAvroParserPlugin.scala +66 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/LoanPattern.scala +19 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/Parser.scala +111 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/PluginTask.scala +5 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/ValueHolder.scala +5 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/column/Column.scala +61 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/column/Columns.scala +47 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/column/EventDimension.scala +19 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/column/UserDimension.scala +28 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/Root.scala +5 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/Event_Dim.scala +8 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/User_Dim.scala +11 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/event_dim/Params.scala +3 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/event_dim/params/Value.scala +6 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/App_Info.scala +7 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/Bundle_Info.scala +3 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/Device_Info.scala +13 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/Geo_Info.scala +3 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/Ltv_Info.scala +3 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/Traffic_Source.scala +5 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/User_Properties.scala +3 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/user_properties/Value.scala +5 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/define/root/user_dim/user_properties/value/Value.scala +6 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/json/CustomEncoder.scala +21 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/json/event_dim/EventParmsJsonSerializer.scala +29 -0
- data/src/main/scala/org/embulk/parser/firebase_avro/json/user_dim/UserPropertiesJsonSerializer.scala +34 -0
- data/src/test/scala/org/embulk/parser/firebase_avro/Implicitly.scala +9 -0
- data/src/test/scala/org/embulk/parser/firebase_avro/ParserTest.scala +22 -0
- data/src/test/scala/org/embulk/parser/firebase_avro/column/ColumnsTest.scala +18 -0
- data/src/test/scala/org/embulk/parser/firebase_avro/json/event_dim/EventParmsJsonSerializerTest.scala +19 -0
- metadata +138 -0
data/gradlew.bat
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
@if "%DEBUG%" == "" @echo off
|
2
|
+
@rem ##########################################################################
|
3
|
+
@rem
|
4
|
+
@rem Gradle startup script for Windows
|
5
|
+
@rem
|
6
|
+
@rem ##########################################################################
|
7
|
+
|
8
|
+
@rem Set local scope for the variables with windows NT shell
|
9
|
+
if "%OS%"=="Windows_NT" setlocal
|
10
|
+
|
11
|
+
set DIRNAME=%~dp0
|
12
|
+
if "%DIRNAME%" == "" set DIRNAME=.
|
13
|
+
set APP_BASE_NAME=%~n0
|
14
|
+
set APP_HOME=%DIRNAME%
|
15
|
+
|
16
|
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
17
|
+
set DEFAULT_JVM_OPTS=
|
18
|
+
|
19
|
+
@rem Find java.exe
|
20
|
+
if defined JAVA_HOME goto findJavaFromJavaHome
|
21
|
+
|
22
|
+
set JAVA_EXE=java.exe
|
23
|
+
%JAVA_EXE% -version >NUL 2>&1
|
24
|
+
if "%ERRORLEVEL%" == "0" goto init
|
25
|
+
|
26
|
+
echo.
|
27
|
+
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
28
|
+
echo.
|
29
|
+
echo Please set the JAVA_HOME variable in your environment to match the
|
30
|
+
echo location of your Java installation.
|
31
|
+
|
32
|
+
goto fail
|
33
|
+
|
34
|
+
:findJavaFromJavaHome
|
35
|
+
set JAVA_HOME=%JAVA_HOME:"=%
|
36
|
+
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
37
|
+
|
38
|
+
if exist "%JAVA_EXE%" goto init
|
39
|
+
|
40
|
+
echo.
|
41
|
+
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
42
|
+
echo.
|
43
|
+
echo Please set the JAVA_HOME variable in your environment to match the
|
44
|
+
echo location of your Java installation.
|
45
|
+
|
46
|
+
goto fail
|
47
|
+
|
48
|
+
:init
|
49
|
+
@rem Get command-line arguments, handling Windows variants
|
50
|
+
|
51
|
+
if not "%OS%" == "Windows_NT" goto win9xME_args
|
52
|
+
|
53
|
+
:win9xME_args
|
54
|
+
@rem Slurp the command line arguments.
|
55
|
+
set CMD_LINE_ARGS=
|
56
|
+
set _SKIP=2
|
57
|
+
|
58
|
+
:win9xME_args_slurp
|
59
|
+
if "x%~1" == "x" goto execute
|
60
|
+
|
61
|
+
set CMD_LINE_ARGS=%*
|
62
|
+
|
63
|
+
:execute
|
64
|
+
@rem Setup the command line
|
65
|
+
|
66
|
+
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
67
|
+
|
68
|
+
@rem Execute Gradle
|
69
|
+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
70
|
+
|
71
|
+
:end
|
72
|
+
@rem End local scope for the variables with windows NT shell
|
73
|
+
if "%ERRORLEVEL%"=="0" goto mainEnd
|
74
|
+
|
75
|
+
:fail
|
76
|
+
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
77
|
+
rem the _cmd.exe /c_ return code!
|
78
|
+
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
79
|
+
exit /b 1
|
80
|
+
|
81
|
+
:mainEnd
|
82
|
+
if "%OS%"=="Windows_NT" endlocal
|
83
|
+
|
84
|
+
:omega
|
@@ -0,0 +1 @@
|
|
1
|
+
sbt.version=0.13.15
|
data/project/plugins.sbt
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro
|
2
|
+
|
3
|
+
import java.io.InputStream
|
4
|
+
|
5
|
+
import scala.collection.JavaConverters._
|
6
|
+
import com.sksamuel.avro4s.AvroInputStream
|
7
|
+
import io.circe.Json
|
8
|
+
import org.apache.commons.compress.utils.IOUtils
|
9
|
+
import org.embulk.config.ConfigSource
|
10
|
+
import org.embulk.config.TaskSource
|
11
|
+
import org.embulk.parser.firebase_avro.column.Columns
|
12
|
+
import org.embulk.parser.firebase_avro.define.Root
|
13
|
+
import org.embulk.spi._
|
14
|
+
import org.embulk.spi.json.JsonParser
|
15
|
+
import org.embulk.spi.time.Timestamp
|
16
|
+
import org.embulk.spi.util.FileInputInputStream
|
17
|
+
|
18
|
+
object FirebaseAvroParserPlugin {
|
19
|
+
def buildColumn(): Schema = {
|
20
|
+
new Schema(Columns.instance.map(_.embulkColumn).asJava)
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
class FirebaseAvroParserPlugin extends ParserPlugin {
|
25
|
+
override def transaction(config: ConfigSource, control: ParserPlugin.Control): Unit = {
|
26
|
+
val task = config.loadConfig(classOf[PluginTask])
|
27
|
+
control.run(task.dump, FirebaseAvroParserPlugin.buildColumn())
|
28
|
+
}
|
29
|
+
|
30
|
+
override def run(taskSource: TaskSource, schema: Schema, input: FileInput, output: PageOutput): Unit =
|
31
|
+
LoanPattern(new FileInputInputStream(input)) { efis =>
|
32
|
+
LoanPattern(new PageBuilder(Exec.getBufferAllocator, schema, output)) { pb =>
|
33
|
+
while (efis.nextFile()) {
|
34
|
+
addRecords(efis, pb)
|
35
|
+
}
|
36
|
+
pb.finish()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
def addRecords(is: InputStream, pb: PageBuilder): Unit =
|
41
|
+
AvroInputStream.data[Root](IOUtils.toByteArray(is)).iterator().foreach { record =>
|
42
|
+
Parser(record).foreach { rows =>
|
43
|
+
rows.foreach {
|
44
|
+
case ValueHolder(c, Some(x: Int)) =>
|
45
|
+
pb.setLong(c, x)
|
46
|
+
case ValueHolder(c, Some(x: Long)) =>
|
47
|
+
pb.setLong(c, x)
|
48
|
+
case ValueHolder(c, Some(x: Double)) =>
|
49
|
+
pb.setDouble(c, x)
|
50
|
+
case ValueHolder(c, Some(x: Float)) =>
|
51
|
+
pb.setDouble(c, x)
|
52
|
+
case ValueHolder(c, Some(x: Boolean)) =>
|
53
|
+
pb.setBoolean(c, x)
|
54
|
+
case ValueHolder(c, Some(x: String)) =>
|
55
|
+
pb.setString(c, x)
|
56
|
+
case ValueHolder(c, Some(x: Json)) =>
|
57
|
+
pb.setJson(c, new JsonParser().parse(x.noSpaces))
|
58
|
+
case ValueHolder(c, Some(x: Timestamp)) =>
|
59
|
+
pb.setTimestamp(c, x)
|
60
|
+
case ValueHolder(c, None) =>
|
61
|
+
pb.setNull(c)
|
62
|
+
}
|
63
|
+
pb.addRecord()
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro
|
2
|
+
|
3
|
+
import scala.util.control.Exception.ignoring
|
4
|
+
|
5
|
+
object LoanPattern {
|
6
|
+
|
7
|
+
type Closable = { def close(): Unit }
|
8
|
+
|
9
|
+
def apply[R <: Closable, A](resource: R)(f: R => A): A = {
|
10
|
+
try {
|
11
|
+
f(resource)
|
12
|
+
} finally {
|
13
|
+
ignoring(classOf[Throwable]) apply {
|
14
|
+
resource.close()
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
}
|
@@ -0,0 +1,111 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro
|
2
|
+
|
3
|
+
import org.embulk.parser.firebase_avro.column.Columns
|
4
|
+
import org.embulk.parser.firebase_avro.define.Root
|
5
|
+
import org.embulk.parser.firebase_avro.define.root.{Event_Dim, User_Dim}
|
6
|
+
import org.embulk.parser.firebase_avro.json.event_dim.EventParmsJsonSerializer
|
7
|
+
import org.embulk.parser.firebase_avro.json.user_dim.UserPropertiesJsonSerializer
|
8
|
+
|
9
|
+
import scala.language.reflectiveCalls
|
10
|
+
|
11
|
+
object Parser {
|
12
|
+
|
13
|
+
def apply(record: Root): Seq[Seq[ValueHolder[_]]] = {
|
14
|
+
val userFields = userDims(record.user_dim.getOrElse(sys.error("could not get user")))
|
15
|
+
if (record.event_dim.isEmpty) sys.error("empty event")
|
16
|
+
record.event_dim.map {
|
17
|
+
userFields ++ eventDims(_)
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
private def eventDims(eventDim: Event_Dim) = {
|
22
|
+
val c = Columns.find("event_dim", _: String)
|
23
|
+
Seq(
|
24
|
+
ValueHolder(c("name"), eventDim.name),
|
25
|
+
ValueHolder(c("date"), eventDim.date),
|
26
|
+
ValueHolder(c("timestamp_micros"), eventDim.timestamp_micros),
|
27
|
+
ValueHolder(c("previous_timestamp_micros"), eventDim.previous_timestamp_micros),
|
28
|
+
ValueHolder(c("value_in_usd"), eventDim.value_in_usd),
|
29
|
+
ValueHolder(c("params"), EventParmsJsonSerializer(eventDim.params))
|
30
|
+
)
|
31
|
+
}
|
32
|
+
|
33
|
+
private def userDims(userDim: User_Dim) = {
|
34
|
+
val userFields = {
|
35
|
+
val c = Columns.find("user_dim", _: String)
|
36
|
+
Seq(
|
37
|
+
ValueHolder(c("user_id"), userDim.user_id),
|
38
|
+
ValueHolder(c("first_open_timestamp_micros"), userDim.first_open_timestamp_micros),
|
39
|
+
ValueHolder(c("user_properties"), UserPropertiesJsonSerializer(userDim.user_properties))
|
40
|
+
)
|
41
|
+
}
|
42
|
+
|
43
|
+
val appInfo = {
|
44
|
+
val c = Columns.find("app_info", _: String)
|
45
|
+
val that = userDim.app_info
|
46
|
+
Seq(
|
47
|
+
ValueHolder(c("app_id"), that.flatMap(_.app_id)),
|
48
|
+
ValueHolder(c("app_instance_id"), that.flatMap(_.app_instance_id)),
|
49
|
+
ValueHolder(c("app_platform"), that.flatMap(_.app_platform)),
|
50
|
+
ValueHolder(c("app_store"), that.flatMap(_.app_store)),
|
51
|
+
ValueHolder(c("app_version"), that.flatMap(_.app_version))
|
52
|
+
)
|
53
|
+
}
|
54
|
+
|
55
|
+
val bundleInfo = {
|
56
|
+
val c = Columns.find("bundle_info", _: String)
|
57
|
+
val that = userDim.bundle_info
|
58
|
+
Seq(
|
59
|
+
ValueHolder(c("bundle_sequence_id"), that.flatMap(_.bundle_sequence_id)),
|
60
|
+
ValueHolder(c("server_timestamp_offset_micros"), that.flatMap(_.server_timestamp_offset_micros))
|
61
|
+
)
|
62
|
+
}
|
63
|
+
|
64
|
+
val geoInfo = {
|
65
|
+
val c = Columns.find("geo_info", _: String)
|
66
|
+
val that = userDim.geo_info
|
67
|
+
Seq(
|
68
|
+
ValueHolder(c("city"), that.flatMap(_.city)),
|
69
|
+
ValueHolder(c("continent"), that.flatMap(_.continent)),
|
70
|
+
ValueHolder(c("country"), that.flatMap(_.country)),
|
71
|
+
ValueHolder(c("region"), that.flatMap(_.region))
|
72
|
+
)
|
73
|
+
}
|
74
|
+
|
75
|
+
val deviceInfo = {
|
76
|
+
val c = Columns.find("device_info", _: String)
|
77
|
+
val that = userDim.device_info
|
78
|
+
Seq(
|
79
|
+
ValueHolder(c("device_category"), that.flatMap(_.device_category)),
|
80
|
+
ValueHolder(c("device_id"), that.flatMap(_.device_id)),
|
81
|
+
ValueHolder(c("device_model"), that.flatMap(_.device_model)),
|
82
|
+
ValueHolder(c("device_time_zone_offset_seconds"), that.flatMap(_.device_time_zone_offset_seconds)),
|
83
|
+
ValueHolder(c("limited_ad_tracking"), that.flatMap(_.limited_ad_tracking)),
|
84
|
+
ValueHolder(c("mobile_brand_name"), that.flatMap(_.mobile_brand_name)),
|
85
|
+
ValueHolder(c("mobile_marketing_name"), that.flatMap(_.mobile_marketing_name)),
|
86
|
+
ValueHolder(c("mobile_model_name"), that.flatMap(_.mobile_model_name)),
|
87
|
+
ValueHolder(c("platform_version"), that.flatMap(_.platform_version)),
|
88
|
+
ValueHolder(c("resettable_device_id"), that.flatMap(_.resettable_device_id)),
|
89
|
+
ValueHolder(c("user_default_language"), that.flatMap(_.user_default_language))
|
90
|
+
)
|
91
|
+
}
|
92
|
+
|
93
|
+
val trafficSource = {
|
94
|
+
val c = Columns.find("traffic_source", _: String)
|
95
|
+
val that = userDim.traffic_source
|
96
|
+
Seq(
|
97
|
+
ValueHolder(c("user_acquired_campaign"), that.flatMap(_.user_acquired_campaign)),
|
98
|
+
ValueHolder(c("user_acquired_medium"), that.flatMap(_.user_acquired_medium)),
|
99
|
+
ValueHolder(c("user_acquired_source"), that.flatMap(_.user_acquired_source))
|
100
|
+
)
|
101
|
+
}
|
102
|
+
|
103
|
+
val ltvInfo = {
|
104
|
+
val c = Columns.find("ltv_info", _: String)
|
105
|
+
val that = userDim.ltv_info
|
106
|
+
Seq(ValueHolder(c("currency"), that.flatMap(_.currency)), ValueHolder(c("revenue"), that.flatMap(_.revenue)))
|
107
|
+
}
|
108
|
+
|
109
|
+
userFields ++ appInfo ++ bundleInfo ++ geoInfo ++ deviceInfo ++ trafficSource ++ ltvInfo
|
110
|
+
}
|
111
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro.column
|
2
|
+
|
3
|
+
import org.embulk.spi.{Column => EmbulkColumn}
|
4
|
+
import org.embulk.spi.`type`.Types
|
5
|
+
|
6
|
+
import scala.language.implicitConversions
|
7
|
+
|
8
|
+
object Column {
|
9
|
+
|
10
|
+
import scala.reflect.runtime.universe._
|
11
|
+
|
12
|
+
def getGeneral[A](index: Int)(implicit tpe: TypeTag[A]): Seq[Column] = {
|
13
|
+
var counter = index
|
14
|
+
typeOf[A].members.filter(!_.isMethod).flatMap { implicit member =>
|
15
|
+
val column = member.typeSignature match {
|
16
|
+
case t if t =:= typeOf[Option[String]] =>
|
17
|
+
Some(factory(counter, Types.STRING))
|
18
|
+
case t if t =:= typeOf[Option[Int]] =>
|
19
|
+
Some(factory(counter, Types.LONG))
|
20
|
+
case t if t =:= typeOf[Option[Long]] =>
|
21
|
+
Some(factory(counter, Types.LONG))
|
22
|
+
case t if t =:= typeOf[Option[Double]] =>
|
23
|
+
Some(factory(counter, Types.DOUBLE))
|
24
|
+
case t if t =:= typeOf[Option[Boolean]] =>
|
25
|
+
Some(factory(counter, Types.BOOLEAN))
|
26
|
+
case t if t =:= typeOf[String] =>
|
27
|
+
Some(factory(counter, Types.STRING))
|
28
|
+
case t if t =:= typeOf[Int] =>
|
29
|
+
Some(factory(counter, Types.LONG))
|
30
|
+
case t if t =:= typeOf[Long] =>
|
31
|
+
Some(factory(counter, Types.LONG))
|
32
|
+
case t if t =:= typeOf[Double] =>
|
33
|
+
Some(factory(counter, Types.DOUBLE))
|
34
|
+
case t if t =:= typeOf[Boolean] =>
|
35
|
+
Some(factory(counter, Types.BOOLEAN))
|
36
|
+
case _ =>
|
37
|
+
counter = counter - 1
|
38
|
+
None
|
39
|
+
}
|
40
|
+
counter = counter + 1
|
41
|
+
column
|
42
|
+
}
|
43
|
+
}.toList
|
44
|
+
|
45
|
+
private def factory[A](counter: Int, tpe: org.embulk.spi.`type`.Type)(implicit tt: TypeTag[A],
|
46
|
+
symbol: Symbol): Column = {
|
47
|
+
val fullpath =
|
48
|
+
(tt.tpe.etaExpand.toString.toLowerCase + "." + symbol.name.toString).trim
|
49
|
+
Column(fullpath, new EmbulkColumn(counter, name, tpe))
|
50
|
+
}
|
51
|
+
|
52
|
+
def name[A](implicit tpe: TypeTag[A], symbol: Symbol): String = {
|
53
|
+
val fullpath = tpe.tpe.etaExpand.toString
|
54
|
+
val lessLength = "org.embulk.parser.firebase_avro.define.root.".length
|
55
|
+
(fullpath
|
56
|
+
.substring(lessLength)
|
57
|
+
.toLowerCase + "." + symbol.name.toString).trim
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
case class Column(fullPath: String, embulkColumn: EmbulkColumn)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro.column
|
2
|
+
|
3
|
+
import org.embulk.spi.{Column => EmbulkColumn}
|
4
|
+
|
5
|
+
object Columns {
|
6
|
+
val instance = Columns()
|
7
|
+
private val map: Map[String, EmbulkColumn] = instance.map { c =>
|
8
|
+
c.fullPath -> c.embulkColumn
|
9
|
+
}.toMap
|
10
|
+
|
11
|
+
private var cache: Map[String, EmbulkColumn] = Map.empty
|
12
|
+
|
13
|
+
def find(className: String, fieldName: String): EmbulkColumn = {
|
14
|
+
cache.get(s"$className.$fieldName") match {
|
15
|
+
case Some(v) => v
|
16
|
+
case None =>
|
17
|
+
val detect = map.flatMap {
|
18
|
+
case (name, column) =>
|
19
|
+
if (name.contains(s"$className.$fieldName")) Some(column)
|
20
|
+
else None
|
21
|
+
}
|
22
|
+
if (detect.size == 1) {
|
23
|
+
val result = detect.head
|
24
|
+
cache = cache ++ Map(s"$className.$fieldName" -> detect.head)
|
25
|
+
result
|
26
|
+
} else
|
27
|
+
sys.error(s"could not find column. ${className + "." + fieldName}")
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
def apply(): Seq[Column] = {
|
32
|
+
val userColumns = UserDimension(0)
|
33
|
+
val eventColumns = EventDimension(userColumns.size)
|
34
|
+
userColumns ++ eventColumns
|
35
|
+
}
|
36
|
+
|
37
|
+
def generate(startIndex: Int)(s: Seq[((Int) => Seq[Column])]): Seq[Column] = {
|
38
|
+
var index = startIndex
|
39
|
+
s.foldLeft[List[Column]](Nil) {
|
40
|
+
case (a, b) =>
|
41
|
+
val additionalColumn = b(index)
|
42
|
+
index = index + additionalColumn.size
|
43
|
+
a ++ additionalColumn
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro.column
|
2
|
+
|
3
|
+
import org.embulk.spi.{Column => EmbulkColumn}
|
4
|
+
import org.embulk.spi.`type`.Types
|
5
|
+
|
6
|
+
object EventDimension {
|
7
|
+
def apply(index: Int): Seq[Column] = {
|
8
|
+
Columns.generate(index) {
|
9
|
+
List(
|
10
|
+
Column
|
11
|
+
.getGeneral[org.embulk.parser.firebase_avro.define.root.Event_Dim],
|
12
|
+
(i: Int) =>
|
13
|
+
List(
|
14
|
+
Column("org.embulk.parser.firebase_avro.define.root.event_dim.params",
|
15
|
+
new EmbulkColumn(i, "event_dim.params", Types.JSON)))
|
16
|
+
)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
package org.embulk.parser.firebase_avro.column
|
2
|
+
|
3
|
+
import org.embulk.parser.firebase_avro.define.root.user_dim._
|
4
|
+
import org.embulk.spi.{Column => EmbulkColumn}
|
5
|
+
import org.embulk.spi.`type`.Types
|
6
|
+
|
7
|
+
object UserDimension {
|
8
|
+
def apply(index: Int): Seq[Column] = {
|
9
|
+
Columns.generate(index) {
|
10
|
+
List(
|
11
|
+
Column
|
12
|
+
.getGeneral[org.embulk.parser.firebase_avro.define.root.User_Dim],
|
13
|
+
(i: Int) =>
|
14
|
+
List(
|
15
|
+
Column("org.embulk.parser.firebase_avro.define.root.user_dim.user_properties",
|
16
|
+
new EmbulkColumn(i, "user_dim.user_property", Types.JSON))),
|
17
|
+
Column.getGeneral[Device_Info],
|
18
|
+
Column.getGeneral[Geo_Info],
|
19
|
+
Column.getGeneral[App_Info],
|
20
|
+
Column.getGeneral[Traffic_Source],
|
21
|
+
Column.getGeneral[Bundle_Info],
|
22
|
+
Column.getGeneral[Ltv_Info]
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
}
|
27
|
+
|
28
|
+
}
|