2010-12-30 5 views
7

Il mio ricercatore funziona molto bene, tuttavia tende a restituire risultati obsoleti. Il mio sito è molto simile a NerdDinner, per cui gli eventi passati diventano irrilevanti.Lucene.Net: Come posso aggiungere un filtro per la data ai miei risultati di ricerca?

Attualmente sto indicizzazione come questo
nota: il mio esempio è in VB.NET, ma non mi importa se esempi sono forniti in C#

Public Function AddIndex(ByVal searchableEvent As [Event]) As Boolean Implements ILuceneService.AddIndex 

     Dim writer As New IndexWriter(luceneDirectory, New StandardAnalyzer(), False) 

     Dim doc As Document = New Document 

     doc.Add(New Field("id", searchableEvent.ID, Field.Store.YES, Field.Index.UN_TOKENIZED)) 
     doc.Add(New Field("fullText", FullTextBuilder(searchableEvent), Field.Store.YES, Field.Index.TOKENIZED)) 
     doc.Add(New Field("user", If(searchableEvent.User.UserName = Nothing, 
            "User" & searchableEvent.User.ID, 
            searchableEvent.User.UserName), 
           Field.Store.YES, 
           Field.Index.TOKENIZED)) 
     doc.Add(New Field("title", searchableEvent.Title, Field.Store.YES, Field.Index.TOKENIZED)) 
     doc.Add(New Field("location", searchableEvent.Location.Name, Field.Store.YES, Field.Index.TOKENIZED)) 
     doc.Add(New Field("date", searchableEvent.EventDate, Field.Store.YES, Field.Index.UN_TOKENIZED)) 

     writer.AddDocument(doc) 

     writer.Optimize() 
     writer.Close() 
     Return True 

    End Function 

Notate come ho un " data "indice che memorizza la data dell'evento.

La mia ricerca si presenta quindi come questo

''# code omitted 
     Dim reader As IndexReader = IndexReader.Open(luceneDirectory) 
     Dim searcher As IndexSearcher = New IndexSearcher(reader) 
     Dim parser As QueryParser = New QueryParser("fullText", New StandardAnalyzer()) 
     Dim query As Query = parser.Parse(q.ToLower) 

     ''# We're using 10,000 as the maximum number of results to return 
     ''# because I have a feeling that we'll never reach that full amount 
     ''# anyways. And if we do, who in their right mind is going to page 
     ''# through all of the results? 
     Dim topDocs As TopDocs = searcher.Search(query, Nothing, 10000) 
     Dim doc As Document = Nothing 

     ''# loop through the topDocs and grab the appropriate 10 results based 
     ''# on the submitted page number 
     While i <= last AndAlso i < topDocs.totalHits 
       doc = searcher.Doc(topDocs.scoreDocs(i).doc) 
       IDList.Add(doc.[Get]("id")) 
       i += 1 
     End While 
''# code omitted 

Ho provato quanto segue, ma era inutilmente (ha gettato un NullReferenceException).

 While i <= last AndAlso i < topDocs.totalHits 
      If Date.Parse(doc.[Get]("date")) >= Date.Today Then 
       doc = searcher.Doc(topDocs.scoreDocs(i).doc) 
       IDList.Add(doc.[Get]("id")) 
       i += 1 
      End If 
     End While 

Ho trovato anche la seguente documentazione, ma non riesco a fare testa o croce di esso
http://lucene.apache.org/java/1_4_3/api/org/apache/lucene/search/DateFilter.html

+0

Ok, pensavo avessi aggiunto il mvc. Quindi stai bene con un esempio di IronPython o IronRuby? ;) – jfar

+0

:-p [uh questo è prolly spingendolo] –

risposta

9

stai collegamento alla documentazione delle API di Lucene 1.4.3 . Lucene.Net è attualmente al 2.9.2. Penso che sia dovuto un aggiornamento.

