Статьи

Понимание поиска информации с помощью Apache Lucene и Tika — Часть 3

Урок 4. Выделите фрагменты, в которых была найдена искомая фраза

Поиск по слову или фразе имеет две цели: выяснить, в каком документе он может быть найден, но также узнать, в каком фрагменте документа был найден ответ. Есть два вспомогательных класса, которые показывают попадания документа:

  • org.apache.lucene.search.TopDocs — содержит ссылки на главные документы, возвращаемые поиском. Методы TopDocs :
    •  totalHits — количество документов, соответствующих запросу
    • ScoreDocs — массив экземпляров ScoreDoc, содержащих результаты
    • getMaxScore () — возвращает лучший результат из всех матчей
  • org.apache.lucene.search.ScoreDoc — представляет один результат поиска. Методы ScoreDoc :
    • doc — идентификатор документа
    • оценка — оценка документа

Чтобы извлечь фрагмент, в котором была найдена фраза / слово, используется класс Highlighter. Пакет выделения содержит классы для предоставления функций «ключевое слово в контексте», которые обычно используются для выделения условий поиска в тексте страниц результатов. Класс Highlighter является центральным компонентом и может использоваться для выделения наиболее интересных разделов фрагмента текста и выделения их с помощью Fragmenter.

Листинг 4.1. Получение совпадений документа с искомой фразой

public static String processResults(IndexReader reader, 
        IndexSearcher searcher, Analyzer analyzer, Query query, 
        TopScoreDocCollector collector) throws Exception {
        StringBuffer answer = new StringBuffer();

        SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter();
        Highlighter highlighter = new Highlighter(htmlFormatter, 
                new QueryScorer(query));
        highlighter.setEncoder(new SimpleHTMLEncoder());
        ScoreDoc[] hits = collector.topDocs().scoreDocs;
       //obtaining document hits of the search phrase
        searcher.search(query, ISearchConstants.MAXIMUM_RESULTS_PER_SEARCH);

        answer.append("Number of documents where it was found : ")
                .append(collector.getTotalHits()).append("\n");
        answer.append("\n----------------------------------------------------\n");
        for (int i = 0; i < hits.length; i++) {
            Document doc = reader.document(hits[i].doc);

            answer.append("Document where phrase or part of it was found and its scoring : ");
            answer.append(doc.get("file")).append("  (").append(hits[i].score)
                    .append(")").append("\n");
            answer.append("\n-----------------------------------------------\n");
            answer.append(searchInContent(highlighter, reader, doc, i, 
                    ISearchConstants.FIELD_ABSTRACT_TEXT, analyzer));
            answer.append(searchInContent(highlighter, reader, doc, i, 
                    ISearchConstants.FIELD_TEXT, analyzer));
            answer.append("\n------------------------------------------------\n");
        }

        return answer.toString();
    }

Анализаторы возвращают TokenStream; TokenStream, полученный анализатором, будет использоваться для выделения фрагментов.

Кроме того, созданный выше объект Highlighter может использоваться для извлечения фрагмента, в котором была найдена фраза / слово:

Листинг 4.2. Выделение фрагментов (сначала анализируется аннотация, затем обычный индексированный текст)

private static String searchInContent(Highlighter highlighter, 
          IndexReader indexReader, Document doc, Integer docId, 
          String contentOption, Analyzer analyzer) throws Exception {
        StringBuffer answer = new StringBuffer();
        String text = doc.get(contentOption);
        TokenStream tokenStream = TokenSources.getAnyTokenStream(indexReader, 
                docId, contentOption, analyzer);
       //try to get the best matching fragment(s) 

        try {

            TextFragment[] frag = highlighter.getBestTextFragments(tokenStream, 
                    text, false, ISearchConstants.MAXIMUM_RESULTS_PER_SEARCH);
            for (int j = 0; j < frag.length; j++) {
                answer.append(SearchUtils.processText(frag[j], (contentOption
                        .equals(ISearchConstants.FIELD_ABSTRACT_TEXT) ? true 
                        : false), j+1));
            }

        } catch (InvalidTokenOffsetsException ex) {
            ex.printStackTrace();
            TextFragment frag  = new TextFragment(text, 0, 10);
            answer.append(SearchUtils.processText(frag, (contentOption
                    .equals(ISearchConstants.FIELD_ABSTRACT_TEXT) ? true 
                    : false), 1));
        }

        return answer.toString();
    }

Личное примечание: при индексации документов старайтесь избегать документов, содержащих изображения; класс Highlighter не может выполнить извлечение фрагмента этих документов, и будет выдано исключение InvalidTokendOffsetsException.

Ресурсы

Учить