2012-12-28 15 views
13

Próbuję zaimplementować indeks dokumentów (z grubsza odpowiadający wierszom DB), gdzie jedno z pól jest liczbą całkowitą. Dodaję je do indeksu jak:Jak przeszukiwać pole int w Lucene 4?

Document doc = new Document(); 
doc.add(new StringField("ticket_number", rs.getString("ticket_number"), 
     Field.Store.YES)); 
doc.add(new IntField("ticket_id", rs.getInt("ticket_id"), 
     Field.Store.YES)); 
doc.add(new StringField("id_s", rs.getString("ticket_id"), 
     Field.Store.YES)); 
w.addDocument(doc); 

Wydaje się, że nie może zapytać pole ticket_id w ogóle, podczas gdy id_s działa dobrze.

Jeden z dokumentów to (I dodaje spacje dla czytelności):

Document< 
    stored,indexed,tokenized,omitNorms,indexOptions=DOCS_ONLY<ticket_number:230114W> 
    stored<ticket_id:152> 
    stored,indexed,tokenized,omitNorms,indexOptions=DOCS_ONLY<id_s:152>> 

Więc moja int pole jest przechowywany, ale nie indeksowane. Ta kwerenda działa zgodnie z oczekiwaniami: id_s:152, podczas gdy ta nigdy nie zwraca niczego: ticket_id:152.

Co robię źle? Jak mogę dodać takie pole do indeksu i umożliwić jego wyszukiwanie?

Odpowiedz

7

Pola numeryczne można wyszukiwać za pomocą NumericRangeQuery. Aby uzyskać dokładne dopasowanie, po prostu ustaw wartość max i min na równe wartości.

Dane wyjściowe wskazujące, że dane pole nie jest indeksowane, mogą wynikać z różnic w sposobie indeksowania wartości numerycznych w porównaniu do wartości tekstowej. Biorąc pod uwagę, że pole jest przekształcane w numeryczną reprezentację Lucene, wartość literalna 152 rzeczywiście nie będzie indeksowana

Na pierwszy rzut oka możliwe jest, że obsługa id_s może być lepszą alternatywą. Identyfikatory nie są zwykle traktowane jako wartości numeryczne, ale raczej jako proste identyfikatory, które są reprezentowane cyframi. Jeśli nie potrzebujesz sortowania numerycznego lub sprawdzania zakresu na polu, indeksowanie jako StringField na pewno ma więcej sensu.

18

Poniżej działa dla mnie:

RAMDirectory idx = new RAMDirectory(); 
    IndexWriter writer = new IndexWriter(
      idx, 
      new IndexWriterConfig(Version.LUCENE_40, new ClassicAnalyzer(Version.LUCENE_40)) 
    ); 
    Document document = new Document(); 
    document.add(new StringField("ticket_number", "t123", Field.Store.YES)); 
    document.add(new IntField("ticket_id", 234, Field.Store.YES)); 
    document.add(new StringField("id_s", "234", Field.Store.YES)); 
    writer.addDocument(document); 
    writer.commit(); 

    IndexReader reader = DirectoryReader.open(idx); 
    IndexSearcher searcher = new IndexSearcher(reader); 

    Query q1 = new TermQuery(new Term("id_s", "234")); 
    TopDocs td1 = searcher.search(q1, 1); 
    System.out.println(td1.totalHits); // prints "1" 

    Query q2 = NumericRangeQuery.newIntRange("ticket_id", 1, 234, 234, true, true); 
    TopDocs td2 = searcher.search(q2, 1); 
    System.out.println(td2.totalHits); // prints "1" 

Jak femtoRgon wskazał, dla wartości numerycznych (wyroby długie, daty, pływaków, etc.) trzeba mieć NumericRangeQuery i określić precyzję. W przeciwnym razie Lucene nie ma pojęcia, jak chcesz zdefiniować podobieństwo.

+0

Dzięki stary, ten bardzo mi pomógł. – SoluableNonagon

+0

Czy "234" wskazuje ten sam fragment danych, jeśli tak, nie sądzę, że ma prawo przechowywać go w indeksie dwa razy, raz jako ciąg i raz jako int. –

+0

'234' jest przechowywany z różnymi polami (' ticket_id' i 'id_s'). Nie widzę w tym nic złego. Koncepcyjnie może to być błędne, ale celem tego przykładu jest tylko udowodnienie, że obie techniki są możliwe. – mindas

4

Inna odpowiedź pochodzi z tego wątku (trzecia odpowiedź): Lucene 4.0 IndexWriter updateDocument for Numeric Term

Zasadniczo, można utworzyć termin z wartości int tak:

String field = "myfield"; 
int value = 4711; 
BytesRef bytes = new BytesRef(NumericUtils.BUF_SIZE_INT); 
NumericUtils.intToPrefixCoded(value, 0, bytes); 
Term term = new Term(field, bytes); 

Następnie można użyć tego określenia do wyszukiwania lub usuwanie/aktualizowanie indeksu. W pierwszym teście sprawdziło się to dla mnie. Nie potrafię powiedzieć, czy jest to "właściwy" sposób na robienie rzeczy. Użyłem NumericRangeFilter wcześniej do filtrowania IntFields, ale teraz jestem skłonny do korzystania z tego podejścia i zamiast tego użyj zwykłego TermsFilter, lub TermQueries.