跳到主要内容

Goose 与 Python 之间的转换

本页说明将 Python 对象转换为 Goose 以及将 Goose 结果转换为 Python 的规则。

对象转换:Python 对象到 Goose

以下是 Python 对象类型到 Goose 逻辑类型 的映射:

  • NoneNULL
  • boolBOOLEAN
  • datetime.timedeltaINTERVAL
  • strVARCHAR
  • bytearrayBLOB
  • memoryviewBLOB
  • decimal.DecimalDECIMAL / DOUBLE
  • uuid.UUIDUUID

其余转换规则如下。

int

由于 Python 中的整数可以是任意精度,int 无法进行一一对应转换。 因此会按以下顺序尝试转换,直到成功为止:

  • BIGINT
  • INTEGER
  • UBIGINT
  • UINTEGER
  • DOUBLE

使用 Goose Value 类时,可以设置目标类型,这会影响转换结果。

float

会按顺序尝试以下转换,直到成功:

  • DOUBLE
  • FLOAT

datetime.datetime

对于 datetime,如果可用会检查 pandas.isnull,若返回 true 则返回 NULL。 会与 datetime.datetime.mindatetime.datetime.max 比较,分别转换为 -inf+inf

如果 datetime 带有 tzinfo,则使用 TIMESTAMPTZ,否则使用 TIMESTAMP

datetime.time

如果 time 带有 tzinfo,则使用 TIMETZ,否则使用 TIME

datetime.date

date 会转换为 DATE 类型。 会与 datetime.date.mindatetime.date.max 比较,分别转换为 -inf+inf

bytes

bytes 默认转换为 BLOB;当它用于构造类型为 BITSTRING 的 Value 对象时,会改为映射到 BITSTRING

list

list 会变为其子元素中“最宽松”类型对应的 LIST,例如:

my_list_value = [
12345,
"test"
]

会变成 VARCHAR[],因为 12345 可以转换为 VARCHAR,但 test 不能转换为 INTEGER

[12345, test]

dict

dict 对象可根据其结构转换为 STRUCT(...)MAP(..., ...)。 如果 dict 的结构类似于:

import goose

my_map_dict = {
"key": [
1, 2, 3
],
"value": [
"one", "two", "three"
]
}

goose.values(my_map_dict)

则会将两组列表 zip 后生成键值对,并转换为 MAP。 上面的示例会变为 MAP(INTEGER, VARCHAR)

┌─────────────────────────┐
│ {1=one, 2=two, 3=three} │
│ map(integer, varchar) │
├─────────────────────────┤
│ {1=one, 2=two, 3=three} │
└─────────────────────────┘

如果 dict 由 function 返回, 该函数会返回 MAP,因此必须指定函数的 return_type。如果提供 无法转换为 MAP 的返回类型,会抛出错误:

import goose
goose_conn = goose.connect()

def get_map() -> dict[str,list[str]|list[int]]:
return {
"key": [
1, 2, 3
],
"value": [
"one", "two", "three"
]
}

goose_conn.create_function("get_map", get_map, return_type=dict[int, str])

goose_conn.sql("select get_map()").show()

goose_conn.create_function("get_map_error", get_map)

goose_conn.sql("select get_map_error()").show()
┌─────────────────────────┐
│ get_map() │
│ map(bigint, varchar) │
├─────────────────────────┤
│ {1=one, 2=two, 3=three} │
└─────────────────────────┘

ConversionException: Conversion Error: Type VARCHAR can't be cast as UNION(u1 VARCHAR[], u2 BIGINT[]). VARCHAR can't be implicitly cast to any of the union member types: VARCHAR[], BIGINT[]

字段名称很重要,且两个列表必须长度一致。

否则会尝试将其转换为 STRUCT

import goose

my_struct_dict = {
1: "one",
"2": 2,
"three": [1, 2, 3],
False: True
}

goose.values(my_struct_dict)

结果为:

