2012-07-10 61 views
0

我有一个相当大的第三方数据集合,它包含一个实际存储纯ASCII的Bindata“包”列。 我的意思是,他们可以将文本存储为字符串。在mongodb中导出保存为Bindata的文本

我必须以csv格式导出这个集合,这对于mongoexport来说效果很好,但是输出包含“包”列的base64编码值。我需要该栏中的实际文字,而不是BinData(0,\"NDYuN.....==")

什么我试过到目前为止是一个新列“RAWDATA”,类似这样的更新集合:

db.segments.find({"_id" : ObjectId("4fc79525f65181293930070b")}).forEach(function(data) { 
    db.segments.update(
    {_id:data._id}, 
    {$set:{ "rawData" : data.package.toString() }} 
); 
}); 

我已经限制查找到只有一个文件,直到我得到它的权利。 不幸的是toString并没有达到我期望的魔法。

而且,我已经试过这样:

db.segments.find({"_id" : ObjectId("4fc79525f65181293930070b")}).forEach(function(data){ 
    data.package = new String(data.package); 
    db.segments.save(data); 
}); 

结果更惨。

如果我使用php读取文档,$response = $db->execute('return db.segments.findOne()');然后print_r($response),我可以验证数据是否正确存储为base64。

我无法在任何地方找到解决方案,也许是因为没有人需要像这样做一些愚蠢的事情。

回答

8

JavaScript BinData对象有base64()hex()方法,您可以使用这些方法接收相应格式的字符串。至于这些值解码成内JS文本字符串,你有一些选项:

按照BSON spec,二进制数据字段由32位的整数长度的,子类型和字节数组。 JS中返回的base64和十六进制表示包含前缀为整数长度的字节数组。这意味着在对这些值进行解码之后,我们需要去掉前四个字节。下面是使用这两个选项的例子:

// https://stackoverflow.com/a/3058974/162228 
function decode_base64(s) { 
    var e={},i,k,v=[],r='',w=String.fromCharCode; 
    var n=[[65,91],[97,123],[48,58],[43,44],[47,48]]; 

    for(z in n){for(i=n[z][0];i<n[z][1];i++){v.push(w(i));}} 
    for(i=0;i<64;i++){e[v[i]]=i;} 

    for(i=0;i<s.length;i+=72){ 
     var b=0,c,x,l=0,o=s.substring(i,i+72); 
     for(x=0;x<o.length;x++){ 
      c=e[o.charAt(x)];b=(b<<6)+c;l+=6; 
      while(l>=8){r+=w((b>>>(l-=8))%256);} 
     } 
    } 
    return r; 
} 

// https://stackoverflow.com/a/3745677/162228 
function hex2a(hex) { 
    var str = ''; 
    for (var i = 0; i < hex.length; i += 2) 
     str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 
    return str; 
} 

db.segments.find().forEach(function(doc){ 
    print(decode_base64(doc.package.base64())); 
}); 

db.segments.find().forEach(function(doc){ 
    print(hex2a(doc.package.hex())); 
}); 

这里是一个小PHP脚本,我用来插入一些固定的数据:

<?php 

$mongo = new Mongo(); 
$c = $mongo->selectCollection('test', 'segments'); 
$c->drop(); 

$c->save(['package' => new MongoBinData('foo')]); 
$c->save(['package' => new MongoBinData('bar')]); 

foreach ($c->find() as $doc) { 
    printf("%s\n", $doc['bindata']->bin); 
} 

根据数据集的大小,可能更合理地做PHP中的二进制字段转换。如果你想使用JavaScript,我肯定会建议通过shell客户端而不是db.eval()来执行脚本,所以你不要使用长时间运行的JS函数来锁定数据库。

+0

谢谢,这是一个非常好的答案!我选择了PHP的方式,但其他功能也很好。唯一的问题是,我不得不从用法示例中删除'.substr(4)',因为我的数据被“裁剪”(结果以72247而不是46.772247开头)。 – ceteras 2012-07-11 08:20:31

+0

有趣。介意我问你使用的MongoDB的版本是什么?上面有2.1.0运行。如果我插入二进制数据“foo”,然后在JS控制台中选择它,它的'hex()'方法返回'03000000666f6f'。 – jmikola 2012-07-11 15:33:41

+0

我正在使用1.8.2 – ceteras 2012-08-01 14:43:12