Lucene:多词短语作为搜索词

时间:2012-01-30 15:31:51

标签: java search 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,但随后所有其他类型的搜索也停止了。我已经尝试用其他字符(+和@)替换空格,并将查询转换为此表单,但仍然无效。我认为它不起作用,因为+和@是没有索引的特殊字符,但我似乎找不到任何字符都是这样的列表。

我开始有点生气,有人知道我做错了吗?

谢谢, 瑞克

4 个答案:

答案 0 :(得分:13)

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

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

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

答案 1 :(得分:7)

我发现我在不使用QueryParser的情况下生成查询的尝试无效,因此我不再尝试创建自己的查询并使用QueryParser。我在网上看到的所有建议都显示您应该在索引编制过程中使用的QueryParser中使用相同的Analyzer,因此我使用StandardAnalyzer构建QueryParser。

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

但是,如果我们选择搜索“Grove Road”,我们就会遇到开箱即用功能的问题,即查询将返回包含“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;

答案 2 :(得分:-1)

如果您想要与街道匹配的确切字词,您可以设置字段“街道”NOT_ANALYZED,它不会过滤停用词“the”。

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

答案 3 :(得分:-1)

此处无需使用任何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;