使用Python SDK的数据流:将PubSub消息转换为BigQuery输出

使用Python SDK的数据流:将PubSub消息转换为BigQuery输出,python,google-bigquery,google-cloud-dataflow,apache-beam,dataflow,Python,Google Bigquery,Google Cloud Dataflow,Apache Beam,Dataflow,我试图使用dataflow来读取pubsub消息并将其写入大查询。我被谷歌团队授予了alpha访问权限,并且已经获得了提供的示例,但是现在我需要将其应用到我的场景中 子有效载荷: Message { data: {'datetime': '2017-07-13T21:15:02Z', 'mac': 'FC:FC:48:AE:F6:94', 'status': 1} attributes: {} } 大查询架构: schema='mac:STRING, status:INTEGE

我试图使用dataflow来读取pubsub消息并将其写入大查询。我被谷歌团队授予了alpha访问权限,并且已经获得了提供的示例,但是现在我需要将其应用到我的场景中

子有效载荷:

Message {
    data: {'datetime': '2017-07-13T21:15:02Z', 'mac': 'FC:FC:48:AE:F6:94', 'status': 1}
    attributes: {}
}
大查询架构:

schema='mac:STRING, status:INTEGER, datetime:TIMESTAMP',
我的目标是简单地读取消息负载并插入到bigquery中。我正在绞尽脑汁研究转换,以及如何将键/值映射到大查询模式

我对这一点很陌生,所以非常感谢您的帮助

当前代码:


谢谢

写入Python SDK的BigQuery接收器的数据应采用字典的形式,其中字典的每个键给出BigQuery表的一个字段,相应的值给出要写入该字段的值。对于BigQuery记录类型,值本身应该是具有相应键、值对的字典


我提交了一个JIRA来改进Beam中相应python模块的文档:

我能够通过定义一个函数将pubsub字符串加载到json对象中来成功解析它(请参见parse_pubsub())。我遇到的一个奇怪问题是,我无法在全局范围内导入json。我收到了“NameError:global name'json'未定义”错误。我必须在函数中导入json

请参见下面我的工作代码:

from __future__ import absolute_import

import logging
import argparse
import apache_beam as beam
import apache_beam.transforms.window as window

'''Normalize pubsub string to json object'''
# Lines look like this:
  # {'datetime': '2017-07-13T21:15:02Z', 'mac': 'FC:FC:48:AE:F6:94', 'status': 1}
def parse_pubsub(line):
    import json
    record = json.loads(line)
    return (record['mac']), (record['status']), (record['datetime'])

def run(argv=None):
  """Build and run the pipeline."""

  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--input_topic', required=True,
      help='Input PubSub topic of the form "/topics/<PROJECT>/<TOPIC>".')
  parser.add_argument(
      '--output_table', required=True,
      help=
      ('Output BigQuery table for results specified as: PROJECT:DATASET.TABLE '
       'or DATASET.TABLE.'))
  known_args, pipeline_args = parser.parse_known_args(argv)

  with beam.Pipeline(argv=pipeline_args) as p:
    # Read the pubsub topic into a PCollection.
    lines = ( p | beam.io.ReadStringsFromPubSub(known_args.input_topic)
                | beam.Map(parse_pubsub)
                | beam.Map(lambda (mac_bq, status_bq, datetime_bq): {'mac': mac_bq, 'status': status_bq, 'datetime': datetime_bq})
                | beam.io.WriteToBigQuery(
                    known_args.output_table,
                    schema=' mac:STRING, status:INTEGER, datetime:TIMESTAMP',
                    create_disposition=beam.io.BigQueryDisposition.CREATE_IF_NEEDED,
                    write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND)
            )

if __name__ == '__main__':
  logging.getLogger().setLevel(logging.INFO)
  run()
来自未来导入绝对导入
导入日志记录
导入argparse
将apache_梁作为梁导入
将apache_beam.transforms.window作为窗口导入
''将pubsub字符串规范化为json对象''
#线条如下所示:
#{'datetime':'2017-07-13T21:15:02Z','mac':'FC:FC:48:AE:F6:94','status':1}
def parse_pubsub(行):
导入json
record=json.loads(第行)
返回(记录['mac']),(记录['status']),(记录['datetime'])
def运行(argv=None):
“”“生成并运行管道。”“”
parser=argparse.ArgumentParser()
parser.add_参数(
'--input_topic',required=True,
help='Input PubSub-topic of the form'/topics//“)
parser.add_参数(
'--output_table',required=True,
帮助=
('Output BigQuery table for results指定为:PROJECT:DATASET.table'
“或DATASET.TABLE。”)
已知参数,管道参数=解析器。解析已知参数(argv)
将beam.Pipeline(argv=Pipeline_args)作为p:
#将pubsub主题读入PCollection。
lines=(p | beam.io.ReadStringsFromPubSub(已知参数输入主题)
|beam.Map(parse_pubsub)
|Map(lambda(mac_-bq,status_-bq,datetime_-bq):{'mac':mac_-bq,'status':status_-bq,'datetime':datetime_-bq})
|beam.io.WriteToBigQuery(
已知参数输出表,
schema='mac:STRING,status:INTEGER,datetime:TIMESTAMP',
create_disposition=beam.io.BigQueryDisposition.create_如果需要,
write\u disposition=beam.io.BigQueryDisposition.write\u APPEND)
)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
logging.getLogger().setLevel(logging.INFO)
运行()
我有一个类似的用例(从PubSub以字符串形式读取行,将它们转换为dict,然后处理它们)

我使用的是
ast.literal\u eval()
,这似乎对我很有用。此命令将对字符串求值,但求值方式比
eval()
(请参阅)更安全。它应该返回一个dict,它的键是字符串,并且值被计算为最可能的类型(int、str、float…)。不过,您可能需要确保值的类型正确

这会给你一个这样的管道

import ast
lines = ( p | beam.io.ReadStringsFromPubSub(known_args.input_topic)
            | "JSON row to dict" >> beam.Map(
                        lambda s: ast.literal_eval(s))
            | beam.io.WriteToBigQuery( ... )
        )

我还没有使用BigQuery,所以我无法在最后一行帮助您,但您所写的乍一看似乎是正确的。

感谢您的反馈。经过更多的实验,似乎传入的pub/sub消息是以字符串的形式传入的(显然)。我必须应用一个转换,将lines对象转换为字典。我在数据流中遇到的一条错误消息是:组中的输入类型提示冲突:预期的元组[TypeVariable[K],TypeVariable[V]],如果将save_main_session设置为true,则应该能够全局导入模块-示例: