Indeksowanie pól typu data/czas w Apache Lucene

8 lipca 2009 | 0 komentarze

Zadanie: wykonanie wyszukiwania w Lucene artykułów w określonym przedziale czasowym i wyświetlenie daty publikacji bez czytania z bazy danych. Indeksowanie pola typu data.

Ekipa Lucene zaleca zapisywanie daty jako zwykłe pole tekstowe po konwersji do typu String (klasa dedykowana do czasu DateField jest oznaczona jako przestarzała @Deprecated). Najbardziej wydajne jest indeksowanie minimalnej potrzebnej dokładności czasu - im wieksza precyzja, tym dłuższy String i większe obciążenie wydajnościowe podczas wyszukiwania. Data jest konwertowana do formatu który pozwala na sortowanie leksykalne, np. dla dokładności rzędu pojedynczego dnia: CCYYMMDD (20090503 = 3 maja 2009). Dodatkowym plusem takiego formatu ma być możliwość zapisania daty sprzed 1970 roku.

Do zamiany czasu na tekst służy klasa org.apache.lucene.document.DateTools której metody statyczne wykonają robotę w obie strony. Do wyboru są metody przyjmujące jako argument typ java.util.Date i long (milisekundy). Statyczna klasa zagnieżdżona DateTools.Resolution pozwala na określenie dokładności konwersji (YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND).

static String dateToString(Date date, DateTools.Resolution resolution)
static String timeToString(long time, DateTools.Resolution resolution)
static Date stringToDate(String dateString)
static long stringToTime(String dateString)


Przykład indeksowania daty z dokładnością do dnia:

Directory directory = getDirectory();
IndexWriter writer = getWriter(directory);
Document doc = new Document();
doc.add(new Field(”body”, text, Field.Store.YES, Field.Index.ANALYZED));
String dateStr = null;
if(content.getDate()!=null)
dateStr = DateTools.dateToString(content.getDate(), DateTools.Resolution.DAY);
doc.add(new Field(”date”, dateStr, Field.Store.YES, Field.Index.NOT_ANALYZED));



Przykład kodu wyszukującego po przedziale czasowym i odczytanie daty z indeksu:

Directory directory = getDirectory();
IndexSearcher searcher = new IndexSearcher(directory);
RangeQuery rquery = new RangeQuery(new Term("date", "20090401"), new Term("date", "20090601"), true);
TopDocs docs = searcher.search(rquery, 100);
ScoreDoc[] scoreDocs = docs.scoreDocs;
for(int d=0;d
ScoreDoc scoreDoc = scoreDocs[d];
Document doc = searcher.doc(scoreDoc.doc);

String body = doc.get(”body”);
String dateStr = doc.get(”date”);
Date date = null;
try {
date = DateTools.stringToDate(dateStr);
} catch (Exception e) { }
System.out.println(”body:”+body);
System.out.println(”data:”+date);
}
searcher.close();


JavaDoc zaleca użycie ConstantScoreRangeQuery w miejsce RangeQuery, bo ponoć jest szybsze i bezpieczniejsze (nie wyrzuca wyjątku BooleanQuery.TooManyClauses przy przektoczeniu 1024 wyników). W miejsce Query można rozważyć zastrosowanie filtra RangeFilter.


Linki:
http://wiki.apache.org/lucene-java/DateRangeQueries
niektóre wpisy w wiki Lucene są nieaktualne

0 komentarze: