org.pageseeder.flint.lucene.LuceneIndexQueries.java Source code

Java tutorial

Introduction

Here is the source code for org.pageseeder.flint.lucene.LuceneIndexQueries.java

Source

/*
 * 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 <code>null</code>)
     *
     * @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.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(),
                        true, true, false);
                searcher.search(lquery, tfc);
                return new SearchResults(query, tfc.topDocs().scoreDocs, tfc.getTotalHits(), paging, io, searcher);
            } catch (IOException e) {
                io.releaseSearcher(searcher);
                throw new IndexException("Failed performing a query on the Index because of an I/O problem", e);
            }
        }
        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.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<Index> 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 <code>null</code>)
     *
     * @return the search results
     *
     * @throws IndexException if any error occurred while performing the search
     */
    public static SearchResults query(List<Index> 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<LuceneIndexIO, IndexReader> 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 setup
            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
            TopFieldDocs results = searcher.search(lquery, paging.getHitsPerPage() * paging.getPage(), sort);
            return new SearchResults(query, results, paging, readersMap, searcher);
        } catch (IOException e) {
            for (LuceneIndexIO io : readersMap.keySet())
                io.releaseReader(readersMap.get(io));
            throw new IndexException("Failed performing a query on the Index because of an I/O problem", e);
        }
    }

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

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

    /**
     * Returns a near real-time Reader on the index provided.
     *
     * <p>IMPORTANT: the reader should not be closed, it should be used in the following way to ensure
     *  it is made available to other threads:</p>
     * <pre>
     *    IndexReader reader = manager.grabReader(index);
     *    try {
     *      ...
     *    } finally {
     *      manager.release(index, reader);
     *    }
     * </pre>
     *
     * @param index the index that the Index Reader will point to.
     * @return the Index Reader to read from the index
     *
     * @throws IndexException If an IO error occurred when getting the reader.
     */
    public static IndexReader grabReader(Index index) throws IndexException {
        LuceneIndexIO io = getIndexIO(index);
        return io == null ? null : io.bookReader();
    }

    /**
     * Release an {@link IndexReader} after it has been used.
     *
     * <p>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.
     *
     * @throws IndexException Wrapping any IO exception
     */
    public static void release(Index index, IndexReader reader) throws IndexException {
        if (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 <code>finally</code>
     * block.
     *
     * <p>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 (reader == null)
            return;
        getIndexIO(index).releaseReader(reader);
    }

    /**
     * Returns a near real-time Searcher on the index provided.
     *
     * <p>IMPORTANT: the searcher should not be closed, it should be used in the following way to
     * ensure it is made available to other threads:</p>
     * <pre>
     *    IndexSearcher searcher = manager.grabSearcher(index);
     *    try {
     *      ...
     *    } finally {
     *      manager.release(index, searcher);
     *    }
     * </pre>
     *
     * @param index the index that the searcher will work on.
     * @return the index searcher to use on the index
     *
     * @throws IndexException If an IO error occurred when getting the reader.
     */
    public static IndexSearcher grabSearcher(Index index) throws IndexException {
        LuceneIndexIO io = getIndexIO(index);
        return io.bookSearcher();
    }

    /**
     * Release an {@link IndexSearcher} after it has been used.
     *
     * <p>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.
     *
     * @throws IndexException Wrapping any IO exception
     */
    public static void release(Index index, IndexSearcher searcher) throws IndexException {
        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 <code>finally</code>
     * block.
     *
     * <p>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;
    }

}