Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.node.NullNode
import com.fasterxml.jackson.databind.node.TextNode
import org.apache.arrow.vector.BigIntVector
import org.apache.arrow.vector.BitVector
import org.apache.arrow.vector.DateDayVector
import org.apache.arrow.vector.Float4Vector
import org.apache.arrow.vector.Float8Vector
import org.apache.arrow.vector.IntVector
Expand Down Expand Up @@ -188,6 +189,7 @@ private fun VectorSchemaRoot.fieldValueAsJsonNode(
is Float8Vector -> DoubleNode(vector.get(rowIndex))
is Float4Vector -> FloatNode(vector.get(rowIndex))
is BitVector -> BooleanNode.valueOf(vector.get(rowIndex) != 0)
is DateDayVector -> TextNode(java.time.LocalDate.ofEpochDay(vector.get(rowIndex).toLong()).toString())
Comment thread
fengelniederhammer marked this conversation as resolved.
Comment thread
fengelniederhammer marked this conversation as resolved.
else -> TextNode(vector.getObject(rowIndex)?.toString() ?: "")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ package org.genspectrum.lapis.silo
import org.apache.arrow.memory.RootAllocator
import org.apache.arrow.vector.BigIntVector
import org.apache.arrow.vector.BitVector
import org.apache.arrow.vector.DateDayVector
import org.apache.arrow.vector.Float8Vector
import org.apache.arrow.vector.IntVector
import org.apache.arrow.vector.VarCharVector
import org.apache.arrow.vector.VectorSchemaRoot
import org.apache.arrow.vector.ipc.ArrowStreamWriter
import org.apache.arrow.vector.types.DateUnit
import org.apache.arrow.vector.types.pojo.ArrowType
import org.apache.arrow.vector.types.pojo.Field
import org.apache.arrow.vector.types.pojo.FieldType
import org.apache.arrow.vector.types.pojo.Schema
import java.io.ByteArrayOutputStream
import java.nio.channels.Channels
import java.time.LocalDate

/**
* Builds an Arrow IPC stream byte array from [rows] using the given builder.
Expand Down Expand Up @@ -51,6 +54,8 @@ fun buildArrowIpcStream(rows: List<Map<String, Any?>>): ByteArray {

is Boolean -> Field(name, FieldType.nullable(ArrowType.Bool()), null)

is LocalDate -> Field(name, FieldType.nullable(ArrowType.Date(DateUnit.DAY)), null)

// Note: null values (and any unrecognized types) are typed as Utf8.
// If the first row has null for a column that later rows fill with a typed value (e.g. Int, Long),
// the column will be inferred as Utf8 and the converter will throw a cast error at runtime.
Expand Down Expand Up @@ -101,6 +106,14 @@ fun buildArrowIpcStream(rows: List<Map<String, Any?>>): ByteArray {
}
}

is DateDayVector -> {
if (value == null) {
vector.setNull(rowIdx)
} else {
vector.set(rowIdx, (value as LocalDate).toEpochDay().toInt())
}
}

is VarCharVector -> {
if (value == null) {
vector.setNull(rowIdx)
Expand Down
31 changes: 31 additions & 0 deletions lapis/src/test/kotlin/org/genspectrum/lapis/silo/SiloClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import org.mockserver.model.MediaType
import org.mockserver.model.MediaType.parse
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import java.time.LocalDate

private const val MOCK_SERVER_PORT = 1080

Expand Down Expand Up @@ -295,6 +296,36 @@ class SiloClientTest(
)
}

@Test
fun `GIVEN server returns date32 column THEN dates are converted to ISO-8601 strings and nulls remain null`() {
expectQueryRequestAndRespondWith(
response()
.withContentType(parse(ARROW_STREAM_MEDIA_TYPE))
.withBody(
buildArrowIpcStream(
listOf(
mapOf("date" to LocalDate.of(2021, 2, 23), "label" to "A"),
mapOf("date" to LocalDate.of(2021, 3, 19), "label" to "B"),
mapOf("date" to null, "label" to "C"),
),
),
),
)

val query = SiloQuery(SiloAction.details(), StringEquals("theColumn", "theValue"))
val result = sendQuery(query)

assertThat(result, hasSize(3))
assertThat(
result,
containsInAnyOrder(
DetailsData(mapOf("date" to TextNode("2021-02-23"), "label" to TextNode("A"))),
DetailsData(mapOf("date" to TextNode("2021-03-19"), "label" to TextNode("B"))),
DetailsData(mapOf("date" to NullNode.instance, "label" to TextNode("C"))),
),
)
}

@Test
fun `GIVEN server returns most recent common ancestor response THEN response can be deserialized`() {
expectQueryRequestAndRespondWith(
Expand Down