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

时间:2016-06-02 14:46:19

标签: javascript node.js node-streams

我有一些非常大(> 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/...'
    }
}

我用这样的小数据集实现了这一点,首先处理'原始'尺寸:

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加载到内存中。这似乎是使用流的好时机,但当然如果我以块的形式读取文件,在小数组上运行.find()以找到“原始”大小将无效。如果我扫描整个文件以找到原件然后再次扫描以将其他尺寸添加到我发现的内容中,那么无论如何我都会在内存中找到整个数据集。

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

当然,我不能成为第一个遇到这类问题的人。过去使用了哪些解决方案?我该如何处理?

2 个答案:

答案 0 :(得分:0)

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

我很确定没有办法如何实现你的目标,如果没有a)损害过程的速度(急剧)或b)从头开始创建穷人的DB(这似乎是一件坏事:) )

答案 1 :(得分:0)

我认为诀窍是即时更新数据库。如果JSON文件对于内存而言太大,那么我希望得到的对象集(在您的示例中为RegCM-4.5.0.tar.gz)对于内存来说太大了。

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

parse.js

readline

text.json

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); }

注意:我需要引用时间戳值以避免语法错误。从您的问题和示例脚本中我预计您可能没有遇到此问题或已经解决了这个问题,或许是另一种方式。

此外,我对{ 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' } 的实现可能与您解析JSON的方式不同。由于没有引用属性,简单的旧parseJSON失败了。