Lucene:多词短语作为搜索词

我正在尝试使用Apache Lucene创建一个可搜索的电话/本地商业目录。

我有街道名称,公司名称,电话号码等字段。我遇到的问题是,当我尝试在街道上搜索街道名称有多个单词(例如“新月”)时,不会返回任何结果。 但如果我尝试用一​​个单词搜索,例如’新月’,我会得到我想要的所有结果。

我正在使用以下内容索引数据:

String LocationOfDirectory = "C:\\dir\\index"; StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34); Directory Index = new SimpleFSDirectory(LocationOfDirectory); IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE.34, analyzer); IndexWriter w = new IndexWriter(index, config); Document doc = new Document(); doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Analyzed); w.add(doc); w.close(); 

我的搜索工作如下:

 int numberOfHits = 200; String LocationOfDirectory = "C:\\dir\\index"; TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true); Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory)); IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory); WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent"); searcher.search(q, collector); ScoreDoc[] hits = collector.topDocs().scoreDocs; 

我已经尝试交换通配符查询以进行短语查询,首先使用整个字符串,然后将字符串拆分为空格并将它们包装在BooleanQuery中,如下所示:

 String term = "the crescent"; BooleanQuery b = new BooleanQuery(); PhraseQuery p = new PhraseQuery(); String[] tokens = term.split(" "); for(int i = 0 ; i < tokens.length ; ++i) { p.add(new Term("Street", tokens[i])); } b.add(p, BooleanClause.Occur.MUST); 

但是,这不起作用。 我尝试使用KeywordAnalyzer而不是StandardAnalyzer,但随后所有其他类型的搜索也停止了。 我尝试用其他字符(+和@)替换空格,并将查询转换为此表单,但仍然无效。 我认为它不起作用,因为+和@是没有索引的特殊字符,但我似乎无法找到任何字符都是这样的列表。

我开始有点生气,有谁知道我做错了什么?

谢谢,Rik

你没有得到你的文件的原因是,在索引时你正在使用StandardAnalyzer ,它将标记转换为小写并删除停用词。 因此,为您的示例编制索引的唯一术语是“新月”。 但是,不分析通配符查询,因此’the’作为查询的必需部分包含在内。 您的方案中的短语查询也是如此。

KeywordAnalyzer可能不太适合您的用例,因为它将整个字段内容作为单个标记。 您可以将SimpleAnalyzer用于街道字段 – 它将在所有非字母字符上拆分输入,然后将它们转换为小写字母。 您还可以考虑将WhitespaceAnalyzerLowerCaseFilter一起LowerCaseFilter 。 您需要尝试不同的选项,并找出最适合您的数据和用户的选项。

此外,如果更改该字段的分析器会破坏其他搜索, PerFieldAnalyzerWrapper每个字段使用不同的分析器(例如,使用PerFieldAnalyzerWrapper )。

我发现我不使用QueryParser生成查询的尝试不起作用,所以我不再尝试创建自己的查询并使用QueryParser。 我在网上看到的所有建议表明你应该在索引期间使用的QueryParser中使用相同的Analyzer,所以我使用StandardAnalyzer来构建QueryParser。

这适用于此示例,因为StandardAnalyzer在索引期间从街道“the crescent”中删除单词“the”,因此我们无法搜索它,因为它不在索引中。

但是,如果我们选择搜索“Grove Road”,我们就会遇到开箱即用function的问题,即查询将返回包含“Grove”或“Road”的所有结果。 通过设置QueryParser可以很容易地解决这个问题,因此它的默认操作是AND而不是OR。

最后,正确的解决方案如下:

 int numberOfHits = 200; String LocationOfDirectory = "C:\\dir\\index"; TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true); Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory)); IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory); StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_35); //WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent"); QueryParser qp = new QueryParser(Version.LUCENE_35, "Street", analyzer); qp.setDefaultOperator(QueryParser.Operator.AND); Query q = qp.parse("grove road"); searcher.search(q, collector); ScoreDoc[] hits = collector.topDocs().scoreDocs; 

如果你想要一个精确的单词匹配街道,你可以设置字段“街道”NOT_ANALYZED,它不会过滤停止单词“the”。

 doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Not_Analyzed); 

这里不需要使用任何Analyzer因为Hibernate隐式使用了StandardAnalyzer ,它会根据white spaces分割单词,所以这里的解决方案将Analyze设置为NO它将自动执行Multi Phrase Search

  @Column(name="skill") @Field(index=Index.YES, analyze=Analyze.NO, store=Store.NO) @Analyzer(definition="SkillsAnalyzer") private String skill;