Преобразование выходных данных Athena в строго типизированную модель в Go

# #go #amazon-athena

Вопрос:

Есть ли простой способ преобразовать структуру из athena.GetQueryResultsOutput структуры в определяемую пользователем структуру в Go?

Вот фрагмент того, чего я хочу достичь:

 const testSql = `
select
    id,
    name,
    count(source_id) as aggregate_count,
    array_agg(source_id) as aggregate_source_ids,
    array_agg(source_name) as aggregate_source_names
from my_glue_catalog_table
group by id, name
`

type myModel struct {
    id                   int64
    name                 string
    aggregateCount       int
    aggregateSourceIDs   []int64
    aggregateSourceNames []string
}

queryResultOutput, err = awsAthenaClient.GetQueryResults(ctx, amp;queryResultInput)
// var mapped []myModel = mapper.FromGetQueryResultsOutput(queryResultOutput.ResultSet)
 

И вот структура queryResultOutput.ResultSet :

 ResultSet: {
    ResultSetMetadata: {
      ColumnInfo: [
        {
          CaseSensitive: false,
          CatalogName: "hive",
          Label: "id",
          Name: "id",
          Nullable: "UNKNOWN",
          Precision: 10,
          Scale: 0,
          SchemaName: "",
          TableName: "",
          Type: "integer"
        },
        {
          CaseSensitive: true,
          CatalogName: "hive",
          Label: "name",
          Name: "name",
          Nullable: "UNKNOWN",
          Precision: 2147483647,
          Scale: 0,
          SchemaName: "",
          TableName: "",
          Type: "varchar"
        },
        {
          CaseSensitive: false,
          CatalogName: "hive",
          Label: "aggregate_count",
          Name: "aggregate_count",
          Nullable: "UNKNOWN",
          Precision: 19,
          Scale: 0,
          SchemaName: "",
          TableName: "",
          Type: "bigint"
        },
        {
          CaseSensitive: false,
          CatalogName: "hive",
          Label: "aggregate_source_ids",
          Name: "aggregate_source_ids",
          Nullable: "UNKNOWN",
          Precision: 0,
          Scale: 0,
          SchemaName: "",
          TableName: "",
          Type: "array"
        },
        {
          CaseSensitive: false,
          CatalogName: "hive",
          Label: "aggregate_source_names",
          Name: "aggregate_source_names",
          Nullable: "UNKNOWN",
          Precision: 0,
          Scale: 0,
          SchemaName: "",
          TableName: "",
          Type: "array"
        }
      ]
    },
    Rows: [{
        // first row data (from page 1 of results) is header, we can ignore this
        Data: [
            VarCharValue: "id" },
            VarCharValue: "name" },
            VarCharValue: "aggregate_count" },
            VarCharValue: "aggregate_source_ids" },
            VarCharValue: "aggregate_source_names" }
        ]
      },
      // all subsequent rows are data values:
      {
        Data: [
            VarCharValue: "920000" },
            VarCharValue: "mydata1" },
            VarCharValue: "2" },
            VarCharValue: "[52800, 113000]" },
            VarCharValue: "[sourcedata1, sourcedata2]" }
        ]
      }
    ]
}
 

Полный пример кода, который я написал: суть

Если нет лучшей альтернативы, я подумываю написать пакет конвертеров, подобный следующему. Основным предостережением будет надежное преобразование массивов, хотя одно из строковых значений в массиве может содержать запятую. И я считаю, что мы можем вернуться только []interface{} потому, что Go не поддерживает дженерики (на сегодняшний день)?

 // myModel defines a schema that corresponds with testSql above
type myModel struct {
    id                   int      `athenaconv:"id"`
    name                 string   `athenaconv:"name"`
    aggregateCount       int64    `athenaconv:"aggregate_count"`
    aggregateSourceIDs   []int64  `athenaconv:"aggregate_source_ids"`
    aggregateSourceNames []string `athenaconv:"aggregate_source_names"`
}
mapper := athenaconv.MapperFor(reflect.TypeOf(myModel))
var mapped []interface{} = mapper.FromAthenaResultSet(queryResultOutput.ResultSet)
 

Комментарии:

1. пожалуйста, перепишите, чтобы сначала представить API Athena, текущий результат, который вы получаете, желаемый результат, а затем описать сложность разрыва.

2. @mh-cbon Я думаю, я надеялся на более простую альтернативу для преобразования набора результатов Athena, который в основном представляет собой 2D-массив строк отдельные метаданные, в строго типизированную модель в golang. В итоге я создал свой собственный пакет, чтобы добиться этого с помощью отражения.

Ответ №1:

Поскольку я не смог найти лучшей альтернативы и работаю с большим количеством наборов данных для анализа, я написал свою собственную библиотеку пакетов, чтобы выяснить сопоставление столбцов и преобразовать типы athena в типы данных go. Это поможет нам привязать афину ResultSet к фрагменту строго типизированной модели.

Я сделал его с открытым исходным кодом под athenaconv. Оставляю в качестве ответа на случай, если это поможет кому-то другому.

Иди и возьми его

 go get github.com/kent-id/athenaconv
 

Использование

 mapper, err := athenaconv.NewMapperFor(reflect.TypeOf(MyModel{}))
if err != nil {
    handleError(err)
}

var mapped []interface{}
mapped, err = mapper.FromAthenaResultSetV2(ctx, queryResultOutput.ResultSet)
if err != nil {
    handleError(err)
}
for _, mappedItem := range mapped {
    mappedItemModel := mappedItem.(*MyModel)
    fmt.Printf("% vn", *mappedItemModel)
}
 

Комментарии:

1. избегайте бесполезного распределения github.com/kent-id/athenaconv/blob/main/mapper.go#L59 на и используйте длину, доступную из среза строк github.com/aws/aws-sdk-go-v2/blob/main/service/athena/types/…

2. @mh-cbon — да, вы правы, я удалю бесполезное выделение. Если у вас есть дополнительные отзывы, пожалуйста, дайте мне знать либо через GitHub предложение по выпуску/DM. Я на ранней стадии погружения в мир голанга, поэтому очень ценю ваши отзывы 🙂