- 自己动手写分布式搜索引擎
- 罗刚
- 1099字
- 2020-11-28 15:52:49
3.3.4 模糊匹配
关于字符串的前缀匹配问题:只查询产品名字为字母A打头的搜索,可以使用前缀匹配(PrefixQuery)实现。查询语法是A*。注意,查询的不一定是整个字段以A开头的记录,而只是其中的单词以A开头。
Query query = new WildcardQuery(new Term(FIELD, "cut*")); // 通配符
等价于:
Query query = new PrefixQuery(new Term(FIELD, "cut")); // 自动在结尾添加 *
测试WildcardQuery():
// 索引一些文档 writer.addDocument(createDocument("1", "foo bar baz")); writer.addDocument(createDocument("2", "red green blue")); writer.addDocument(createDocument("3", "The Lucene was made by Doug Cutting")); writer.close(); IndexReader reader = DirectoryReader.open(directory); IndexSearcher searcher = new IndexSearcher(reader); Query query = new WildcardQuery(new Term(FIELD, "cut*")); TopDocs topDocs = searcher.search(query, 10);
这里的cut只能是小写,否则就匹配不上。如果用户输入的是大写,如Cut,可以用QueryParser()转换成小写。
使用反转Token的方法允许通配符出现在开头。ReversedWildcardsTokenFilter可以实现Token反转。ReversedWildcardsTokenFilter返回原来的Token和反转的Token,其中反转的Token的positionIncrement值是0。
可以用一个标识字符来避免正常Token和反转Token之间的冲突。例如:“DNA”反转后就变成“and”了,和正常的词“and”有冲突。但是使用标识字符后,“DNA”就变成了“\u0001and”。
可以把ReversedWildcardsTokenFilter加入分析器链,这样在做索引的时候就可以使用了。
有些英文单词需要查询扩展,如搜索“dog”的同时查找“dogs”。FuzzyQuery()有限状态查询(Finite-State Query)用编辑距离衡量相似度,例如“dog”和“dogs”的编辑距离是1。FuzzyQuery()内部使用编辑距离有限状态机实现,所以性能很好。
int maxEdits = 2; //编辑距离最多不能超过2 new FuzzyQuery(new Term("title", "dog"), maxEdits);
可以设置相同前缀的长度。例如,相同前缀的长度是1:
FuzzyQuery query = new FuzzyQuery(new Term("field", "WEBER"), 2, 1);
query要求匹配的词必须以W开头,而且匹配的词与查询词之间的编辑距离不超过2。这个FuzzyQuery等价于如下的有限状态自动机:
LevenshteinAutomata builder = new LevenshteinAutomata("EBER", true); Automaton a = builder.toAutomaton(2); //最大编辑距离是2 Automaton b = BasicAutomata.makeChar('W'); //创建一个字符W组成的自动机 Automaton c = BasicOperations.concatenate(b, a); //连接两个自动机b和a
这里的BasicOperations.concatenate()方法把b的结束状态和a的开始状态用空转换连接起来,也就是按顺序走过a中的状态,然后走b中的状态。例如,自动机b可以接收字符W,然后自动机a继续接收EB,就可以结束了。这说明自动机c可以接收WEB。
测试自动机c可以接收哪些字符:
System.out.println(BasicOperations.run(c, "WBR")); //输出true System.out.println(BasicOperations.run(c, "WEB")); //输出true System.out.println(BasicOperations.run(c, "WEBE")); //输出true System.out.println(BasicOperations.run(c, "WEBER")); //输出true
在中文人名中使用自动机:
LevenshteinAutomata builder = new LevenshteinAutomata("杰伦", true); Automaton a = builder.toAutomaton(1); //最大编辑距离是1 Automaton b = BasicAutomata.makeChar(’周’); Automaton c = BasicOperations.concatenate(b, a); //连接两个自动机b和c System.out.println(BasicOperations.run(c, "周杰伦")); //匹配写错的人名
在中文人名中使用模糊查询:
FuzzyQuery query = new FuzzyQuery(new Term("field", "周杰伦"), 1, 1);
“dogs~”这样的模糊查询语法使用FuzzyQuery()。FuzzyQuery()还可以用于拼写检查。
可以根据Automaton对象构建AutomatonQuery。
Term term= new Term("yourfield", "周~*"); LevenshteinAutomata builder = new LevenshteinAutomata("杰伦", true); Automaton a = builder.toAutomaton(1); //最大编辑距离是1 Automaton b = BasicAutomata.makeChar(’周’); Automaton c= BasicOperations.concatenate(b, a); //连接两个自动机b和c AutomatonQuery query = new AutomatonQuery(term, c);
再给出一个星闭包的例子:
//查询的term表示,包含列 Term term= new Term("yourfield", "bla~*"); //对所有的和“bla”编辑距离在2以内的字符串构建一个确定性有限状态自动机 Automaton fuzzy = new LevenshteinAutomata("bla", false).toAutomaton(2); //串联fuzzy和另外一个DFA等于"*"操作符,也就是星闭包 Automaton fuzzyPrefix= BasicOperations.concatenate(fuzzy, BasicAutomata.makeAnyString()); //构建一个查询,用它搜索以得到结果 AutomatonQuery query = new AutomatonQuery(term, fuzzyPrefix);
Lucene的NumericRangeQuery采用了Trie树结构的索引,可以模仿NumericRangeQuery编写字符串的前缀匹配实现。