将多个对象映射到流中的单个对象

Map multiple objects to single object in stream

我有一些非常大的 (> 500MB) JSON 文件需要映射到新格式并上传到新数据库。

旧格式:

{
    id: '001',
    timestamp: 2016-06-02T14:10:53Z,
    contentLength: 123456,
    filepath: 'original/...',
    size: 'original'
},
{
    id: '001',
    timestamp: 2016-06-02T14:10:53Z,
    contentLength: 24565,
    filepath: 'medium/...',
    size: 'medium'
},
{
    id: '001',
    timestamp: 2016-06-02T14:10:53Z,
    contentLength: 5464,
    filepath: 'small/...',
    size: 'small'
}

新格式:

{
    Id: '001',
    Timestamp: 2016-06-02T14:10:53Z,
    OriginalSize: {
        ContentLength: 123456,
        FilePath: 'original/...'
    },
    MediumSize: {
       ContentLength: 24565,
       FilePath: 'medium/...'
    },
    SmallSize: {
        ContentLength: 5464,
        FilePath: 'small/...'
    }
}

我是用这样的小数据集实现的,首先处理 'original' 大小:

let out = data.filter(o => o.size === 'original).map(o => {
    return {
        Id: o.id,
        Timestamp: o.timestamp,
        OriginalSize: {
            ContentLength: o.contentLength,
            FilePath: o.filepath
        }
    };
});
data.filter(o => o.size !== 'original').forEach(o => {
    let orig = out.find(function (og) {
        return og.Timestamp === o.timestamp;
    });
    orig[o.size + 'Size'] = {
        ContentLength: o.contentLength,
        FilePath: o.filepath
    };
)
// out now contains the correctly-formatted objects

问题来自于非常大的数据集,我无法一次性将数百兆字节的 JSON 加载到内存中。这似乎是使用流的好时机,但当然,如果我以块的形式读取文件,运行ning .find() 在一个小数组上查找 'original' 大小将不起作用。如果我扫描整个文件以找到原件,然后再次扫描以将其他尺寸添加到我找到的文件中,我最终会在内存中得到整个数据集。

我知道 JSONStream,如果我对我的对象进行简单的 1-1 重新映射,那会很棒。

我肯定不是第一个 运行 遇到这种问题的人。过去使用过哪些解决方案?我该如何处理?

设置一些数据库实例,可以存储JSON 文档。 MongoDB 或 PostgreSQL(最近他们引入了 jsonb 数据类型来存储 json 文档)。遍历旧 JSON 文档并将它们组合到新结构中,使用数据库作为存储 - 这样您就可以克服内存问题。

我很确定,如果不 a) 牺牲进程速度(急剧地)或 b) 从头开始​​创建穷人的数据库(这似乎是一件坏事),就没有办法实现你的目标:))

我认为诀窍是即时更新数据库。如果 JSON 文件对于内存来说太大了,那么我预计生成的对象集(在你的示例中为 out)对于内存来说也太大了。

在评论中,您声明 JSON 文件每行有一个对象。因此使用 node.js 内置 fs.createReadStreamreadline 来获取文本文件的每一行。接下来将行(字符串)处理成一个 json 对象,最后更新数据库。

parse.js

var readline = require('readline');
var fs = require('fs');

var jsonfile = 'text.json';

var linereader = readline.createInterface({
  input: fs.createReadStream(jsonfile)
});

linereader.on('line', function (line) {
  obj = parseJSON(line); // convert line (string) to JSON object

  // check DB for existing id/timestamp
  if ( existsInDB({id:obj.id, timestamp:obj.timestamp}) ) {
    updateInDB(obj); // already exists, so UPDATE
  }
  else { insertInDB(obj); } // does not exist, so INSERT
});


// DUMMY functions below, implement according to your needs

function parseJSON (str) {
  str = str.replace(/,\s*$/, ""); // lose trailing comma
  return eval('(' + str + ')'); // insecure! so no unknown sources
}
function existsInDB (obj) { return true; }
function updateInDB (obj) { console.log(obj); }
function insertInDB (obj) { console.log(obj); }

text.json

{ id: '001', timestamp: '2016-06-02T14:10:53Z', contentLength: 123456, filepath: 'original/...', size: 'original' },
{ id: '001', timestamp: '2016-06-02T14:10:53Z', contentLength: 24565, filepath: 'medium/...', size: 'medium' },
{ id: '001', timestamp: '2016-06-02T14:10:53Z', contentLength: 5464, filepath: 'small/...', size: 'small' }

注意:我需要引用时间戳值以避免语法错误。根据您的问题和示例脚本,我希望您没有这个问题或者已经解决了这个问题,也许是另一种方式。

此外,我对 parseJSON 的实现可能与您解析 JSON 的方式不同。由于未引用属性,普通旧 JSON.parse 对我来说失败了。