2012-01-30 49 views
5

Tôi đang cố gắng tạo danh bạ doanh nghiệp/địa phương có thể tìm kiếm bằng cách sử dụng Apache Lucene.Lucene: Cụm từ nhiều từ như cụm từ tìm kiếm

Tôi có các trường tên đường, tên doanh nghiệp, số điện thoại, v.v. Vấn đề tôi gặp phải là khi tôi cố gắng tìm kiếm theo đường phố nơi tên phố có nhiều từ (ví dụ: 'lưỡi liềm'), không kết quả được trả về. Nhưng nếu tôi cố gắng tìm kiếm chỉ với một từ, ví dụ như 'lưỡi liềm', tôi nhận được tất cả các kết quả mà tôi muốn.

Tôi đang lập chỉ mục dữ liệu như sau:

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

tìm kiếm của tôi làm việc như thế này:

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; 

Tôi đã cố gắng trao đổi các truy vấn wildcard cho một truy vấn cụm từ, đầu tiên với toàn bộ chuỗi và sau đó tách chuỗi trên không gian màu trắng và gói chúng trong một BooleanQuery như thế này:

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

Tuy nhiên, điều này không hoạt động. Tôi đã thử sử dụng KeywordAnalyzer thay vì StandardAnalyzer, nhưng sau đó tất cả các loại tìm kiếm khác cũng ngừng hoạt động. Tôi đã thử thay thế không gian bằng các ký tự khác (+ và @) và chuyển đổi truy vấn đến và từ biểu mẫu này, nhưng điều đó vẫn không hoạt động. Tôi nghĩ rằng nó không hoạt động vì + và @ là các ký tự đặc biệt không được lập chỉ mục, nhưng tôi dường như không thể tìm thấy danh sách ở bất kỳ đâu có ký tự giống như vậy.

Tôi bắt đầu phát điên, có ai biết tôi đang làm gì sai không?

Cảm ơn, Rik

+0

nhân vật đặc biệt có thể được tìm thấy ở đây: http://lucene.apache.org/core/3_5_0/queryparsersynta x.html # N10180. – Oliver

Trả lời

5

tôi thấy rằng nỗ lực của tôi để tạo ra một truy vấn mà không sử dụng một QueryParser không hoạt động, vì vậy tôi ngừng cố gắng để tạo ra các truy vấn riêng của tôi và sử dụng một QueryParser để thay thế. Tất cả các gợi ý mà tôi thấy trực tuyến cho thấy rằng bạn nên sử dụng cùng một Trình phân tích trong QueryParser mà bạn sử dụng trong khi lập chỉ mục, vì vậy tôi đã sử dụng một StandardAnalyzer để xây dựng QueryParser.

Điều này hoạt động trên ví dụ này vì StandardAnalyzer loại bỏ từ "the" khỏi đường "lưỡi liềm" trong khi lập chỉ mục và do đó chúng tôi không thể tìm kiếm vì nó không có trong chỉ mục.

Tuy nhiên, nếu chúng tôi chọn tìm kiếm "Đường Grove", chúng tôi có vấn đề với chức năng ngoài hộp, cụ thể là truy vấn sẽ trả về tất cả các kết quả chứa "Grove" HOẶC "Road ". Điều này có thể dễ dàng được sửa bằng cách thiết lập QueryParser để hoạt động mặc định của nó là AND thay vì OR.

Cuối cùng, giải pháp đúng là như sau:

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

Xóa từ dừng khỏi tên phố không chính xác. Hãy nghĩ về những cái tên như [Cả hai đường phố] (http://g.co/maps/r5rnc). Tôi chắc chắn rằng bạn có thể tìm thấy nhiều ví dụ sinh động hơn. Chỉ cần lý do tại sao loại bỏ một cái gì đó nếu nó không có ý nghĩa? –

11

Lý do tại sao bạn không nhận được văn bản của bạn trở lại là trong khi lập chỉ mục bạn đang sử dụng StandardAnalyzer, chuyển đổi thẻ thành chữ thường và loại bỏ dừng lời. Vì vậy, thuật ngữ duy nhất được lập chỉ mục cho ví dụ của bạn là 'lưỡi liềm'. Tuy nhiên, các truy vấn ký tự đại diện không được phân tích, do đó 'the' được bao gồm như một phần bắt buộc của truy vấn. Cũng vậy với các truy vấn cụm từ trong kịch bản của bạn.

KeywordAnalyzer có lẽ không phù hợp cho trường hợp sử dụng của bạn, vì nó lấy toàn bộ nội dung của trường dưới dạng một mã thông báo duy nhất. Bạn có thể sử dụng SimpleAnalyzer cho trường đường phố - nó sẽ tách đầu vào trên tất cả các ký tự không phải chữ cái và sau đó chuyển đổi chúng thành chữ thường. Bạn cũng có thể xem xét sử dụng WhitespaceAnalyzer với LowerCaseFilter. Bạn cần phải thử các tùy chọn khác nhau và tìm ra cách nào phù hợp nhất với dữ liệu và người dùng của mình.

Ngoài ra, bạn có thể sử dụng các trình phân tích khác nhau cho mỗi trường (ví dụ: PerFieldAnalyzerWrapper) nếu thay đổi máy phân tích cho trường đó phá vỡ các tìm kiếm khác.

0

Nếu bạn muốn một từ chính xác khớp với đường phố, bạn có thể đặt Trường "Đường phố" NOT_ANALYZED sẽ không lọc từ dừng "the".

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

Đây không phải là một giải pháp tốt - theo cách này, bạn sẽ cần luôn bao gồm 'the' trong truy vấn để nhận kết quả này. –

+0

@Artur Nowak: Bình chọn câu trả lời của bạn. Một Analyzer phù hợp là điểm. –

0

Không cần phải sử dụng bất kỳ Analyzer đây coz Hibernate ngầm sử dụng StandardAnalyzer mà sẽ chia những lời dựa trên white spaces vì vậy giải pháp ở đây được thiết lập các Analyze để NO nó sẽ tự động thực hiện Multi Phrase Search

@Column(name="skill") 
    @Field(index=Index.YES, analyze=Analyze.NO, store=Store.NO) 
    @Analyzer(definition="SkillsAnalyzer") 
    private String skill; 
Các vấn đề liên quan