更有效的近似字符串与MongoDB匹配

时间:2015-03-19 10:01:40

标签: java regex mongodb string-matching similarity

我有这个相对较大(近130000个文件)的MongoDB名称集合,其中包含大量重复的OCR噪音,我尝试使用近似字符串匹配将这些副本组合在一起。
我已经使用Simmetrics library在Java中实现了这一点,如下所示:

DBCursor persons = coll.find(query).addOption(Bytes.QUERYOPTION_NOTIMEOUT);
        try{
            while(persons.hasNext()){
                DBObject p = persons.next();

                DBObject personName = (DBObject) p.get("person");
                String n1 = personName.get("Name").toString();
                System.out.println("\n"+ personName + ":");

                DBCursor aliases = coll.find(query).addOption(Bytes.QUERYOPTION_NOTIMEOUT);
                try{
                    while(aliases.hasNext()){
                        DBObject a = aliases.next();    

                        DBObject aliasName = (DBObject) a.get("person");                            
                        String n2 = aliasName.get("Name").toString();

                        float simLev = new Levenshtein().getSimilarity(n1, n2);

                        if (simLev >= 0.65){
                        System.out.println("    "+ aliasName + ", Sim: " + simLev);
                        }
                    }
                } finally{
                    aliases.close();
                }

            }
        } finally{
            persons.close();
        }

因此,我使用两个游标将每个文档与每个文档进行比较,并且(仅用于测试目的)我只打印出65%或更高相似度的匹配(以Levenshtein距离为例)。
1名称的输出示例:

{ "Name" : "Baldaino, Manene M."}:
    { "Name" : "Baldaino, Manene M."}, Sim: 1.0
    { "Name" : "Baldaino, Marlene C4."}, Sim: 0.8095238
    { "Name" : "Baldaino Marlene M"}, Sim: 0.78947365
    { "Name" : "BaldainD, Marlene M."}, Sim: 0.85
    { "Name" : "Baldaino Madene M."}, Sim: 0.8947368
    { "Name" : "Baidaino, Marlene M"}, Sim: 0.78947365
    { "Name" : "Baldaino, Marlene M. 0C"}, Sim: 0.7826087
    { "Name" : "Baldaino, Marlene M. (0"}, Sim: 0.7826087
    { "Name" : "8aldaino, Marlene M,"}, Sim: 0.8
    { "Name" : "Baldaino Madene"}, Sim: 0.7368421
    { "Name" : "Baldaino, Marlene 00"}, Sim: 0.8
    { "Name" : "Baldaino, Marlene hi."}, Sim: 0.8095238
    { "Name" : "BaWaino, Marlene M."}, Sim: 0.78947365
    { "Name" : "Baldaino, Marlene M. (3i"}, Sim: 0.75
    { "Name" : "Bedainc, Marlene M."}, Sim: 0.7368421
    { "Name" : "Baldaino, Marlene M. cfl"}, Sim: 0.75  

这个实现并不是非常有效,但在整个Collection上运行它我认为在我的电脑上至少需要40个小时。

有谁知道如何才能获得更好的表现? 我已经阅读了有关使用ElasticSearch with Mongo Connector的信息,但我不想学习使用新工具。

3 个答案:

答案 0 :(得分:0)

我认为可以将此请求结果加载到内存中(您可以使用Go of ram)" name"和" id"它会避免循环中的第二个光标。它会提高性能。

List<NameID> nameIds = new List<NameID>(100000);
    DBCursor persons = coll.find(query).addOption(Bytes.QUERYOPTION_NOTIMEOUT);
        try{
            while(persons.hasNext()){
                    DBObject p = persons.next();
                  nameIds.add(new NameId((DBObject) p.get("person"),(DBObject) p.get("ID"))
         }
    } } finally{
            persons.close();
        } }

循环算法中的列表。

答案 1 :(得分:0)

有一种非常快速的算法可以对一大组条目进行字符串相似性。检查Simstring [1]。这是高度优化的方式来做余弦,jaccard和骰子字符串的相似之处。 作者声称他可以查询Google Web1T unigrams(13,588,391字符串),每个查询1.10 [ms](在Intel Xeon 5140 2.33 GHz CPU上)的余弦相似度≥0.7。

首先它构建了一个ngrams映射,然后给出一个查询,它计算出它应该匹配的最小ngram匹配集。

我用它来匹配数百万个Freebase名称和一组实体名称以及其他数百万条目。

我最近在Scala上实现了一个实现[2]。但是你也可以使用Chokkan实现(需要从c ++编译,然后为Java生成swig类)。

如果你从mongo转出字符串,并使用这个数据结构,你绝对可以非常快速地计算近似的字符串匹配。

[1] http://www.chokkan.org/software/simstring/

[2] https://github.com/dav009/FuzzyDict

答案 2 :(得分:0)

目前您只查看文档中的person.Name子字段, 但是你正在从数据库加载整个文档 - 浪费RAM和带宽。

这样做:

find(query).projection(new Document("person.Name", 1))

应该加快速度。