Innanzitutto, stai utilizzando Store.Yes. I campi memorizzati aumenteranno il tuo indice, il che potrebbe essere un problema di prestazioni. Il tuo problema con la data può essere facilmente risolto memorizzando le date come stringhe nel formato "yyyyMMddHHmmssfff" (che è davvero ad alta risoluzione, fino a millisecondi). Potresti voler ridurre la risoluzione per creare meno token per ridurre le dimensioni dell'indice.

var dateValue = DateTools.DateToString(searchableEvent.EventDate, DateTools.Resolution.MILLISECOND); 
doc.Add(new Field("date", dateValue, Field.Store.YES, Field.Index.NOT_ANALYZED)); 

Quindi si applica un filtro alla ricerca (il secondo parametro, in cui attualmente si passa in Nothing/null).

var dateValue = DateTools.DateToString(DateTime.Now, DateTools.Resolution.MILLISECOND); 
var filter = FieldCacheRangeFilter.NewStringRange("date", 
       lowerVal: dateValue, includeLower: true, 
       upperVal: null, includeUpper: false); 
var topDocs = searcher.Search(query, filter, 10000); 

Si può fare questo con un BooleanQuery che unisce la query normale con un RangeQuery, ma che avrebbe anche influenzare punteggio (che viene calcolato sulla query, senza il filtro). Si potrebbe anche voler evitare di modificare la query per semplicità, in modo da sapere quale query viene eseguita.

+0

l'esempio era proprio quello che ho trovato facendo una ricerca su Google. Sto usando Lucene.Net v2.4.0.2 –

+0

Voglio che l'utente sia in grado di cercare tramite 'date: gg/mm/aaaa 'funzionerà ancora? –

+0

Una rapida ricerca nel magico Changes.txt mostra che 2.4.0 è stato rilasciato il 2008-10-06 e si trova dopo la riga 1100 in un lungo elenco di correzioni di bug e nuove funzionalità. Immagino che ci sia un nuovo formato di indice (che verrà automaticamente aggiornato, ma le versioni precedenti non possono continuare a leggere), ma è comunque necessario considerare un aggiornamento. – sisve

7

È possibile combinare più query con un BooleanQuery. Poiché Lucene cerca solo testo, il campo della data nell'indice deve essere ordinato dalla parte più significativa alla parte meno significativa della data, ad esempio nel formato IS8601 ("2010-11-02T20: 49: 16.000000 + 00: 00")

Esempio:

Lucene.Net.Index.Term searchTerm = new Lucene.Net.Index.Term("fullText", searchTerms); 
Lucene.Net.Index.Term dateRange = new Lucene.Net.Index.Term("date", "2010*"); 

Lucene.Net.Search.Query termQuery = new Lucene.Net.Search.TermQuery(searchTerm); 
Lucene.Net.Search.Query dateRangeQuery = new Lucene.Net.Search.WildcardQuery(dateRange); 

Lucene.Net.Search.BooleanQuery query = new Lucene.Net.Search.BooleanQuery(); 
query.Add(termQuery, BooleanClause.Occur.MUST); 
query.Add(dateRangeQuery, BooleanClause.Occur.MUST); 

in alternativa, se un jolly non è abbastanza preciso è possibile aggiungere un RangeQuery invece:

Lucene.Net.Search.Query termQuery = new Lucene.Net.Search.TermQuery(searchTerm); 
Lucene.Net.Index.Term date1 = new Lucene.Net.Index.Term("date", "2010-11-02*"); 
Lucene.Net.Index.Term date2 = new Lucene.Net.Index.Term("date", "2010-11-03*"); 
Lucene.Net.Search.Query dateRangeQuery = new Lucene.Net.Search.RangeQuery(date1, date2, true); 

Lucene.Net.Search.BooleanQuery query = new Lucene.Net.Search.BooleanQuery(); 
query.Add(termQuery, BooleanClause.Occur.MUST); 
query.Add(dateRangeQuery, BooleanClause.Occur.MUST); 
+0

oh merda, come devo capire come fare una ricerca booleana ;-) –