Goose 与 Python 之间的转换
本页说明将 Python 对象转换为 Goose 以及将 Goose 结果转换为 Python 的规则。
对象转换:Python 对象到 Goose
以下是 Python 对象类型到 Goose 逻辑类型 的映射:
None→NULLbool→BOOLEANdatetime.timedelta→INTERVALstr→VARCHARbytearray→BLOBmemoryview→BLOBdecimal.Decimal→DECIMAL/DOUBLEuuid.UUID→UUID
其余转换规则如下。
int
由于 Python 中的整数可以是任意精度,int 无法进行一一对应转换。
因此会按以下顺序尝试转换,直到成功为止:
BIGINTINTEGERUBIGINTUINTEGERDOUBLE
使用 Goose Value 类时,可以设置目标类型,这会影响转换结果。
float
会按顺序尝试以下转换,直到成功:
DOUBLEFLOAT
datetime.datetime
对于 datetime,如果可用会检查 pandas.isnull,若返回 true 则返回 NULL。
会与 datetime.datetime.min 和 datetime.datetime.max 比较,分别转换为 -inf 和 +inf。
如果 datetime 带有 tzinfo,则使用 TIMESTAMPTZ,否则使用 TIMESTAMP。
datetime.time
如果 time 带有 tzinfo,则使用 TIMETZ,否则使用 TIME。
datetime.date
date 会转换为 DATE 类型。
会与 datetime.date.min 和 datetime.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.ndarray 和 numpy.datetime64
ndarray 和 datetime64 会先调用 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
fetch_arrow_table()将数据作为 Arrow table 返回to_arrow_table()是fetch_arrow_table()的别名arrow()返回一个 Arrow record batch readerfetch_record_batch(chunk_size)返回一个 Arrow record batch reader,每个 batch 含chunk_size行
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