Package org.olat.search.service.searcher

Source Code of org.olat.search.service.searcher.SearchResultsImpl

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.search.service.searcher;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.commons.services.search.OlatDocument;
import org.olat.core.commons.services.search.ResultDocument;
import org.olat.core.commons.services.search.SearchResults;
import org.olat.core.id.Identity;
import org.olat.core.id.Roles;
import org.olat.core.id.context.BusinessControl;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.search.service.SearchServiceImpl;
import org.olat.search.service.indexer.Indexer;


/**
* Data object to pass search results back from search service.
* @author Christian Guretzki
*
*/
public class SearchResultsImpl implements SearchResults {
  private static final OLog log = Tracing.createLoggerFor(SearchResultsImpl.class);
 
  private static final String HIGHLIGHT_PRE_TAG  = "<span class=\"o_search_result_highlight\">";
  private static final String HIGHLIGHT_POST_TAG = "</span>";
  private static final String HIGHLIGHT_SEPARATOR = "...<br />";
 
  /* Define in module config */
  private int maxResults;
 
  private long queryTime;
 
  private int numberOfIndexDocuments;
 
  /* List of ResultDocument. */
  private List<ResultDocument> resultList;

  /**
   * Constructure for certain search-results.
   * Does not include any search-call to search-service.
   * Search call must be made before to create a Hits object.
   * @param hits           Search hits return from search.
   * @param query          Search query-string.
   * @param analyzer       Search analyser, must be the same like at creation of index.
   * @param identity       Filter results for this idenity (user).
   * @param roles          Filter results for this roles (role of user).
   * @param doHighlighting Flag to enable highlighting search
   * @throws IOException
   */
  public SearchResultsImpl(Searcher searcher, TopDocs docs, Query query, Analyzer analyzer, Identity identity, Roles roles, boolean doHighlighting) throws IOException{
    maxResults = SearchServiceImpl.getInstance().getSearchModuleConfig().getMaxResults();
    int maxHits = SearchServiceImpl.getInstance().getSearchModuleConfig().getMaxHits();
   
    List<OlatDocument> olatDocList = createOlatDocumentList(searcher, docs, maxHits);   
    // filter this list (remove those entries not visible)
    List<OlatDocument> filteredList = doFilter(identity, roles, olatDocList, maxResults);
    // Convert filteredList to ResultDocument list with highlighting text if nesessary
    resultList = convertToResultDocument(query, analyzer, doHighlighting, filteredList);
  }
 
  /**
   *
   * @return  Length of result-list.
   */
  public String getLength() {
    return Integer.toString(resultList.size());
  }
 
  /**
   * @return List of ResultDocument.
   */
  public List<ResultDocument> getList() {
    return resultList;
  }
 
  /**
   * Set query response time in milliseconds.
   * @param queryTime  Query response time in milliseconds.
   */
  public void setQueryTime(long queryTime) {
    this.queryTime = queryTime;
  }

  /**
   * @return  Query response time in milliseconds.
   */
  public String getQueryTime() {
    return Long.toString(queryTime);
  }

  /**
   * Set number of search-index-elements.
   * @param numberOfIndexDocuments  Number of search-index-elements.
   */
  public void setNumberOfIndexDocuments(int numberOfIndexDocuments) {
    this.numberOfIndexDocuments = numberOfIndexDocuments;
  }

  /**
   * @return  Number of search-index-elements.
   */
  public String getNumberOfIndexDocuments() {
    return Integer.toString(numberOfIndexDocuments);
  }

  /**
   * Check if the search found too many results.
   * @return  TRUE: search results found too many results.
   */
  public boolean hasTooManyResults() {
    return (resultList.size() >= maxResults);
  }
 
  /**
   * @return  Number of maximal possible results.
   */
  public String getMaxResults() {
    return Integer.toString(maxResults);
  }

  ///////////////////
  // Private Methods
  ///////////////////
  /**
   * Convert filteredList to ResultDocument list with highlighting text if nesessary.
   * @param query
   * @param analyzer
   * @param doHighlighting
   * @param filteredList
   * @return
   * @throws IOException
   */
  private List<ResultDocument> convertToResultDocument(Query query, Analyzer analyzer, boolean doHighlighting, List<OlatDocument> filteredList) throws IOException {
    List<ResultDocument> newResultList = new ArrayList<ResultDocument>(maxResults);
    Iterator<OlatDocument> iter = filteredList.iterator();
    while (iter.hasNext()) {
      OlatDocument odoc = iter.next();
      Document doc = odoc.getLuceneDocument();
      ResultDocument resultDocument = new ResultDocument(doc);
      if (doHighlighting) {
        doHighlight(query, analyzer, doc, resultDocument);
      }
      newResultList.add(resultDocument);
    }
    return newResultList;
  }

