我是Clojure的新手,我正试图通过Java互操作与BigQuery (BQ)合作。我在BQ中使用的数据集可以在这里找到(WARNING:链接到CSV文件)。我有以下代码:
(ns bq
(:import (com.google.cloud.bigquery BigQueryOptions QueryJobConfiguration BigQuery BigQueryException BigQuery$JobOption)))
(defn querybq [query]
(let [^BigQuery big-query (.getService (BigQueryOptions/getDefaultInstance))
^QueryJobConfiguration query-config (.build (QueryJobConfiguration/newBuilder query))
results (.query big-query query-config (into-array BigQuery$JobOption []))]
results))
;; Dataset location: https://msi.nga.mil/api/publications/download?type=view&key=16920959/SFH00000/UpdatedPub150.csv
(def res (querybq (str "SELECT * FROM xxxxxxxxxx.wpi_dataset.wpi_table LIMIT 10")))
(defn print-res [args]
(doseq [x (.iterateAll res)]
(let [^String y (.getStringValue (.get x 0))]
(println y))))
首先,我查询BigQuery并将结果赋值给res
。然后,我调用print-res
,它打印出第一列数据。我有两个问题。
我使用
.getStringValue
从第一列获得值,但这一列不一定包含字符串。(事实上,它没有——它包含数字。)如何自动将数据提取为正确的类型?这只打印第一列,但我想引用所有列。如果我知道
doseq
和range
有多少列,我相信我可以这样做。我能弄清楚有多少列有没有第二次查询数据库(即,直接从res
)?
背景:当我在这里打印输出时,最终我试图提取数据,以便我可以将其放入tech.ml.dataset
的数据帧中。
deps.edn
{:deps
{com.google.cloud/google-cloud-bigquery {:mvn/version "2.24.4"}}}
res
内容示例
#object[com.google.cloud.bigquery.TableResult 0x63d5874f TableResult{rows=[[FieldValue{attribute=PRIMITIVE, value=12500.0}, FieldValue{attribute=PRIMITIVE, value=B
595 razil NE Coast -- 12470}, FieldValue{attribute=PRIMITIVE, value=Cameta}, FieldValue{attribute=PRIMITIVE, value= }, FieldValue{attribute=PRIMITIVE, value=BR CMA}, F
596 ieldValue{attribute=PRIMITIVE, value=Brazil}, FieldValue{attribute=PRIMITIVE, value=South Atlantic Ocean}, FieldValue{attribute=PRIMITIVE, value= }, FieldValue{att
597 ribute=PRIMITIVE, value=Sailing Directions Pub. 124 (Enroute) - East Coast of South America}, FieldValue{attribute=PRIMITIVE, value=https://msi.geo.nga.mil/api/pub
598 lications/download?key=16694491/SFH00000/Pub124bk.pdf&type=view}, FieldValue{attribute=PRIMITIVE, value= }, FieldValue{attribute=PRIMITIVE, value= }, FieldValue{at
599 tribute=PRIMITIVE, value= }, FieldValue{attribute=PRIMITIVE, value=gen01b}, FieldValue{attribute=PRIMITIVE, value=3.0}, FieldValue{attribute=PRIMITIVE, value=0.0},
600 FieldValue{attribute=PRIMITIVE, value=7.9}, FieldValue{attribute=PRIMITIVE, value=14.0}, FieldValue{attribute=PRIMITIVE, value=0.0}, FieldValue{attribute=PRIMITIV
601 E, value=0.0}, FieldValue{attribute=PRIMITIVE, value=0.0}, FieldValue{attribute=PRIMITIVE, value=0.0}, FieldValue{attribute=PRIMITIVE, value=0.0}...
回答我自己的问题:
- 还有一个
getValue()
函数将字段作为对象返回。 - 我可以使用
size()
函数从模式(或从单个行)获得列数。
对于那些纠结于如何在Clojure中处理BQ查询结果的人来说,这是我想到的(在问题中的代码之上)。
有三个函数实际完成以下工作:1)解析出一行的每个字段(parse-row
), 2)从模式中提取列名(column-names
),以及3)将前两个函数应用于所有行/列,将结果修补在一起,并将结果转换为Tablecloth数据集(bq-to-dataset
)。
(defn parse-row [row]
"Split each FieldValueList into its components"
(let [indices (range (.size row))]
(map #(.getValue (.get row %1)), indices)))
(defn column-names [table-result]
"Get column names from TableResult"
(let [field-list (.getFields (.getSchema table-result))
list-length (.size field-list)
list-indices (range list-length)]
(map #(.getName (.get field-list %1)) list-indices)))
(defn bq-to-dataset [table-result]
"Convert a TableResult into a TMD dataset"
(let [cols (map keyword (column-names table-result))]
(->> table-result
(.iterateAll)
(map parse-row)
(map #(zipmap (partial cols) %1))
(tc/dataset))))
为了演示这一点,我们可以提取一些列,然后打印它们。
(defn -main [& args]
(-> (bq-to-dataset res)
(tc/select-columns [:MainPortName :RegionName :WorldPortIndexNumber])
(print)))
| :MainPortName | :RegionName | :WorldPortIndexNumber |
|---------------|-----------------------------|-----------------------|
| Deering | Alaska Continued -- 20185 | 20430.0 |
| Manistee | US Lake Michigan -- 4570 | 4650.0 |
| Great Lakes | US Lake Michigan -- 4570 | 4820.0 |
| Oconto | US Lake Michigan -- 4570 | 5010.0 |
| Blind River | Canada Lake Huron -- 3640 | 4200.0 |
| Presque Isle | US Lake Superior -- 5230 | 5290.0 |
| Marquette | US Lake Superior -- 5230 | 5280.0 |
| Wilmette | US Lake Michigan -- 4570 | 4810.0 |
| Oakville | Canada Lake Ontario -- 2820 | 3020.0 |
| Belleville | Canada Lake Ontario -- 2820 | 2930.0 |