将Mongodb在GCE上的数据导入Bigquery

时间:2016-02-10 17:17:09

标签: json mongodb google-bigquery google-compute-engine

我的任务是将数据从GCE托管的mongodb集合导入Bigquery。我尝试了以下内容。由于bigquery不接受' $'字段名称中的符号,我运行以下内容删除$ oid字段,

mongo test --quiet \
  --eval "db.trial.find({}, {_id: 0})
                  .forEach(function(doc) {
                     print(JSON.stringify(doc)); });" \
  > trial_noid.json

但是,在导入结果文件时,我收到错误消息

  

解析错误:过早的EOF(错误代码:无效)

有没有办法避免这些步骤并直接从GCE上托管的mongodb将数据传输到bigquery?

2 个答案:

答案 0 :(得分:2)

在我看来,最好的做法是建立自己的提取器。这可以使用您选择的语言完成,您可以提取到CSV或JSON。

但是如果你想要快速的方式,如果你的数据不是很大并且可以放在一台服务器中,那么我建议使用mongoexport提取到JSON。我们假设您有一个简单的文档结构,如下所示:

{
    "_id" : "tdfMXH0En5of2rZXSQ2wpzVhZ",
    "statuses" : [ 
        {
            "status" : "dc9e5511-466c-4146-888a-574918cc2534",
            "score" : 53.24388894
        }
    ],
    "stored_at" : ISODate("2017-04-12T07:04:23.545Z")
}

然后您需要定义您的BigQuery架构(mongodb_schema.json),例如:

$ cat > mongodb_schema.json <<EOF
[
    { "name":"_id", "type": "STRING" },
    { "name":"stored_at", "type": "record", "fields": [
        { "name":"date", "type": "STRING" }
    ]},
    { "name":"statuses", "type": "record", "mode": "repeated", "fields": [
        { "name":"status", "type": "STRING" },
        { "name":"score", "type": "FLOAT" }
    ]}
]
EOF

现在,有趣的部分开始:-)从MongoDB中提取数据为JSON。假设您的群集的副本集名称为statuses,您的数据库为sample,而您的集合为status

mongoexport \
    --host statuses/db-01:27017,db-02:27017,db-03:27017 \
    -vv \
    --db "sample" \
    --collection "status" \
    --type "json" \
    --limit 100000 \
    --out ~/sample.json

正如您在上面所看到的,我将输出限制为100k记录,因为我建议您在为所有数据执行之前运行sample并加载到BigQuery。运行上面的命令之后,你应该在sample.json中得到你的样本数据但是有一个字段$date会导致你加载到BigQuery时出错。要解决此问题,我们可以使用sed将其替换为简单字段名称:

# Fix Date field to make it compatible with BQ
sed -i 's/"\$date"/"date"/g' sample.json

现在您可以压缩,上传到Google云端存储(GCS),然后使用以下命令加载到BigQuery:

# Compress for faster load
gzip sample.json

# Move to GCloud
gsutil mv ./sample.json.gz gs://your-bucket/sample/sample.json.gz

# Load to BQ
bq load \
    --source_format=NEWLINE_DELIMITED_JSON \
    --max_bad_records=999999 \
    --ignore_unknown_values=true \
    --encoding=UTF-8 \
    --replace \
    "YOUR_DATASET.mongodb_sample" \
    "gs://your-bucket/sample/*.json.gz" \
    "mongodb_schema.json"

如果一切正常,请返回并从--limit 100000命令中删除mongoexport并再次重新运行上面的命令以加载所有内容而不是100k样本。

使用此解决方案,您可以将具有相同层次结构的数据导入BigQuery,但如果您想要平坦化数据,那么下面的替代解决方案可以更好地工作。

替代解决方案:

如果您需要更多灵活性并且不需要考虑性能,那么您也可以使用mongo CLI工具。这样,您可以在JavaScript中编写提取逻辑并针对您的数据执行它,然后将输出发送到BigQuery。这是我为同一个过程所做的,但是使用JavaScript以CSV格式输出,这样我就可以更轻松地加载BigQuery了:

# Export Logic in JavaScript
cat > export-csv.js <<EOF
var size = 100000;
var maxCount = 1;
for (x = 0; x < maxCount; x = x + 1) {
    var recToSkip = x * size;
    db.entities.find().skip(recToSkip).limit(size).forEach(function(record) {
        var row = record._id + "," + record.stored_at.toISOString();;
        record.statuses.forEach(function (l) {
            print(row + "," + l.status + "," + l.score)
        });
    });
}
EOF

# Execute on Mongo CLI
_MONGO_HOSTS="db-01:27017,db-02:27017,db-03:27017/sample?replicaSet=statuses"
mongo --quiet \
    "${_MONGO_HOSTS}" \
    export-csv.js \
    | split -l 500000 --filter='gzip > $FILE.csv.gz' - sample_

# Load all Splitted Files to Google Cloud Storage
gsutil -m mv ./sample_* gs://your-bucket/sample/

# Load files to BigQuery
bq load \
    --source_format=CSV \
    --max_bad_records=999999 \
    --ignore_unknown_values=true \
    --encoding=UTF-8 \
    --replace \
    "YOUR_DATASET.mongodb_sample" \
    "gs://your-bucket/sample/sample_*.csv.gz" \
    "ID,StoredDate:DATETIME,Status,Score:FLOAT"

提示:在上面的脚本中,我通过管道输出做了一个小技巧,能够将输出分成多个sample_前缀的文件。在分割期间,它将GZip输出,以便您可以更轻松地加载到GCS。

答案 1 :(得分:1)

使用NEWLINE_DELIMITED_JSON将数据导入BigQuery时,each line上必须出现一个JSON对象,包括任何嵌套/重复字段。

输入文件的问题似乎是JSON对象被分成多行;如果你将它折叠成一行,它将解决这个问题 错误。

要求此格式允许BigQuery拆分文件并并行处理,而不必担心拆分文件会将JSON对象的一部分放在一个拆分中,而另一部分放在下一个拆分中。