  /**
   * Highlight (bold,color) query words in result-document. Set HighlightResult for content or description.
   * @param query
   * @param analyzer
   * @param doc
   * @param resultDocument
   * @throws IOException
   */
  private void doHighlight(Query query, Analyzer analyzer, Document doc, ResultDocument resultDocument) throws IOException {
    Highlighter highlighter = new Highlighter(new SimpleHTMLFormatter(HIGHLIGHT_PRE_TAG,HIGHLIGHT_POST_TAG) , new QueryScorer(query));
    // Get 3 best fragments of content and seperate with a "..."
    String content = doc.get(OlatDocument.CONTENT_FIELD_NAME);
    String title = doc.get(OlatDocument.TITLE_FIELD_NAME);
   
    Set terms = new HashSet();
    query.extractTerms(terms);
   
    try {
      //highlight content
      TokenStream tokenStream = analyzer.tokenStream(OlatDocument.CONTENT_FIELD_NAME, new StringReader(content));               
      String highlightResult = highlighter.getBestFragments(tokenStream, content, 3, HIGHLIGHT_SEPARATOR);
      // if no highlightResult is in content => look in description
      if (highlightResult.length() == 0) {
        String description = doc.get(OlatDocument.DESCRIPTION_FIELD_NAME);    
        tokenStream = analyzer.tokenStream(OlatDocument.DESCRIPTION_FIELD_NAME, new StringReader(description));               
        highlightResult = highlighter.getBestFragments(tokenStream, description, 3, HIGHLIGHT_SEPARATOR);
        resultDocument.setHighlightingDescription(true);
     
      resultDocument.setHighlightResult(highlightResult);
     
      //highlight title
      tokenStream = analyzer.tokenStream(OlatDocument.CONTENT_FIELD_NAME, new StringReader(title));               
      String highlightTitle = highlighter.getBestFragments(tokenStream, title, 3, " ")
      resultDocument.setHighlightTitle(highlightTitle);
    } catch (InvalidTokenOffsetsException e) {
      log.warn("", e);
    }
  } 
 
  /**
   * Creates a list of OlatDocument based on Hits object
   */
  private List<OlatDocument> createOlatDocumentList(Searcher searcher, TopDocs docs, int maxHits) throws CorruptIndexException, IOException
    // build up a list
    int numOfDocs = Math.min(maxHits, docs.totalHits);
    List<OlatDocument> res = new ArrayList<OlatDocument>(numOfDocs);
    for (int i=0; i<numOfDocs; i++) {
      Document luDoc = searcher.doc(docs.scoreDocs[i].doc);
      res.add(new OlatDocument(luDoc));
    }
    return res;
  }

  /**
   * Filter search results for certain user and roles.
   * @param identity  The current identity of the user.
   * @param roles     The roles of the user.
   * @param res       Unfiltered list of OlatDocument's
   * @return  Filtered list of OlatDocument's
   */
  private List<OlatDocument> doFilter(Identity identity, Roles roles, List<OlatDocument> unFilteredList, int maxToReturn) {
    // check if admin
    if (roles.isOLATAdmin()) {
      return createResultListForAdmin(unFilteredList, maxToReturn);
    }
   
    List<OlatDocument> filteredList = new ArrayList<OlatDocument>(maxToReturn);
    long startTime = 0;
    if ( log.isDebug() ) {
      startTime = System.currentTimeMillis();
    }
    Indexer mainIndexer = (Indexer)CoreSpringFactory.getBean("mainindexer");
    // loop over all results
    Iterator<OlatDocument> it_odocs = unFilteredList.iterator();
    int resultCount = 0;
    int loopCount = 0;
    while (it_odocs.hasNext() && resultCount < maxToReturn) {
      long elementStartTime = 0;
      if ( log.isDebug() ) {
        elementStartTime = System.currentTimeMillis();
      }
      OlatDocument odoc = it_odocs.next();
      String resourceUrl = odoc.getResourceUrl();
      BusinessControl businessControl = BusinessControlFactory.getInstance().createFromString(resourceUrl);
      boolean hasAccess = mainIndexer.checkAccess(null, businessControl, identity, roles);
      if (hasAccess) {
        filteredList.add(odoc);
        resultCount++;
      }
      loopCount++;
      if ( log.isDebug() ) {
        long elementEndTime = System.currentTimeMillis();
        log.debug("doFilter: elementStartTime[" + resultCount + "]=" + (elementEndTime - elementStartTime) + " bc=(" + resourceUrl + ")");
      }
      if (loopCount % 10 == 0) {
        // Do commit after certain number of documents because the transaction should not be too big
        DBFactory.getInstance().intermediateCommit();
        log.debug("DB: committed and close session");
      }
    }
    if ( log.isDebug() ) {
      log.debug("doFilter: loopCount=" + loopCount);
      log.debug("doFilter: resultCount=" + resultCount);
      long endTime = System.currentTimeMillis();
      log.debug("doFilter: summary time=" + (endTime - startTime));
    }
   
    return filteredList;
  }

  private List<OlatDocument> createResultListForAdmin(List<OlatDocument> unFilteredList, int maxToReturn) {   
    //     Admin has access for everything => Return list unchanged, first check size
    if (unFilteredList.size() > maxResults) {
      // Too many results => reduce to MAX_RESULTS
      return unFilteredList.subList(0, maxToReturn);
    }
    // list < max => nothing to filter, return un-changed
    return unFilteredList;
  }
 
}
TOP

Related Classes of org.olat.search.service.searcher.SearchResultsImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.