从MongoDB中的文本字段生成Unigrams列表的最有效方法

时间:2011-07-08 15:41:06

标签: mongodb lucene nlp mapreduce opennlp

我需要生成一个unigrams矢量,即我在MongoDB中作为更广泛的JSON对象的一部分存储的特定文本字段中出现的所有唯一单词的向量。

我不确定生成此向量的最简单,最有效的方法是什么。我正在考虑编写一个可以处理标记化的简单Java应用程序(使用类似OpenNLP的东西),但我认为更好的方法可能是尝试使用Mongo的Map-Reduce功能解决这个问题...但是我不是真的确定我怎么能这样做。

另一个选择是使用Apache Lucene索引,但这意味着我仍然需要逐个导出这些数据。这与我使用自定义Java或Ruby方法的问题完全相同......

Map减少声音很好,但随着插入更多文档,Mongo数据日益增长。这不是一次性的任务,因为一直在添加新文档。更新非常罕见。我真的不希望每次想要更新我的Unigram向量时都在数百万个文档上运行Map-Reduce,因为我担心这会非常低效地使用资源......

生成unigram向量然后保持更新的最有效方法是什么?

谢谢!

1 个答案:

答案 0 :(得分:2)

由于您尚未提供样本文档(对象)格式,请将此作为名为'stories'的样本集合。

{ "_id" : ObjectId("4eafd693627b738f69f8f1e3"), "body" : "There was a king", "author" : "tom" }
{ "_id" : ObjectId("4eafd69c627b738f69f8f1e4"), "body" : "There was a queen", "author" : "tom" }
{ "_id" : ObjectId("4eafd72c627b738f69f8f1e5"), "body" : "There was a queen", "author" : "tom" }
{ "_id" : ObjectId("4eafd74e627b738f69f8f1e6"), "body" : "There was a jack", "author" : "tom" }
{ "_id" : ObjectId("4eafd785627b738f69f8f1e7"), "body" : "There was a humpty and dumpty . Humtpy was tall . Dumpty was short .", "author" : "jane" }
{ "_id" : ObjectId("4eafd7cc627b738f69f8f1e8"), "body" : "There was a cat called Mini . Mini was clever cat . ", "author" : "jane" }

对于给定的数据集,您可以使用以下javascript代码来获取解决方案。集合“ authors_unigrams ”包含结果。所有代码都应该使用mongo控制台运行(http://www.mongodb.org/display/DOCS/mongo+-+The+Interactive+Shell)。

首先,我们需要标记重新进入'故事'集合的所有新文档。我们使用以下命令来完成它。它将在每个文档中添加一个名为“mr_status”的新属性,并为其分配值“inprocess”。稍后,我们将看到map-reduce操作只会将那些具有“inprocess”值的文档记录到字段“mr_status”中。通过这种方式,我们可以避免重新考虑以前任何尝试中已经考虑过的map-reduce操作的所有文档,从而使操作高效。

db.stories.update({mr_status:{$exists:false}},{$set:{mr_status:"inprocess"}},false,true);

第二,我们定义 map() reduce()功能。

var map = function() {
        uniqueWords = function (words){
            var arrWords = words.split(" ");
            var arrNewWords = [];
            var seenWords = {};
            for(var i=0;i<arrWords.length;i++) {
                if (!seenWords[arrWords[i]]) {
                    seenWords[arrWords[i]]=true;
                    arrNewWords.push(arrWords[i]);
                }
            }
            return arrNewWords;
        }
      var unigrams =  uniqueWords(this.body) ;
      emit(this.author, {unigrams:unigrams});
};

var reduce = function(key,values){

    Array.prototype.uniqueMerge = function( a ) {
        for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
            if ( this.indexOf( a[i] ) === -1 ) {
                nonDuplicates.push( a[i] );
            }
        }
        return this.concat( nonDuplicates )
    };

    unigrams = [];
    values.forEach(function(i){
        unigrams = unigrams.uniqueMerge(i.unigrams);
    });
    return { unigrams:unigrams};
};

第三次,我们实际上运行了map-reduce函数。

var result  = db.stories.mapReduce( map,
                                  reduce,
                                  {query:{author:{$exists:true},mr_status:"inprocess"},
                                   out: {reduce:"authors_unigrams"}
                                  });

第四,我们将上次运行中考虑用于map-reduce的所有记录标记为已处理,将“mr_status”设置为“已处理”。

db.stories.update({mr_status:"inprocess"},{$set:{mr_status:"processed"}},false,true);

(可选),您可以通过触发以下命令来查看结果集合“authors_unigrams”

db.authors_unigrams.find();