读取许多 JSON 文档并合并在一个(大数据?)

Posted

技术标签:

【中文标题】读取许多 JSON 文档并合并在一个(大数据?)【英文标题】:Read many JSON documents and union in only one (BigData?) 【发布时间】:2016-03-29 19:27:27 【问题描述】:

我需要读取许多 JSON 文档并进行合并以仅生成一个文档。

让我解释一下:我有一个跟踪系统来捕获来自网站用户导航的许多事件。我可以使用属性ID来映射相关文档并创建合并文档。

例子

我的意见:

 id : "12345", fly : "nyc-bos", time: "10:00am" 
 id : "12345", fly : "orl-mia", time: "09:00am" 
 id : "12345", fly : "chi-mem", time: "07:00am" 
 id : "12345", order: "099300" 
 id : "12345", order: "677800" 
 id : "12345", order: "129999" 
 id : "12345", product: "DVD" 
 id : "12345", product: "LCD TV" 

我需要这样的文件:


    id: "12345"
          fly :
             "nyc-bos", time: "10:00am"
             "orl-mia", time: "09:00am"
             "chi-mem", time: "07:00am"
          order :
             "099300"
             "677800"
             "129999"
          product :
             "DVD"
             "LCD TV"

重要:

我有数百万个输入文档 我无法使用 BigData 框架(Hadoop 等) 我的堆栈受到限制(Windows + C# + CouchDB)

有人有我可以遵循的想法吗?

谢谢

【问题讨论】:

【参考方案1】:

您只需在 map/reduce 中执行此操作,只需为您的 map 提供一个普通的 emit(doc.id, doc),然后为您的 reduce 执行类似的操作:

function( keys, values, rereduce ) 
  var doc = ;
  values.forEach( function(d) 
    var dd = doc[d.id] = doc[d.id] || ;

    if(d.fly) 
      dd['fly'] = dd['fly'] || [];
      dd.fly.push( code: d.fly, time: d.time );
    
    else if(d.order) 
      dd['order'] = dd['order'] || [];
      dd.order.push(d.order);
    
    else if(d.product) 
      dd['product'] = dd['product'] || [];
      dd.product.push(d.product);
    
  );
  return doc;

请注意,我在fly 对象中使用code 作为键,在JSON 中不能有"nyc-bos", time: "10:00am",两个值都需要有键。

【讨论】:

【参考方案2】:

您正在查看的是数据聚合,它可以通过 couchdb 使用名为“视图”和“列表”的两个概念来实现。您的技术堆栈应该不是问题,因为从概念上讲,您可以使用 httpclient 与 couchdb 进行交互。我建议你先阅读沙发视图, https://wiki.apache.org/couchdb/Introduction_to_CouchDB_views http://guide.couchdb.org/draft/views.html 一旦你熟悉了它们,你就可以输出一个数据集,如你的第一个 sn-p 中所示,这就是沙发“列表”函数将发挥作用的地方,它可以聚合数据并输出某种格式,如你的第二个 sn-p .基本上,列表函数位于管道中的视图函数旁边,它们将被提供视图函数的输出。通过阅读官方 couchdb 文档,您可以更深入地理解这个概念。为方便起见,请参阅下面的链接。 http://guide.couchdb.org/draft/transforming.html

如果需要为文档输出一个统计数据集 (couch reduce functions),它可能看起来像这样

 "rows":
   [
     "key":"de", "value":"sum":2,"count":2,"min":1,"max":1,"sumsqr":2,
     "key":"ee", "value":"sum":2,"count":2,"min":1,"max":1,"sumsqr":2,
     "key":"de", "value":"sum":2,"count":2,"min":1,"max":1,"sumsqr":2,
     "key":"ee", "value":"sum":2,"count":2,"min":1,"max":1,"sumsqr":2,
     "key":"de", "value":"sum":2,"count":2,"min":1,"max":1,"sumsqr":2
   ]

为了聚合它们,我们可以输出一个具有唯一键但聚合值(最小值、最大值等)的数据集,我们可以编写一个列表函数,如下所示

function(head, req) 
  var row;
  var result = [];
  var firstRun = true;
  var found = true;
  start(
    "headers": 
      "Content-Type": "application/json"
     
  );

while(row = getRow())       
      if(firstRun)
          firstRun = false;
          result.push(id: row.key, sum: row.value.sum, count: row.value.count, min: row.value.min, max: row.value.max, sumsqr: row.value.sumsqr);
      else
        for (var i = 0; i < result.length; i++) 
          if (row.key === result[i].id) 
              result[i].sum += row.value.sum;
              result[i].count += row.value.count;
              result[i].min = ((row.value.min < result[i].min) ? row.value.min : result[i].min);
              result[i].max = ((row.value.max > result[i].max) ? row.value.max : result[i].max);
              result[i].sumsqr = result[i].sum;
              found = true;
            break;
          else
            found = false;
          
              
      
    if(!found)
        result.push(id: row.key, sum: row.value.sum, count: row.value.count, min: row.value.min, max: row.value.max, sumsqr: row.value.sumsqr);
        found = true;
    
   return JSON.stringify(result);

【讨论】:

【参考方案3】:

这是Map/Reduce "collation" 的典型案例。

注意:由于id 具有误导性(带有_id,文档标识符),我将其称为user_id

首先,您必须发出没有值的排序规则键(永远不要发出 doc 作为值。索引越轻越好。到原始文档的链接会自动完成)。我们还将类别添加到键中,因为您希望对结果进行排序。

function(o) 
    if (o.fly) 
      emit([o.user_id, 'fly']);
     else if (o.order) 
      emit([o.user_id, 'order']);
     else if (o.product) 
      emit([o.user_id, 'product']);
    

不要定义 reduce 函数,因为您想保留指向原始文档的链接。

使用include_docs=true 查询您的视图以获取链接的文档并使用startkeyendkey 选择用户:

/mydb/_design/mydesign/_view/myview?include_docs=true&startkey=["12345"]&endkey=["12345",]

你会得到:

[
"key":["12345", "fly"], "value": null, "doc":"user_id": "12345", "fly":"nyc-bos", "time":"10:00am",
"key":["12345", "fly"], "value": null, "doc":"user_id": "12345", "fly": "orl-mia", "time": "09:00am",
"key":["12345", "fly"], "value": null, "doc":"user_id": "12345", "fly": "chi-mem", "time": "07:00am",
"key":["12345", "order"], "value": null, "doc":"user_id": "12345", "order": "099300",
"key":["12345", "order"], "value": null, "doc":"user_id": "12345", "order": "677800",
"key":["12345", "order"], "value": null, "doc":"user_id": "12345", "order": "129999",
"key":["12345", "product"], "value": null, "doc":"user_id": "12345", "product": "DVD",
"key":["12345", "product"], "value": null, "doc":"user_id": "12345", "product": "LCD TV"
]

所有繁重的计算(也称为“限制”、“连接”和“排序”)都由 Map/Reduce 完成。然后,您可以使用简单的list function 调整格式。

【讨论】:

以上是关于读取许多 JSON 文档并合并在一个(大数据?)的主要内容,如果未能解决你的问题,请参考以下文章

如何读取真正大的 JSON 文件并使用 node.js 将该文件的数据插入 MYSQL 数据库?

使用 JdbcTemplate 从数据库中读取大数据并通过 api 公开?

NodeJS:读取一个大的 csv 文件

Python - 读取 JSON 大尺寸数据

从文件中读取,并归并排序

从文件中读取,并归并排序