diff --git a/build.gradle.kts b/build.gradle.kts index d50d8a37fc..01aedf47b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -138,7 +138,8 @@ configure { dependencies { api(project(":elasticmagic-serde")) api(project(":elasticmagic-transport")) - implementation(Libs.kotlinCoroutines("core")) + implementation(Libs.kotlinxCoroutines("core")) + api(Libs.kotlinxDatetime()) } } } diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index 43a14d3c6b..7fc3fd2b38 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -17,6 +17,7 @@ object Versions { val kotlin = versionProps["kotlin"]!!.toString() const val kotlinxSerialization = "1.1.0" const val kotlinxCoroutines = "1.4.3-native-mt" + const val kotlinxDatetime = "0.2.1" const val ktor = "1.5.2" // Other dependencies @@ -39,12 +40,20 @@ object Libs { return "io.kotest:kotest-$flavor:${Versions.kotest}" } - fun kotlinSerialization(flavor: String): String { - return "org.jetbrains.kotlinx:kotlinx-serialization-$flavor:${Versions.kotlinxSerialization}" + fun kotlinxLib(lib: String, version: String): String { + return "org.jetbrains.kotlinx:kotlinx-$lib:$version" } - fun kotlinCoroutines(flavor: String): String { - return "org.jetbrains.kotlinx:kotlinx-coroutines-$flavor:${Versions.kotlinxCoroutines}" + fun kotlinxSerialization(flavor: String): String { + return kotlinxLib("serialization-$flavor", Versions.kotlinxSerialization) + } + + fun kotlinxCoroutines(flavor: String): String { + return kotlinxLib("coroutines-$flavor", Versions.kotlinxCoroutines) + } + + fun kotlinxDatetime(): String { + return kotlinxLib("datetime", Versions.kotlinxDatetime) } fun ktorClient(flavor: String): String { diff --git a/integ-tests/build.gradle.kts b/integ-tests/build.gradle.kts index 15462b858d..370babe753 100644 --- a/integ-tests/build.gradle.kts +++ b/integ-tests/build.gradle.kts @@ -8,7 +8,7 @@ kotlin { api(project(":elasticmagic-serde-serialization-json")) api(project(":elasticmagic-transport-ktor")) - implementation(Libs.kotlinCoroutines("core")) + implementation(Libs.kotlinxCoroutines("core")) implementation(Libs.ktorClient("core")) } } diff --git a/serde-serialization-json/build.gradle.kts b/serde-serialization-json/build.gradle.kts index cc49797242..ce61fcc99d 100644 --- a/serde-serialization-json/build.gradle.kts +++ b/serde-serialization-json/build.gradle.kts @@ -5,7 +5,7 @@ kotlin { val commonMain by getting { dependencies { api(project(":elasticmagic-serde")) - api(Libs.kotlinSerialization("json")) + api(Libs.kotlinxSerialization("json")) } } val commonTest by getting { diff --git a/src/commonMain/kotlin/dev/evo/elasticmagic/Document.kt b/src/commonMain/kotlin/dev/evo/elasticmagic/Document.kt index eb175275ec..a01755a541 100644 --- a/src/commonMain/kotlin/dev/evo/elasticmagic/Document.kt +++ b/src/commonMain/kotlin/dev/evo/elasticmagic/Document.kt @@ -1,5 +1,6 @@ package dev.evo.elasticmagic +import kotlinx.datetime.LocalDateTime import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -253,6 +254,21 @@ abstract class FieldSet : Named { params = params, ) } + fun date( + name: String? = null, + docValues: Boolean? = null, + index: Boolean? = null, + store: Boolean? = null, + params: Params? = null, + ): Field { + return field( + name, DateType, + docValues = docValues, + index = index, + store = store, + params = params, + ) + } fun keyword( name: String? = null, normalizer: String? = null, diff --git a/src/commonMain/kotlin/dev/evo/elasticmagic/FieldType.kt b/src/commonMain/kotlin/dev/evo/elasticmagic/FieldType.kt index 140432171d..55199d0d2c 100644 --- a/src/commonMain/kotlin/dev/evo/elasticmagic/FieldType.kt +++ b/src/commonMain/kotlin/dev/evo/elasticmagic/FieldType.kt @@ -1,5 +1,11 @@ package dev.evo.elasticmagic +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toInstant +import kotlinx.datetime.toLocalDateTime + class ValueDeserializationException(value: Any, type: String) : IllegalArgumentException("Cannot deserialize $value to $type") @@ -90,6 +96,21 @@ object TextType : StringType() { override val name = "text" } +object DateType : FieldType { + override val name = "date" + + override fun serialize(v: LocalDateTime): Any { + return v.toInstant(TimeZone.UTC).toString() + } + + override fun deserialize(v: Any, valueFactory: (() -> LocalDateTime)?) = when (v) { + is Int -> Instant.fromEpochMilliseconds(v.toLong()).toLocalDateTime(TimeZone.UTC) + is Long -> Instant.fromEpochMilliseconds(v).toLocalDateTime(TimeZone.UTC) + is String -> Instant.parse(v).toLocalDateTime(TimeZone.UTC) + else -> throw ValueDeserializationException(v, "LocalDateTime") + } +} + data class Join( val name: String, val parent: String? = null, diff --git a/src/commonTest/kotlin/dev/evo/elasticmagic/FieldTypeTests.kt b/src/commonTest/kotlin/dev/evo/elasticmagic/FieldTypeTests.kt new file mode 100644 index 0000000000..7a73e6b142 --- /dev/null +++ b/src/commonTest/kotlin/dev/evo/elasticmagic/FieldTypeTests.kt @@ -0,0 +1,22 @@ +package dev.evo.elasticmagic + +import io.kotest.matchers.shouldBe +import kotlinx.datetime.LocalDateTime + +import kotlin.test.Test + +class FieldTypeTests { + @Test + fun dateSerialize() { + DateType.serialize(LocalDateTime(2015, 1, 1, 12, 10, 30)) shouldBe "2015-01-01T12:10:30Z" + DateType.serialize(LocalDateTime(2015, 1, 1, 12, 10, 30, 1_000_000)) shouldBe "2015-01-01T12:10:30.001Z" + } + + @Test + fun dateDeserialize() { + DateType.deserialize(0) shouldBe LocalDateTime(1970, 1, 1, 0, 0, 0) + DateType.deserialize(1618249875_000) shouldBe LocalDateTime(2021, 4, 12, 17, 51, 15) + DateType.deserialize("2015-01-01T12:10:30Z") shouldBe LocalDateTime(2015, 1, 1, 12, 10, 30) + DateType.deserialize("2015-01-01T12:10:30.001Z") shouldBe LocalDateTime(2015, 1, 1, 12, 10, 30, 1_000_000) + } +} diff --git a/src/commonTest/kotlin/dev/evo/elasticmagic/compile/MappingCompilerTests.kt b/src/commonTest/kotlin/dev/evo/elasticmagic/compile/MappingCompilerTests.kt index b7cd272244..ae74e9727d 100644 --- a/src/commonTest/kotlin/dev/evo/elasticmagic/compile/MappingCompilerTests.kt +++ b/src/commonTest/kotlin/dev/evo/elasticmagic/compile/MappingCompilerTests.kt @@ -139,7 +139,7 @@ class MappingCompilerTests { fun testRuntimeFields() { val logDoc = object : Document() { // TODO: Replace with date field - val timestamp by keyword("@timestamp") + val timestamp by date("@timestamp") override val runtime = object : RuntimeFields() { val dayOfWeek by runtime( @@ -182,7 +182,7 @@ class MappingCompilerTests { ), "properties" to mapOf( "@timestamp" to mapOf( - "type" to "keyword", + "type" to "date", ), ) ) diff --git a/transport-ktor/build.gradle.kts b/transport-ktor/build.gradle.kts index 021be47f78..6d3a00ebfb 100644 --- a/transport-ktor/build.gradle.kts +++ b/transport-ktor/build.gradle.kts @@ -12,7 +12,7 @@ kotlin { val commonTest by getting { dependencies { implementation(Libs.ktorClient("mock")) - implementation(Libs.kotlinSerialization("json")) + implementation(Libs.kotlinxSerialization("json")) } } } diff --git a/transport/build.gradle.kts b/transport/build.gradle.kts index 18339acee3..982615f79e 100644 --- a/transport/build.gradle.kts +++ b/transport/build.gradle.kts @@ -11,7 +11,7 @@ kotlin { getByName("commonTest") { dependencies { api(project(":elasticmagic-serde-serialization-json")) - api(Libs.kotlinSerialization("json")) + api(Libs.kotlinxSerialization("json")) } } }