本文是我们名为“ Apache Lucene基础知识 ”的学院课程的一部分。
在本课程中,您将了解Lucene。 您将了解为什么这样的库很重要,然后了解Lucene中搜索的工作方式。 此外,您将学习如何将Lucene Search集成到您自己的应用程序中,以提供强大的搜索功能。 在这里查看 !
目录
- 1.引言 2.Lucene查询 3,Lucene查询API 4,基本搜索
- 4.1。条款 4.2。通配符查询 4.3。布尔运算符 4.3分组 4.4。短语查询 4.5。范围查询 4.6。前缀查询 4.7。模糊查询
1.引言
在上一章中,我们了解了Lucene搜索引擎的不同组件 。 我们还使用lucene索引和搜索程序构建了一个小型搜索应用程序。 在本章中,我们将讨论Lucene查询。
2.Lucene查询
Lucene具有用于查询其索引的自定义查询语法。 查询分为术语和运算符。 术语有两种类型: 1 . 单词和2.短语 。 单个术语是一个单词,例如“测试”或“样本”。 词组是一组用双引号括起来的单词,例如“ welcome lucene”。 可以将多个术语与布尔运算符组合在一起以形成更复杂的查询。 对于Lucene Java,TermQuery是最原始的查询。 然后是BooleanQuery,PhraseQuery和许多其他Query子类可供选择。
字段执行搜索时,我们可以指定要搜索的字段。任何现有的字段名称都可以用作字段名称。 语法为FieldName:VALUE 。 有一些特殊的字段类型具有自己的语法来定义查询词。 例如,DateTime:ModificationDate:>'2010-09-01 12:00:00'我们将在后面的部分中解释对这些字段的搜索操作。
3,Lucene查询API
当Lucene的QueryParser
解析人类可读的查询时,它将转换为Query类的单个具体子类。 我们需要对基础的具体Query子类有所了解。 下表列出了相关的子类,它们的用途以及一些示例表达式:
查询实施 | 目的 | 样本表达 |
---|---|---|
TermQuery | 单项查询,实际上是一个单词。 | 雷诺 |
PhraseQuery | 多个项按顺序排列或彼此接近的匹配项 | “前方点亮” |
RangeQuery | 用开始和结束词之间的词匹配文档,包括或不包括端点。 | [A到Z] {A到Z} |
WildcardQuery | 轻量级的,类似于正则表达式的术语匹配语法。 | j * v? f ?? bar |
PrefixQuery | 匹配以指定字符串开头的所有术语。 | 起司* |
FuzzyQuery | Levenshtein紧密匹配算法。 | 树〜 |
BooleanQuery | 将其他查询实例聚合为允许AND,OR和NOT逻辑的复杂表达式。 | 雷诺和“前方点亮” 奶酪*-奶酪 |
所有这些Query
实现都在org.apache.lucene.search
包中。 BooleanQuery
是一种特殊情况,因为它是一个聚合其他查询(包括用于复杂表达式的嵌套BooleanQuery
)的Query
容器。
这是一个基于查询片段的BooleanQuery
。 在这里,我们可以看到QueryParser创建的查询与API创建的查询等效:
public class RulezTest extends TestCase { public void testJavaNotDotNet() throws Exception { BooleanQuery apiQuery = new BooleanQuery(); apiQuery.add(new TermQuery(new Term("contents", "java")), true, false); apiQuery.add(new TermQuery(new Term("contents", "net")), true, false); apiQuery.add(new TermQuery(new Term("contents", "dot")), false, true); Query qpQuery = QueryParser.parse("java AND net NOT dot", "contents", new StandardAnalyzer()); // Query and subclasses behave as expected with .equals assertEquals(qpQuery, apiQuery); }
}
Query
类的一些有趣的功能是它们的toString
方法。 每个Query
子类都会生成等效的QueryParserexpression
(尽管不一定在文本上精确)。 有两种变体:一种是标准的Object.toString
重写方法,另一种接受默认字段名称。 下面的测试案例演示了这两种方法的工作原理,并说明了如何返回等效(但不是确切)的表达式。
public void testToString() throws Exception { Query query = QueryParser.parse("java AND net NOT dot", "contents", new StandardAnalyzer()); assertEquals("+java +net -dot", query.toString("contents")); assertEquals("+contents:java +contents:net -contents:dot", query.toString());
}
注意,解析的表达式是“ java AND net NOT dot”,但是从toString
方法返回的表达式使用了缩写语法“ + java + net -dot”。 我们的第一个测试用例( testJavaNotDotNet
)证明了底层查询对象本身是等效的。
no-arg toString
方法不对每个术语的字段名称做任何假设,并使用字段选择器语法明确指定它们。 使用这些toString
方法可方便地诊断QueryParser
问题。
4,基本搜索
在大多数情况下,您要查找单个术语或短语,即由双引号引起的一组单词(“示例应用程序”)。 在这些情况下,我们将在其默认索引数据中查找包含这些单词的内容,这些内容包含内容的所有相关文本。
在更复杂的情况下,我们可能需要根据要查找的内容的类型或位置进行某些过滤,或者我们要在特定字段中进行搜索。 在这里,我们可以学习如何构建更复杂的查询,这些查询可用于有效地在大型存储库中查找内容。
4.1。条款
假设我们要在标签字段中使用关键字“博客”进行搜索。 语法将是
tag : blog
现在,我们将在标签字段中使用短语“ lucene blog”进行搜索。 为此,语法将是
tag : "lucene blog"
现在,让我们在标签字段中搜索“ lucene blog”,然后在正文中搜索“ technical blog”,
tag : "lucene blog" AND body : "technical blog"
假设我们要在标签字段中搜索短语“ lucene blog”,在正文中搜索“技术博客”,或者在标签字段中搜索“ searching blog”,
(tag : "lucene blog" AND body : "technical blog") OR tag : "searching blog"
如果我们要在标记字段中搜索“博客”而不是“ lucene”,则语法看起来会相似,
tag : blog -tag : lucene
4.2。通配符查询
Lucene支持在单个术语内(而不是在短语查询中)单字符和多字符通配符搜索。
- 要执行单个字符通配符搜索,请使用“?” 符号。
- 要执行多字符通配符搜索,请使用“ *”符号。
单字符通配符搜索查找与替换了单个字符的词匹配的术语。 例如,要搜索“文本”或“测试”,我们可以使用搜索:te?t
多字符通配符搜索将查找0个或多个字符。 例如,要搜索测试,测试或测试员,我们可以使用搜索:
test*
我们还可以在术语中间使用通配符搜索。
te*t
这是Lucene通配符搜索的示例,
假设我们在“文件”目录中有两个文件。
- test-foods.txt
以下是Deron喜欢的一些食物:
hamburger
french fries
steak
mushrooms
artichokes
- sample-food.txt
以下是妮可喜欢的一些食物:
apples
bananas
salad
mushrooms
cheese
现在,我们看一下LuceneWildcardQueryDemo
类。 此类基于上述文本文件通过createIndex()
方法创建索引,此后,它尝试对该索引执行8个通配符搜索。 使用WildcardQuery
类执行其中四个搜索,而使用QueryParser
类执行其他四个搜索。
首先使用createIndex()
方法对以上两个文件建立索引。
public static void createIndex() throws CorruptIndexException, LockObtainFailedException, IOException {Analyzer analyzer = new StandardAnalyzer();boolean recreateIndexIfExists = true;IndexWriter indexWriter = new IndexWriter(INDEX_DIRECTORY, analyzer, recreateIndexIfExists);File dir = new File(FILES_TO_INDEX_DIRECTORY);File[] files = dir.listFiles();for (File file : files) {Document document = new Document();String path = file.getCanonicalPath();document.add(new Field(FIELD_PATH, path, Field.Store.YES, Field.Index.UN_TOKENIZED));Reader reader = new FileReader(file);document.add(new Field(FIELD_CONTENTS, reader));indexWriter.addDocument(document);}indexWriter.optimize();indexWriter.close();
}
为了使用查询解析器执行搜索操作,我们可以添加一个名为searchIndexWithQueryParser()
的方法,
public static void searchIndexWithQueryParser(String whichField, String searchString) throws IOException,ParseException {System.out.println("\\nSearching for '" + searchString + "' using QueryParser");Directory directory = FSDirectory.getDirectory(INDEX_DIRECTORY);IndexSearcher indexSearcher = new IndexSearcher(directory);QueryParser queryParser = new QueryParser(whichField, new StandardAnalyzer());Query query = queryParser.parse(searchString);System.out.println("Type of query: " + query.getClass().getSimpleName());Hits hits = indexSearcher.search(query);displayHits(hits);
}
使用以下代码在searchIndexWithWildcardQuery()
方法中执行WildcardQuery
查询:
Directory directory = FSDirectory.getDirectory(INDEX_DIRECTORY); IndexSearcher indexSearcher = new IndexSearcher(directory); Term term = new Term(whichField, searchString); Query query = new WildcardQuery(term); Hits hits = indexSearcher.search(query);
QueryParser
查询通过以下方式在searchIndexWithQueryParser()
方法中执行:
Directory directory = FSDirectory.getDirectory(INDEX_DIRECTORY); IndexSearcher indexSearcher = new IndexSearcher(directory); QueryParser queryParser = new QueryParser(whichField, new StandardAnalyzer()); Query query = queryParser.parse(searchString); Hits hits = indexSearcher.search(query);
从我们的main()方法可以看到, LuceneWildcardQueryDemo
类执行八个通配符搜索:
searchIndexWithWildcardQuery(FIELD_CONTENTS, "t*t"); searchIndexWithQueryParser(FIELD_CONTENTS, "t*t"); searchIndexWithWildcardQuery(FIELD_CONTENTS, "sam*"); searchIndexWithQueryParser(FIELD_CONTENTS, "sam*"); searchIndexWithWildcardQuery(FIELD_CONTENTS, "te?t"); searchIndexWithQueryParser(FIELD_CONTENTS, "te?t"); searchIndexWithWildcardQuery(FIELD_CONTENTS, "*est"); try { searchIndexWithQueryParser(FIELD_CONTENTS, "*est"); } catch (ParseException pe) { pe.printStackTrace(); }
最后,我们将使用类似的方法打印每次搜索操作的命中数,
public static void displayHits(Hits hits) throws CorruptIndexException, IOException {System.out.println("Number of hits: " + hits.length());Iterator<Hit> it = hits.iterator();while (it.hasNext()) {Hit hit = it.next();Document document = hit.getDocument();String path = document.get(FIELD_PATH);System.out.println("Hit: " + path);}
}
如果我们运行上面的代码,它将向我们显示,
Searching for 't*t' using WildcardQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/test-foods.txt
Searching for 't*t' using QueryParser
Type of query: WildcardQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/test-foods.txt
Searching for 'sam*' using WildcardQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/sample-foods.txt
Searching for 'sam*' using QueryParser
Type of query: PrefixQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/sample-foods.txt
Searching for 'te?t' using WildcardQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/test-foods.txt
Searching for 'te?t' using QueryParser
Type of query: WildcardQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/test-foods.txt
Searching for '*est' using WildcardQuery
Number of hits: 1
Hit: /home/debarshi/workspace/Test/filesToIndex/test-foods.txt
Searching for '*est' using QueryParserorg.apache.lucene.queryParser.ParseException: Cannot parse '*est': '*' or '?' not allowed as first character in WildcardQuery at org.apache.lucene.queryParser.QueryParser.parse(QueryParser.java:175) at LuceneWildcardQueryDemo.searchIndexWithQueryParser(LuceneWildcardQueryDemo.java:81) at LuceneWildcardQueryDemo.main(LuceneWildcardQueryDemo.java:46)
- 第一个查询使用带有“ t * t”的
WildcardQuery
对象。 由于“ t * t”与索引中的“ test”匹配,因此此查询返回1次匹配。 - 第二个查询使用
QueryParser
查询“ t * t”。 QueryParserparse()
方法返回WildcardQuery
,并且该查询返回1个匹配项,因为它与第一个查询基本相同。 - 第三个查询使用带有“ sam *”的
WildcardQuery
对象。 由于“ sam *”与“ sample”匹配,因此该通配符查询获得了成功。 - 第四个查询使用带有“ sam *”的
QueryParser
。 但是请注意,QueryParser的parse()
方法返回PrefixQuery
而不是WildcardQuery
。 由于星号在“ sam *”的末尾。 由于“ sam *”与“ sample”匹配,因此该针对“ sam *”的PrefixQuery命中。 - 第五个查询是
WildcardQuery
,它在其搜索词“ te?t”中使用问号。 问号可以匹配一个字符。 由于“ te?t”与“ test”匹配,因此搜索返回1个匹配项。 - 第六个查询使用带有“ te?t”的
QueryParser
。 QueryParserparse()
方法返回WildcardQuery
,并且与第五个查询一样,该命中率WildcardQuery
。 - 第七个查询是“ * est”的
WildcardQuery
查询。 由于“ test”匹配“ * est”,因此它收到一个匹配。 通常,在第一个字符为通配符的情况下执行查询不是一个好主意。 - 第八个查询是“ * est”的
QueryParser
查询。 请注意,QueryParser
对象甚至不允许我们执行第一个字符为星号的查询。 引发解析异常。
4.3。布尔运算符
布尔运算符允许通过逻辑运算符组合术语。 Lucene支持AND,“ +”,OR,NOT和“-”作为布尔运算符(注意:布尔运算符必须为ALL CAPS)。
OR运算符是默认的合取运算符。 这意味着如果两个术语之间没有布尔运算符,则使用OR运算符。 OR运算符链接两个术语,如果文档中存在两个术语中的任何一个,则查找匹配的文档。 这等效于使用集合的并集。 符号|| 可以代替单词OR。
要搜索包含“ jakarta apache”或仅包含“ jakarta”的文档,请使用查询:
"jakarta apache" jakarta
要么
"jakarta apache" OR jakarta
和
AND运算符匹配两个术语都存在于单个文档的文本中任意位置的文档。 这等效于使用集合的交点。 可以使用符号&&代替单词AND。
要搜索包含“ jakarta apache”和“ Apache Lucene”的文档,请使用查询:
"jakarta apache" AND "Apache Lucene"
+
“ +”或必需的运算符要求“ +”符号后的术语存在于单个文档的字段中。
要搜索必须包含“ jakarta”且可能包含“ lucene”的文档,请使用以下查询:
+jakarta lucene
不
NOT运算符排除包含NOT之后的术语的文档。 这等效于使用集的区别。 符号! 可以代替“非”一词使用。
要搜索包含“ jakarta apache”但不包含“ Apache Lucene”的文档,请使用以下查询:
"jakarta apache" NOT "Apache Lucene"
注意:NOT运算符不能仅使用一个术语。 例如,以下搜索将不返回任何结果:
NOT "jakarta apache"
“-”
“-”或禁止运算符排除包含在“-”符号后的术语的文档。
要搜索包含“ jakarta apache”但不包含“ Apache Lucene”的文档,请使用以下查询:
"jakarta apache" -"Apache Lucene"
4.3分组
Lucene支持使用括号将子句分组以形成子查询。 如果要控制查询的布尔逻辑,这可能非常有用。
要搜索“ jakarta”或“ apache”和“网站”,请使用查询:
(jakarta OR apache) AND website
这样可以消除任何混乱,并确保您必须存在该网站,并且可能存在“雅加达”或“ apache”一词。
现场分组
Lucene支持使用括号将多个子句分组到一个字段中。
要搜索包含单词“ return”和短语“ pink panther”的标题,请使用以下查询:
title:(+return +"pink panther")
转义特殊字符
Lucene支持转义查询语法中包含的特殊字符。 当前列表的特殊字符为:
+ – && || ! (){} [] ^”〜*吗? :\
要转义这些字符,请在字符前使用“ \”(反斜杠)。 例如,要搜索(1 + 1):2,请使用查询:
\\(1\\+1\\)\\:2
4.4。短语查询
Lucene中的PhraseQuery
匹配包含特定术语序列的文档。 PhraseQuery
使用存储在索引中的术语的位置信息。
查询短语中单词之间允许的其他单词的数量称为“斜率”。 可以通过调用setSlop方法进行设置。 如果为零,则为精确短语搜索。 对于较大的值,其工作方式类似于WITHIN或NEAR运算符。
斜率实际上是一个编辑距离,其中单位对应于查询短语中词条移动的位置。 例如,要切换两个单词的顺序需要两个步骤(第一个步骤将单词彼此放在首位),因此要允许对短语进行重新排序,斜率必须至少为两个。
较精确的匹配比不精确的匹配得分更高,因此搜索结果按精确性排序。 默认情况下,斜率为零,要求完全匹配。
PhraseQuery
还支持多个术语短语。
短语查询可以与其他术语组合,也可以与BooleanQuery
组合使用。 默认情况下,子句的最大数量限制为1,024。
在前面的Lucene通配符查询示例中,我们已经基于两个文本文件完成了搜索操作。 现在,我们将尝试在Lucene中使用PhraseQuery查找匹配的短语。
为此,我们将引入一个新方法searchIndexWithPhraseQuery()
代替searchIndexWithWildcardQuery()
方法,该方法采用两个字符串表示文档中的单词和一个倾斜值。 它通过基于“ contents”字段以及string1和string2参数添加两个Term对象来构造PhraseQuery
。 然后,它使用setSlop()
方法设置PhraseQuery
对象的setSlop()
值。 通过将PhraseQuery
对象传递给IndexSearcher的search()
方法来进行search()
。 这是代码,
public static void searchIndexWithPhraseQuery(String string1, String string2, int slop) throws IOException,ParseException {Directory directory = FSDirectory.getDirectory(INDEX_DIRECTORY);IndexSearcher indexSearcher = new IndexSearcher(directory);Term term1 = new Term(FIELD_CONTENTS, string1);Term term2 = new Term(FIELD_CONTENTS, string2);PhraseQuery phraseQuery = new PhraseQuery();phraseQuery.add(term1);phraseQuery.add(term2);phraseQuery.setSlop(slop);displayQuery(phraseQuery);Hits hits = indexSearcher.search(phraseQuery);displayHits(hits);
}
而且,我们从main()
调用此方法,
searchIndexWithPhraseQuery("french", "fries", 0);searchIndexWithPhraseQuery("hamburger", "steak", 0);searchIndexWithPhraseQuery("hamburger", "steak", 1);searchIndexWithPhraseQuery("hamburger", "steak", 2);searchIndexWithPhraseQuery("hamburger", "steak", 3);searchIndexWithQueryParser("french fries"); // BooleanQuerysearchIndexWithQueryParser("\\"french fries\\""); // PhaseQuerysearchIndexWithQueryParser("\\"hamburger steak\\"~1"); // PhaseQuerysearchIndexWithQueryParser("\\"hamburger steak\\"~2"); // PhaseQuery
第一个查询以斜率0搜索“ french”和“ fries”,这意味着短语搜索最终是对“ french fries”的搜索,其中“ french”和“ fries”彼此相邻。 由于这存在于test-foods.txt中,因此我们获得了1次点击。
在第二个查询中,我们搜索坡度为0的“汉堡”和“牛排”。由于在两个文档中都不存在“汉堡”和“牛排”,因此得到0个匹配。 第三个查询还涉及对“汉堡包”和“牛排”的搜索,但斜率为1。这些单词彼此之间的距离不超过1个单词,因此我们获得0次匹配。
第四个查询以“ 2”的斜率搜索“汉堡”和“牛排”。在test-foods.txt文件中,我们有“……汉堡薯条……”字样。 由于“汉堡”和“牛排”彼此之间不超过两个字,因此我们获得1分。 第五个短语查询是相同的搜索,但斜率为3。由于“汉堡包”和“牛排”彼此带有三个单词(彼此是两个单词),因此命中率为1。
接下来的四个查询使用QueryParser
。 注意,在第一个QueryParser
查询中,我们得到一个BooleanQuery
而不是PhraseQuery
。 这是因为我们传递了QueryParser的parse()
方法“炸薯条”而不是“ \”炸薯条\””。 如果我们希望QueryParser生成PhraseQuery,则搜索字符串需要用双引号引起来。 下一个查询确实会搜索“ \”炸薯条\”,并且我们可以看到它生成了一个PhraseQuery
(默认PhraseQuery
为0),并响应该查询获得了1次PhraseQuery
。
最后两个QueryParser
查询演示了设置倾斜值。 我们可以看到,可以在搜索字符串的双引号后面设置斜率值,并在其后加上斜线号(〜)和斜率号。
4.5。范围查询
与专有术语范围内的文档匹配的Query
。 它允许匹配字段值在RangeQuery
指定的上下限之间的RangeQuery
。 范围查询可以包含上限或下限,也可以不包括上限和下限。 排序是按字典顺序进行的(按字典顺序排列(排序)的项的集合)。
现在,如果要为Lucene搜索操作实现RangeQuery
,则必须添加一个名为searchIndexWithRangeQuery()
,该方法基本上是一个构造函数,需要一个Term
指示范围的开始,一个Term
指示范围的结束和一个布尔值,指示搜索是包含开始和结束值(“ true”)还是排除开始和结束值(“ false”)。 代码看起来像
public static void searchIndexWithRangeQuery(String whichField, String start, String end, boolean inclusive)throws IOException, ParseException {System.out.println("\\nSearching for range '" + start + " to " + end + "' using RangeQuery");Directory directory = FSDirectory.getDirectory(INDEX_DIRECTORY);IndexSearcher indexSearcher = new IndexSearcher(directory);Term startTerm = new Term(whichField, start);Term endTerm = new Term(whichField, end);Query query = new RangeQuery(startTerm, endTerm, inclusive);Hits hits = indexSearcher.search(query);displayHits(hits);
}
现在我们将调用上述方法,
searchIndexWithRangeQuery(FIELD_LAST_MODIFIED, "2014-04-01-00-00-00", "2014-04-01-23-59-59", INCLUSIVE);searchIndexWithRangeQuery(FIELD_LAST_MODIFIED, "2014-04-02-00-00-00", "2014-04-02-23-59-59", INCLUSIVE);searchIndexWithRangeQuery(FIELD_LAST_MODIFIED, "2014-04-01-00-00-00", "2014-04-01-21-21-02", INCLUSIVE);searchIndexWithRangeQuery(FIELD_LAST_MODIFIED, "2014-04-01-00-00-00", "2014-04-01-21-21-02", EXCLUSIVE);// equivalent range searches using QueryParsersearchIndexWithQueryParser(FIELD_LAST_MODIFIED, "[2014-04-01-00-00-00 TO 2014-04-01-23-59-59]");searchIndexWithQueryParser(FIELD_LAST_MODIFIED, "[2014-04-02-00-00-00 TO 2014-04-02-23-59-59]");searchIndexWithQueryParser(FIELD_LAST_MODIFIED, "[2014-04-01-00-00-00 TO 2014-04-01-21-21-02]");searchIndexWithQueryParser(FIELD_LAST_MODIFIED, "{2014-04-01-00-00-00 TO 2014-04-01-21-21-02}");
最后, createIndex()
方法稍有变化。 我们添加了一些实现日期时间的操作并打印了索引文件的最后修改时间。
在控制台输出的顶部,我们可以看到两个文件都已建立索引,并且这些文件的“最后修改”时间为“ 2014-04-01-21-21-02”(对于test-foods.txt )和“ 2014-04-01-21-21-38”(针对sample-foods.txt)。
在第一个范围查询中,我们搜索所有在2014年4月1日最后一次修改的文件。由于两个文件在该日期最后一次修改,这将返回2次匹配。 在第二个范围查询中,我们搜索所有在2014年4月2日最后一次修改的文件。由于两个文档都在2014年4月1日最后一次修改,因此返回0次匹配。
接下来,我们在2014年4月1日至2014年4月1日至2014年4月1日的范围内进行搜索。 由于test-foods.txt的上次修改时间为2014-04-01-21-21-02,并且范围查询包含此值,因此我们获得了一个搜索结果。 之后,我们仅在2014-04-01-00-00-00到2014-04-01-21-21-02之间进行搜索。 由于test-foods.txt的上次修改时间为2014-04-01-21-21-02,并且范围查询不包含此值(因为已将其排除在外),因此此搜索返回0个匹配。
此后,接下来的四个搜索显示使用QueryParser
对象执行的等效搜索。 请注意,对于每个查询,QueryParser的parse()
方法都返回ConstantScoreRangeQuery
对象而不是RangeQuery
对象,正如我们从这些查询的控制台输出中看到的那样。
4.6。前缀查询
与包含带有指定前缀的术语的文档匹配的Query
。 PrefixQuery
由QueryParser
构建,用于类似nam *的输入。
我们将尝试使用两个文本文件(test-foods.txt和sample-foods.txt)通过前缀查询来搜索带有其前缀的特定术语。
对于这样做,我们将增加一个名为方法searchIndexWithPrefixQuery()
将搜索器的索引(这将创建createIndex()
使用PrefixQuery
。 此方法有两个参数,一个是字段名,另一个是搜索字符串。
public static void searchIndexWithPrefixQuery(String whichField, String searchString) throws IOException,ParseException {System.out.println("\\nSearching for '" + searchString + "' using PrefixQuery");Directory directory = FSDirectory.getDirectory(INDEX_DIRECTORY);IndexSearcher indexSearcher = new IndexSearcher(directory);Term term = new Term(whichField, searchString);Query query = new PrefixQuery(term);Hits hits = indexSearcher.search(query);displayHits(hits);
}
接下来,我们将从程序的main()
方法中调用此方法–
searchIndexWithPrefixQuery(FIELD_CONTENTS, "test");searchIndexWithPrefixQuery(FIELD_CONTENTS, "tes*");
在第一个查询中,查询字符串不包含星号。 因此,如果我们打印QueryParser的parse()
方法的查询类型,它将打印TermQuery
而不是PrefixQuery
。
在第二查询中,星号向QueryParser
指示这是一个前缀查询,因此它从其parse()
方法返回PrefixQuery
对象。 这导致在索引内容中搜索前缀“ tes”。 由于“ test”在索引中,因此产生1次匹配。
4.7。模糊查询
模糊查询基于Damerau-Levenshtein(最佳字符串对齐)算法。 FuzzyQuery
将术语“接近”匹配到指定的基本术语:我们指定了一个允许的最大编辑距离,并且在与基本术语(然后包含这些术语的文档)相距该编辑距离内的所有术语都匹配。
QueryParser
语法是QueryParser
或QueryParser
,其中N是允许的最大编辑数量(对于较早的发行版,N是0.0到1.0之间的令人困惑的浮点数,它通过一个棘手的公式转换为等效的最大编辑距离)。
FuzzyQuery
非常适合匹配专有名称:我们可以搜索lucene〜1,它将匹配luccene(插入c),lucee(删除n),lukene(用k替换c)和许多其他“接近”术语。 使用最大编辑距离2,我们最多可以有2个插入,删除或替换。 每次比赛的得分均基于该词的编辑距离; 因此完全匹配的得分最高; 编辑距离1,降低; 等等
QueryParser
支持在词条上使用尾随波浪号的模糊词条查询。 例如,搜索wuzza〜将找到包含“ fuzzy”和“ wuzzy”的文档。 编辑距离会影响得分,例如,较低的编辑距离会得分较高。
翻译自: https://www.javacodegeeks.com/2015/09/advanced-lucene-query-examples.html