2009-08-21 6 views
4

Avec Lucene, quelle serait l'approche recommandée pour localiser les correspondances dans les résultats de recherche? Plus précisément, supposons que les documents d'index ont un champ "fullText" qui stocke le contenu en texte brut d'un document. En outre, supposons que pour l'un de ces documents le contenu est "Le renard brun rapide saute par-dessus le chien paresseux". Ensuite, une recherche est effectuée pour "chien de renard". De toute évidence, le document serait un succès.Trouver la position des résultats de recherche de Lucene

Dans ce scénario, Lucene peut-il être utilisé pour fournir quelque chose comme les régions correspondantes pour le document trouvé? Donc, pour ce scénario, je voudrais produire quelque chose comme:

[{match: "fox", startIndex: 10, length: 3}, 
{match: "dog", startIndex: 34, length: 3}] 

Je pense qu'il pourrait être mis en œuvre par ce qui est prévu dans le paquet org.apache.lucene.search.highlight. Je ne suis pas sûr de l'approche globale si ...

Répondre

7

TermFreqVector est ce que j'ai utilisé. Voici une démonstration de travail, qui imprime à la fois les postes de durée déterminée et début et de fin indices terme:

public class Search { 
    public static void main(String[] args) throws IOException, ParseException { 
     Search s = new Search(); 
     s.doSearch(args[0], args[1]); 
    } 

    Search() { 
    } 

    public void doSearch(String db, String querystr) throws IOException, ParseException { 
     // 1. Specify the analyzer for tokenizing text. 
     // The same analyzer should be used as was used for indexing 
     StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT); 

     Directory index = FSDirectory.open(new File(db)); 

     // 2. query 
     Query q = new QueryParser(Version.LUCENE_CURRENT, "contents", analyzer).parse(querystr); 

     // 3. search 
     int hitsPerPage = 10; 
     IndexSearcher searcher = new IndexSearcher(index, true); 
     IndexReader reader = IndexReader.open(index, true); 
     searcher.setDefaultFieldSortScoring(true, false); 
     TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true); 
     searcher.search(q, collector); 
     ScoreDoc[] hits = collector.topDocs().scoreDocs; 

     // 4. display term positions, and term indexes 
     System.out.println("Found " + hits.length + " hits."); 
     for(int i=0;i<hits.length;++i) { 

      int docId = hits[i].doc; 
      TermFreqVector tfvector = reader.getTermFreqVector(docId, "contents"); 
      TermPositionVector tpvector = (TermPositionVector)tfvector; 
      // this part works only if there is one term in the query string, 
      // otherwise you will have to iterate this section over the query terms. 
      int termidx = tfvector.indexOf(querystr); 
      int[] termposx = tpvector.getTermPositions(termidx); 
      TermVectorOffsetInfo[] tvoffsetinfo = tpvector.getOffsets(termidx); 

      for (int j=0;j<termposx.length;j++) { 
       System.out.println("termpos : "+termposx[j]); 
      } 
      for (int j=0;j<tvoffsetinfo.length;j++) { 
       int offsetStart = tvoffsetinfo[j].getStartOffset(); 
       int offsetEnd = tvoffsetinfo[j].getEndOffset(); 
       System.out.println("offsets : "+offsetStart+" "+offsetEnd); 
      } 

      // print some info about where the hit was found... 
      Document d = searcher.doc(docId); 
      System.out.println((i + 1) + ". " + d.get("path")); 
     } 

     // searcher can only be closed when there 
     // is no need to access the documents any more. 
     searcher.close(); 
    }  
} 
+0

Le commentaire « cette partie ne fonctionne que s'il y a un terme Thr chaîne de requête ». Ma question suivante était: comment trouver quels termes correspondent à la requête (s'il s'agit d'une requête complexe (par exemple avec des caractères génériques), cette réponse comble bien ce vide: http://stackoverflow.com/questions/7896183/get-matched- terms-from-lucene-query – geert3

2

Voici une solution pour Lucene 5.2.1. Il ne fonctionne que pour les requêtes de mots simples, mais devrait démontrer les principes de base.

