我正在尝试使用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,但随后所有其他类型的搜索也停止了。我已经尝试用其他字符(+和@)替换空格,并将查询转换为此表单,但仍然无效。我认为它不起作用,因为+和@是没有索引的特殊字符,但我似乎找不到任何字符都是这样的列表。
我开始有点生气,有人知道我做错了吗?
谢谢, 瑞克
答案 0 :(得分:13)
你没有收到你的文件的原因是,在索引时你正在使用StandardAnalyzer
,它将标记转换为小写并删除停用词。因此,为您的示例编制索引的唯一术语是“新月”。但是,不会分析通配符查询,因此“the”包含在查询的必需部分中。您的方案中的短语查询也是如此。
KeywordAnalyzer
可能不太适合您的用例,因为它将整个字段内容作为单个标记。您可以将SimpleAnalyzer
用于街道字段 - 它会将输入拆分为所有非字母字符,然后将其转换为小写字母。您还可以考虑将WhitespaceAnalyzer
与LowerCaseFilter
一起使用。您需要尝试不同的选项,找出最适合您的数据和用户的选项。
此外,如果更改该字段的分析器会破坏其他搜索,则每个字段可以使用不同的分析器(例如,使用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;