文本匹配算法

最近一直在搞数据血缘相关的功能,对于纯粹的sql的代码其实是不需要搞得这么麻烦。但是后面由于是需要拉通下游应用,包括下游的magicapi,和帆软报表标本之类的。但是下面这些的代码呢,是无法通过druid或者别的sql代码来进行解析的。因为这些脚本本身就不是正常sql代码。因此还是得回到文本匹配上面的。
当然传统的匹配算法可以简单的使用String.contains()算法,但是这个本质上就是简单的字符串暴力匹配,像我数据库底表本身有3000多张,如果每一个脚本都要这样进行循环匹配的话,时间效率将会非常低。
因此在这里我们引入了ahocorasick算法。

ahocorasick 算法

ahocorasick本质上是一种构建自动机的算法。具体可以参考这个文章https://developer.aliyun.com/article/1504093 但是今天其实并不像太多的讲述算法的底层原理。这里主要解释一下在java环境下具体要怎么用。

pom

ahocorasick本质是一种思想,因此没有一个很官方的方法。在这里引用这个包

    <dependency>
      <groupId>org.ahocorasick</groupId>
      <artifactId>ahocorasick</artifactId>
      <version>0.6.3</version>
    </dependency>

在正式代码为:

package org.example.utils;
import org.ahocorasick.trie.Trie;
import org.ahocorasick.trie.Emit;

import java.nio.file.*;
import java.util.*;

import static org.example.utils.RegexMapUtils.containsAnyWord;

public class ComplexStringMap {
    public static Set<String> getTableNames(String script,List<String> mapping) {
        Trie trie = Trie.builder().ignoreCase().addKeywords(mapping).build();

        Collection<Emit> emits = trie.parseText(script);
        Set<String> foundTables = new HashSet<>();
        for (Emit emit : emits) {
            foundTables.add(emit.getKeyword());
        }
        Set<String> resultSet=new HashSet<>();
        StringBuffer buffer=new StringBuffer();
        buffer.append("\\b(");
        for (String tableName : foundTables){
            buffer.append(tableName);
            buffer.append("|");
        }
        buffer.deleteCharAt(buffer.length()-1);
        buffer.append(")\\b");
        String pattern=buffer.toString();
        Set<String> tables=containsAnyWord(pattern,script);
        return tables;
    }

}

其中参数1是需要匹配文本,参数2是目标匹配文本。然后,文本中的foundTables就是匹配命中的字符串。我这边由于要继续其他操作,因此进行了其他的操作。
实际上测试,这个算法确实是非常快的。