L'idée de base est:

  1. Obtenir un TokenStream pour chaque document, ce qui correspond à votre requête.
  2. Créez un QueryScorer et initialisez-le avec le tokenStream récupéré.
  3. "Boucle" sur chaque jeton du flux (effectué par tokenStream.incrementToken()) et vérifiez si le jeton correspond aux critères de recherche (effectué par queryScorer.getTokenScore()).

Voici le code:

import java.io.IOException; 
import java.util.List; 
import java.util.Vector; 

import org.apache.lucene.analysis.TokenStream; 
import org.apache.lucene.analysis.de.GermanAnalyzer; 
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; 
import org.apache.lucene.document.Document; 
import org.apache.lucene.index.DirectoryReader; 
import org.apache.lucene.index.IndexReader; 
import org.apache.lucene.index.IndexWriter; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.Query; 
import org.apache.lucene.search.ScoreDoc; 
import org.apache.lucene.search.TopDocs; 
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; 
import org.apache.lucene.search.highlight.QueryScorer; 
import org.apache.lucene.search.highlight.TokenSources; 

public class OffsetSearcher { 

    private IndexReader reader; 

    public OffsetSearcher(IndexWriter indexWriter) throws IOException { 
     reader = DirectoryReader.open(indexWriter, true); 
    } 

    public OffsetData[] getTermOffsets(Query query) throws IOException, InvalidTokenOffsetsException 
    { 
     List<OffsetData> result = new Vector<>(); 

     IndexSearcher searcher = new IndexSearcher(reader); 
     TopDocs topDocs = searcher.search(query, 1000); 

     ScoreDoc[] scoreDocs = topDocs.scoreDocs; 

     Document doc; 
     TokenStream tokenStream; 
     CharTermAttribute termAtt; 
     OffsetAttribute offsetAtt; 
     QueryScorer queryScorer; 
     OffsetData offsetData; 
     String txt, tokenText; 
     for (int i = 0; i < scoreDocs.length; i++) 
     { 
      int docId = scoreDocs[i].doc; 
      doc = reader.document(docId); 

      txt = doc.get(RunSearch.CONTENT); 
      tokenStream = TokenSources.getTokenStream(RunSearch.CONTENT, reader.getTermVectors(docId), txt, new GermanAnalyzer(), -1); 

      termAtt = (CharTermAttribute)tokenStream.addAttribute(CharTermAttribute.class); 
      offsetAtt = (OffsetAttribute)tokenStream.addAttribute(OffsetAttribute.class); 

      queryScorer = new QueryScorer(query); 
      queryScorer.setMaxDocCharsToAnalyze(RunSearch.MAX_DOC_CHARS); 
      TokenStream newStream = queryScorer.init(tokenStream); 
      if (newStream != null) { 
       tokenStream = newStream; 
      } 
      queryScorer.startFragment(null); 

      tokenStream.reset(); 

      int startOffset, endOffset; 
      for (boolean next = tokenStream.incrementToken(); next && (offsetAtt.startOffset() < RunSearch.MAX_DOC_CHARS); next = tokenStream.incrementToken()) 
      { 
       startOffset = offsetAtt.startOffset(); 
       endOffset = offsetAtt.endOffset(); 

       if ((endOffset > txt.length()) || (startOffset > txt.length())) 
       { 
        throw new InvalidTokenOffsetsException("Token " + termAtt.toString() + " exceeds length of provided text sized " + txt.length()); 
       } 

       float res = queryScorer.getTokenScore(); 
       if (res > 0.0F && startOffset <= endOffset) { 
        tokenText = txt.substring(startOffset, endOffset); 
        offsetData = new OffsetData(tokenText, startOffset, endOffset, docId); 
        result.add(offsetData); 
       }   
      } 
     } 

     return result.toArray(new OffsetData[result.size()]); 
    } 


    public void close() throws IOException { 
     reader.close(); 
    } 


    public static class OffsetData { 

     public String phrase; 
     public int startOffset; 
     public int endOffset; 
     public int docId; 

     public OffsetData(String phrase, int startOffset, int endOffset, int docId) { 
      super(); 
      this.phrase = phrase; 
      this.startOffset = startOffset; 
      this.endOffset = endOffset; 
      this.docId = docId; 
     } 

    } 

} 
+0

Pourriez-vous nous dire comment l'implémenter pour des requêtes à plusieurs termes? @matthiasboesinger – Heidar