┌────────────────────────────────────────────────────────────────────┐
│ {'1': 'one', '2': 2, 'three': [1, 2, 3], 'False': true} │
│ struct("1" varchar, "2" integer, three integer[], "false" boolean) │
├────────────────────────────────────────────────────────────────────┤
│ {'1': one, '2': 2, 'three': [1, 2, 3], 'False': true} │
└────────────────────────────────────────────────────────────────────┘

如果 dict 由 function 返回, 由于自动转换,函数会返回 MAP。 若要返回 STRUCT,需要提供 return_type

import goose
from goose.sqltypes import BOOLEAN, INTEGER, VARCHAR
from goose import list_type, struct_type

goose_conn = goose.connect()

my_struct_dict = {
1: "one",
"2": 2,
"three": [1, 2, 3],
False: True
}

def get_struct() -> dict[str|int|bool,str|int|list[int]|bool]:
return my_struct_dict

goose_conn.create_function("get_struct_as_map", get_struct)

goose_conn.sql("select get_struct_as_map()").show()

goose_conn.create_function("get_struct", get_struct, return_type=struct_type({
1: VARCHAR,
"2": INTEGER,
"three": list_type(INTEGER),
False: BOOLEAN
}))

goose_conn.sql("select get_struct()").show()
┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ get_struct_as_map() │
│ map(union(u1 varchar, u2 bigint, u3 boolean), union(u1 varchar, u2 bigint, u3 bigint[], u4 boolean)) │
├──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ {1=one, 2=2, three=[1, 2, 3], false=true} │
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────┐
│ get_struct() │
│ struct("1" varchar, "2" integer, three integer[], "false" boolean) │
├────────────────────────────────────────────────────────────────────┤
│ {'1': one, '2': 2, 'three': [1, 2, 3], 'False': true} │
└────────────────────────────────────────────────────────────────────┘

字典中的每个 key 都会转换为字符串。

tuple

tuple 默认转换为 LIST;当它用于构造类型为 STRUCT 的 Value 对象时,会改为转换为 STRUCT

numpy.ndarraynumpy.datetime64

ndarraydatetime64 会先调用 tolist(),再对其结果进行转换。

结果转换:Goose 结果到 Python

Goose 的 Python 客户端提供了多个可高效获取数据的附加方法。

NumPy

  • fetchnumpy() 将数据作为 NumPy 数组字典返回

Pandas

  • df() 将数据作为 Pandas DataFrame 返回
  • fetchdf()df() 的别名
  • fetch_df()df() 的别名
  • fetch_df_chunk(vector_multiple) 将部分结果读取到 DataFrame 中。每个分块返回的行数为向量大小(默认 2048)* vector_multiple(默认 1)。

Apache Arrow

Polars

  • pl() 将数据作为 Polars DataFrame 返回

示例

下面是一些使用该功能的示例。更多示例请参见 Python guides

以 Pandas DataFrame 形式获取:

df = con.execute("SELECT * FROM items").fetchdf()
print(df)
       item   value  count
0 jeans 20.0 1
1 hammer 42.2 2
2 laptop 2000.0 1
3 chainsaw 500.0 10
4 iphone 300.0 2

以 NumPy 数组字典形式获取:

arr = con.execute("SELECT * FROM items").fetchnumpy()
print(arr)
{'item': masked_array(data=['jeans', 'hammer', 'laptop', 'chainsaw', 'iphone'],
mask=[False, False, False, False, False],
fill_value='?',
dtype=object), 'value': masked_array(data=[20.0, 42.2, 2000.0, 500.0, 300.0],
mask=[False, False, False, False, False],
fill_value=1e+20), 'count': masked_array(data=[1, 2, 1, 10, 2],
mask=[False, False, False, False, False],
fill_value=999999,
dtype=int32)}

以 Arrow table 形式获取。随后转换为 Pandas 仅用于美观打印:

tbl = con.execute("SELECT * FROM items").fetch_arrow_table()
print(tbl.to_pandas())
       item    value  count
0 jeans 20.00 1
1 hammer 42.20 2
2 laptop 2000.00 1
3 chainsaw 500.00 10
4 iphone 300.00 2