2012-01-30 13 views
5

Usiłuję utworzyć możliwy do wyszukania telefon/lokalny katalog firmowy przy użyciu Apache Lucene.Lucene: Wyrażenia składające się z wielu wyrazów jako kryteria wyszukiwania

Mam pola dla nazwy ulicy, nazwy firmy, numeru telefonu itp. Problem, który mam, polega na tym, że gdy próbuję wyszukiwać według ulicy, gdzie ulica ma wiele słów (np. "Półksiężyc"), nie ma wyniki są zwracane. Ale jeśli spróbuję wyszukać za pomocą tylko jednego słowa, np. "Półksiężyca", otrzymam wszystkie wyniki, które chcę.

jestem indeksowania danych z następujących czynności:

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(); 

moich poszukiwań działa tak:

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; 

Próbowałem swapping zapytanie wieloznaczny dla zapytania frazy, najpierw z całą łańcuch, a następnie dzielenie łańcucha na białą przestrzeń i zawijanie ich w BooleanQuery w następujący sposób:

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); 

To jednak nie zadziałało. Próbowałem użyć KeywordAnalyzer zamiast StandardAnalyzer, ale wtedy wszystkie inne typy wyszukiwania również przestały działać. Próbowałem zastąpić spacje innymi znakami (+ i @) i konwertować zapytania do iz tego formularza, ale to nadal nie działa. Myślę, że to nie działa, ponieważ znaki + i @ są znakami specjalnymi, które nie są indeksowane, ale nie mogę znaleźć listy gdziekolwiek tego rodzaju postaci.

Zaczynam szaleć, czy ktoś wie, co robię źle?

Dzięki Rik

+0

specjalny charakter można znaleźć tutaj: http://lucene.apache.org/core/3_5_0/queryparsersynta x.html # N10180. – Oliver

Odpowiedz

5

odkryłem, że moja próba wygenerowania zapytania bez użycia QueryParser nie działa, więc przestałem próbuje tworzyć własne zapytania i używane QueryParser zamiast. Wszystkie rekomendacje, które zobaczyłem w Internecie, pokazały, że powinieneś użyć tego samego analizatora w QueryParser, którego używasz podczas indeksowania, więc użyłem narzędzia StandardAnalyzer do zbudowania QueryParser.

Działa to w tym przykładzie, ponieważ narzędzie StandardAnalyzer usuwa słowo "the" z ulicy "półksiężyc" podczas indeksowania, a zatem nie możemy go wyszukać, ponieważ nie znajduje się w indeksie.

Jeśli jednak zdecydujemy się wyszukać "Grove Road", mamy problem z natychmiastową funkcjonalnością, mianowicie że zapytanie zwróci wszystkie wyniki zawierające "Gaj" LUB "Drogę" ". Można to łatwo naprawić, konfigurując QueryParser tak, aby jego domyślną operacją był AND i zamiast OR.

W końcu prawidłowe rozwiązanie było następujące:

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; 
+1

Usunięcie słów zatrzymania z nazw ulic jest nieprawidłowe. Pomyśl o nazwach takich jak [Both Street] (http://g.co/maps/r5rnc). Jestem pewien, że możesz znaleźć bardziej żywe przykłady. Po prostu dlaczego usunąć coś, jeśli to nie ma sensu? –

11

Powodem dlaczego nie dostać dokumenty z powrotem jest to, że podczas indeksowania używasz StandardAnalyzer, który konwertuje znaki na małe litery i usuwa zatrzymać słów. Więc jedynym terminem, który zostanie zindeksowany na twój przykład, jest "półksiężyc". Jednak kwerendy typu wildcard nie są analizowane, więc "the" jest uwzględniane jako obowiązkowa część zapytania. To samo dotyczy zapytań do fraz w swoim scenariuszu.

KeywordAnalyzer prawdopodobnie nie jest zbyt odpowiedni dla twojego przypadku użycia, ponieważ zajmuje całą zawartość pola jako pojedynczy token. Możesz użyć SimpleAnalyzer dla pola ulicy - podzieli on dane wejściowe na wszystkie znaki inne niż litery, a następnie przekształci je w małe litery. Możesz również rozważyć użycie WhitespaceAnalyzer z LowerCaseFilter. Musisz wypróbować różne opcje i ustalić, które rozwiązanie najlepiej pasuje do danych i użytkowników.

Można również użyć różnych analizatorów dla każdego pola (np. Z PerFieldAnalyzerWrapper), jeśli zmiana analizatora dla tego pola spowoduje przerwanie innych wyszukiwań.

0

Jeśli chcesz, aby dokładne słowa pasowały do ​​ulicy, możesz ustawić pole "Ulica" NOT_ANALYZED, które nie będzie filtrować słowa zatrzymania "the".

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

To nie jest dobre rozwiązanie - w ten sposób zawsze będziesz musiał wpisać "the" w zapytaniu, aby uzyskać ten wynik. –

+0

@Artur Nowak: Głosuj swoją odpowiedz w górę. Odpowiedni analizator to punkt. –

0

Nie ma potrzeby stosowania żadnych Analyzer tutaj coz Hibernate domyślnie używa StandardAnalyzer który podzieli słowa oparte na white spaces tak tu rozwiązaniem jest ustawiony Analyze do NO zostanie ona automatycznie wykonuje Multi Phrase Search

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