簡體   English   中英

Dataflow GCS 到 BigQuery - 如何為每個輸入輸出多行?

[英]Dataflow GCS to BigQuery - How to output multiple rows per input?

目前我正在使用谷歌提供的 gcs-text-to-bigquery 模板並輸入轉換函數來轉換我的 jsonl 文件。 jsonl 是非常嵌套的,我希望能夠通過進行一些轉換來為換行符分隔的 json 的每一行輸出多行。

例如:

{'state': 'FL', 'metropolitan_counties':[{'name': 'miami dade', 'population':100000}, {'name': 'county2', 'population':100000}…], 'rural_counties':{'name': 'county1', 'population':100000}, {'name': 'county2', 'population':100000}….{}], 'total_state_pop':10000000,….}

顯然,縣的數量會多於 2 個,並且每個州都會有一條這樣的線。 我老板想要的輸出是:

輸出

當我進行 gcs-to-bq 文本轉換時,我最終每個州只得到一行(所以我將從佛羅里達州得到 miamidade 縣,然后是下一個州的第一個縣在我的轉換中)。 我讀了一點,我認為這是因為模板中的映射期望每個 jsonline 有一個輸出。 似乎我可以做一個 pardo(DoFn ?) 不知道那是什么,或者在 python 中有一個與 beam.Map 類似的選項。 轉換中有一些業務邏輯(現在大約有 25 行代碼,因為 json 的列比我顯示的多,但這些非常簡單)。

對此有何建議? 今晚/明天有數據進來,一個BQ表會有幾十萬行。

我正在使用的模板目前是在 java 中,但我可以很容易地將它轉換為 python,因為 python 中有很多在線示例。 我更了解 python 並且我認為它更容易考慮到不同的類型(有時一個字段可以為空)並且考慮到我看到的示例看起來更簡單,它似乎不那么令人生畏,但是,對任何一個都開放

在 Python 中解決這個問題有點簡單。 這是一種可能性(未完全測試):

from __future__ import absolute_import                                                               

import ast                                                                      

import apache_beam as beam                                                      
from apache_beam.io import ReadFromText                                            
from apache_beam.io import WriteToText                                             

from apache_beam.options.pipeline_options import PipelineOptions                   

import os                                                                       
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = '/path/to/service_account.json'      

pipeline_args = [                                                                  
    '--job_name=test'                                                              
]                                                                                  

pipeline_options = PipelineOptions(pipeline_args)                                  


def jsonify(element):                                                              
    return ast.literal_eval(element)                                               


def unnest(element):                                                            
    state = element.get('state')                                                
    state_pop = element.get('total_state_pop')                                  
    if state is None or state_pop is None:                                                   
        return                                                                  
    for type_ in ['metropolitan_counties', 'rural_counties']:                   
        for e in element.get(type_, []):                                        
            name = e.get('name')                                                
            pop = e.get('population')                                           
            county_type = (                                                     
                'Metropolitan' if type_ == 'metropolitan_counties' else 'Rural' 
            )                                                                   
            if name is None or pop is None:                                     
                continue                                                        
            yield {                                                             
                'State': state,                                                 
                'County_Type': county_type,                                     
                'County_Name': name,                                            
                'County_Pop': pop,                                              
                'State_Pop': state_pop                                          
            }

with beam.Pipeline(options=pipeline_options) as p:                              
    lines = p | ReadFromText('gs://url to file')                                        

    schema = 'State:STRING,County_Type:STRING,County_Name:STRING,County_Pop:INTEGER,State_Pop:INTEGER'                                                                      

    data = (                                                                    
        lines                                                                   
        | 'Jsonify' >> beam.Map(jsonify)                                        
        | 'Unnest' >> beam.FlatMap(unnest)                                      
        | 'Write to BQ' >> beam.io.Write(beam.io.BigQuerySink(                  
            'project_id:dataset_id.table_name', schema=schema,                     
            create_disposition=beam.io.BigQueryDisposition.CREATE_IF_NEEDED,    
            write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND)       
        )                                                                       
    )

這只會在您處理批處理數據時成功。 如果您有流數據,那么只需將beam.io.Write(beam.io.BigquerySink(...))更改為beam.io.WriteToBigQuery

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM