All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pageseeder.flint.lucene.LuceneIndexQueries Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 Allette Systems (Australia)
 * http://www.allette.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.pageseeder.flint.lucene;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.MultiReader;
import org.apache.lucene.search.*;
import org.pageseeder.flint.Index;
import org.pageseeder.flint.IndexException;
import org.pageseeder.flint.IndexIO;
import org.pageseeder.flint.lucene.query.SearchPaging;
import org.pageseeder.flint.lucene.query.SearchQuery;
import org.pageseeder.flint.lucene.query.SearchResults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utility class to handle lucene queries.
 *
 * @author Jean-Baptiste Reure
 *
 * @version 27 May 2019
 */
public final class LuceneIndexQueries {

  /**
   * Logger will receive debugging and low-level data, use the listener to capture specific indexing operations.
   */
  private static final Logger LOGGER = LoggerFactory.getLogger(LuceneIndexQueries.class);

  // Public external methods
  // ----------------------------------------------------------------------------------------------

  /**
   * Run a search on the given Index.
   *
   * @param index the Index to run the search on
   * @param query the query to run
   * @return the search results
   * @throws IndexException if any error occurred while performing the search
   */
  public static SearchResults query(Index index, SearchQuery query) throws IndexException {
    return query(index, query, new SearchPaging());
  }

  /**
   * Run a search on the given Index.
   *
   * @param index  the Index to run the search on
   * @param query  the query to run
   * @param paging paging details (can be null)
   *
   * @return the search results
   *
   * @throws IndexException if any error occurred while performing the search
   */
  public static SearchResults query(Index index, SearchQuery query, SearchPaging paging) throws IndexException {
    LuceneIndexIO io = getIndexIO(index);
    IndexSearcher searcher = io == null ? null : io.bookSearcher();
    if (searcher != null) {
      try {
        Query lquery = query.toQuery();
        if (lquery == null) {
          io.releaseSearcher(searcher);
          throw new IndexException("Failed performing a query on the Index because the query is null",
              new NullPointerException("Null query"));
        }
        LOGGER.debug("Performing search [{}] on index {}", query, index);
        Sort sort = query.getSort();
        if (sort == null) {
          sort = Sort.INDEXORDER;
        }
        // load the scores
        TopFieldCollector tfc = TopFieldCollector.create(sort, paging.getHitsPerPage() * paging.getPage(), Integer.MAX_VALUE);
        searcher.search(lquery, tfc);
        ScoreDoc[] docs = tfc.topDocs().scoreDocs;
        TopFieldCollector.populateScores(docs, searcher, lquery);
        return new SearchResults(query, docs, tfc.getTotalHits(), paging, io, searcher);
      } catch (IOException ex) {
        io.releaseSearcher(searcher);
        throw new IndexException("Failed performing a query on the Index because of an I/O problem", ex);
      } catch (IllegalArgumentException ex) {
        if (ex.getMessage() != null
            && ex.getMessage().contains("was indexed with bytesPerDim")
            && ex.getMessage().contains("but this query has bytesPerDim")) {
          // no matches
          return new SearchResults(query, new ScoreDoc[] {}, 0, paging, io, searcher);
        } else {
          io.releaseSearcher(searcher);
          throw new IndexException("Failed performing invalid query", ex);
        }
      }
    }
    return null;
  }

  /**
   * Run a search on the given Index.
   *
   * @param index    the Index to run the search on
   * @param query    the query to run
   * @param results  the results' collector
   *
   * @throws IndexException if any error occurred while performing the search
   */
  public static void query(Index index, Query query, Collector results) throws IndexException {
    LuceneIndexIO io = getIndexIO(index);
    IndexSearcher searcher = io == null ? null : io.bookSearcher();
    if (searcher != null) {
      try {
        LOGGER.debug("Performing search [{}] on index {}", query, index);
        // load the scores
        searcher.search(query, results);
      } catch (IOException e) {
        throw new IndexException("Failed performing a query on the Index because of an I/O problem", e);
      } finally {
        io.releaseSearcher(searcher);
      }
    }
  }

  /**
   * Run a search on the given Indexes.
   *
   * @param indexes the Indexes to run the search on
   * @param query   the query to run
   *
   * @return the search results
   *
   * @throws IndexException if any error occurred while performing the search
   */
  public static SearchResults query(List indexes, SearchQuery query) throws IndexException {
    return query(indexes, query, new SearchPaging());
  }

  /**
   * Run a search on the given Indexes.
   *
   * @param indexes  the Indexes to run the search on
   * @param query    the query to run
   * @param paging   paging details (can be null)
   *
   * @return the search results
   *
   * @throws IndexException if any error occurred while performing the search
   */
  public static SearchResults query(List indexes, SearchQuery query, SearchPaging paging) throws IndexException {
    Query lquery = query.toQuery();
    if (lquery == null)
      throw new IndexException("Failed performing a query because the query is null", new NullPointerException("Null query"));
    // find all readers
    Map readersMap = new HashMap<>();
    IndexReader[] readers = new IndexReader[indexes.size()];
    // grab a reader for each indexes
    for (int i = 0; i < indexes.size(); i++) {
      LuceneIndexIO io = getIndexIO(indexes.get(i));
      // make sure index has been set up
      if (io != null) {
        // grab what we need
        IndexReader reader = io.bookReader();
        readers[i] = reader;
        readersMap.put(io, reader);
      }
    }
    try {
      MultiReader reader = new MultiReader(readers);
      IndexSearcher searcher = new IndexSearcher(reader);
      LOGGER.debug("Performing search [{}] on {} indexes", query, readers.length);
      Sort sort = query.getSort();
      if (sort == null)
        sort = Sort.INDEXORDER;

      // load the scores
      TopFieldCollector tfc = TopFieldCollector.create(sort, paging.getHitsPerPage() * paging.getPage(), Integer.MAX_VALUE);
      searcher.search(lquery, tfc);
      ScoreDoc[] docs = tfc.topDocs().scoreDocs;
      TopFieldCollector.populateScores(docs, searcher, lquery);
      return new SearchResults(query, docs, tfc.getTotalHits(), paging, readersMap, searcher);
    } catch (IOException e) {
      for (Map.Entry io : readersMap.entrySet())
        io.getKey().releaseReader(io.getValue());
      throw new IndexException("Failed performing a query on the Index because of an I/O problem", e);
    }
  }

  public static MultipleIndexReader getMultipleIndexReader(List indexes) {
    return new MultipleIndexReader(indexes);
  }

  // Lower level API providing access to Lucene objects
  // ----------------------------------------------------------------------------------------------

  /**
   * Returns a near real-time Reader on the index provided.
   *
   * 

IMPORTANT: the reader should not be closed, it should be used in the following way to ensure * it is made available to other threads:

*
   *    IndexReader reader = manager.grabReader(index);
   *    try {
   *      ...
   *    } finally {
   *      manager.release(index, reader);
   *    }
   * 
* * @param index the index that the Index Reader will point to. * @return the Index Reader to read from the index * */ public static IndexReader grabReader(Index index) { if (index == null) throw new NullPointerException("Cannot grab reader from null index"); LuceneIndexIO io = getIndexIO(index); if (io == null) { LOGGER.error("Failed to grab reader as the IndexIO is not available"); return null; } return io.bookReader(); } /** * Release an {@link IndexReader} after it has been used. * *

It is necessary to release a reader so that it can be reused for other threads. * * @param index The index the reader works on. * @param reader The actual Lucene index reader. * */ public static void release(Index index, IndexReader reader) { if (index == null || reader == null) return; LuceneIndexIO io = getIndexIO(index); if (io != null) io.releaseReader(reader); } /** * Releases an {@link IndexReader} quietly after it has been used so that it can be used in a finally * block. * *

It is necessary to release a reader so that it can be reused for other threads. * * @param index The index the reader works on. * @param reader The actual Lucene index reader. */ public static void releaseQuietly(Index index, IndexReader reader) { if (index == null || reader == null) return; LuceneIndexIO io = getIndexIO(index); if (io != null) io.releaseReader(reader); } /** * Returns a near real-time Searcher on the index provided. * *

IMPORTANT: the searcher should not be closed, it should be used in the following way to * ensure it is made available to other threads:

*
   *    IndexSearcher searcher = manager.grabSearcher(index);
   *    try {
   *      ...
   *    } finally {
   *      manager.release(index, searcher);
   *    }
   * 
* * @param index the index that the searcher will work on. * @return the index searcher to use on the index * */ public static IndexSearcher grabSearcher(Index index) { if (index == null) throw new NullPointerException("Cannot grab searcher from null index"); LuceneIndexIO io = getIndexIO(index); if (io == null) { LOGGER.error("Failed to grab searcher as the IndexIO is not available"); return null; } return io.bookSearcher(); } /** * Release an {@link IndexSearcher} after it has been used. * *

It is necessary to release a searcher so that it can be reused by other threads. * * @param index The index the searcher works on. * @param searcher The actual Lucene index searcher. * */ public static void release(Index index, IndexSearcher searcher) { if (searcher == null) return; LuceneIndexIO io = getIndexIO(index); io.releaseSearcher(searcher); } /** * Releases an {@link IndexSearcher} quietly after it has been used so that it can be used in a finally * block. * *

It is necessary to release a searcher so that it can be reused for other threads. * * @param index The index the searcher works on. * @param searcher The actual Lucene index searcher. */ public static void releaseQuietly(Index index, IndexSearcher searcher) { if (searcher == null) return; getIndexIO(index).releaseSearcher(searcher); } // Private helpers // ============================================================================== /** * Retrieves an IndexIO, creates it if non-existent. * * @param index the index requiring the IO utility. * * @return the Index IO operations manager */ private static LuceneIndexIO getIndexIO(Index index) { if (index == null) return null; IndexIO io = index.getIndexIO(); if (io instanceof LuceneIndexIO) return (LuceneIndexIO) io; return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy