com.fuerve.villageelder.search.Searcher.java Source code

Java tutorial

Introduction

Here is the source code for com.fuerve.villageelder.search.Searcher.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.fuerve.villageelder.search;

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

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.facet.search.FacetRequest;
import org.apache.lucene.facet.search.FacetsCollector;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MultiCollector;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopFieldCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

/**
 * This class provides an interface for regular and faceted search
 * of the revision index.  The bulk of this class is a thin wrapper
 * around the {@link Search} class, slightly decoupling the management
 * of the indexes and the logic of creating and managing a search.
 * @author lparker
 *
 */
public class Searcher {
    private Directory indexDirectory;
    private Directory taxonomyDirectory;
    private String indexDirectoryName;
    private String taxonomyDirectoryName;
    private boolean stringDirectories;

    private DirectoryReader indexReader;
    private IndexSearcher indexSearcher;
    private TaxonomyReader taxonomyReader;

    private Search search;

    private boolean initialized;

    /**
     * Initializes a new instance of Searcher with a preconstructed regular index
     * Directory and taxonomy Directory.
     * @param iindexDirectory The Directory in which the primary Lucene index exists.
     * @param ttaxonomyDirectory The Directory in which the taxonomy index exists.
     */
    public Searcher(final Directory iindexDirectory, final Directory ttaxonomyDirectory) {
        indexDirectory = iindexDirectory;
        taxonomyDirectory = ttaxonomyDirectory;
    }

    /**
     * Initializes a new instance of Searcher with String pathnames to directories
     * in which regular and taxonomy indices exist.
     * @param iindexDirectory The pathname of the directory in which the primary Lucene
     * index exists.
     * @param ttaxonomyDirectory The pathname of the directory in which the
     * taxonomy index exists.
     */
    public Searcher(final String iindexDirectory, final String ttaxonomyDirectory) {
        if (iindexDirectory == null || ttaxonomyDirectory == null) {
            throw new IllegalArgumentException("The index and/or taxonomy directories were unspecified");
        }

        stringDirectories = true;
        indexDirectoryName = iindexDirectory;
        taxonomyDirectoryName = ttaxonomyDirectory;
    }

    /**
     * Performs initialization of the Lucene regular and taxonomy
     * indices by opening the directories and initializing
     * a searcher and taxonomy reader.
     * @throws IOException A fatal exception occurred while interacting
     * with the index directories.
     */
    public void initializeSearch() throws IOException {
        // Don't try to re-initialize an already initialized index.
        if (initialized == true) {
            return;
        }

        initializeDirectories();
        initializeSearcher();

        initialized = true;
    }

    /**
     * Readies the index directories for managing of the regular and
     * taxonomy indices by Lucene.
     * @throws IOException A fatal exception occurred when trying to
     * construct the Lucene Directory objects.  The path may not exist,
     * the Directory may be invalid, or some other error has occurred.
     */
    private void initializeDirectories() throws IOException {
        if (stringDirectories == true) {
            indexDirectory = initializeDirectoryFromString(indexDirectoryName);
            taxonomyDirectory = initializeDirectoryFromString(taxonomyDirectoryName);
        }
    }

    /**
     * Given a string pathname, returns a valid Lucene Directory object.
     * @param path The pathname in which to create and/or manage a
     * Lucene index.
     * @return The Directory object that contains the specified path.
     * @throws IOException A fatal exception occurred when trying to open
     * the pathname.  The path may not exist in the file system.
     */
    private Directory initializeDirectoryFromString(final String path) throws IOException {
        if (path.isEmpty()) {
            throw new IllegalArgumentException("An empty pathname was specified for the index directory");
        } else {
            return FSDirectory.open(new File(path));
        }
    }

    /**
     * Initializes an index searcher and taxonomy reader for
     * search operations.
     * @throws IOException A fatal exception occurred when trying
     * to interact with the indices or their directories.
     */
    private void initializeSearcher() throws IOException {
        if (indexDirectory == null || taxonomyDirectory == null) {
            throw new IllegalArgumentException("Tried to open a searcher on null directories.");
        }
        indexReader = DirectoryReader.open(indexDirectory);
        indexSearcher = new IndexSearcher(indexReader);
        taxonomyReader = new DirectoryTaxonomyReader(taxonomyDirectory);
    }

    /**
     * Initializes a new instance of Search with a query.
     * @param qquery The query to be executed during the search.
     */
    public Search createSearch(final Query query) {
        search = new Search(query);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query and
     * a custom sort order.
     * @param qquery The query to be executed during the search.
     * @param ssort The custom sort order.
     */
    public Search createSearch(final Query query, final Sort sort) {
        search = new Search(query, sort);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query and a
     * set of facets (in {@link FacetRequest} form).
     * @param qquery The query to be executed during the search.
     * @param ffacets The facets for which to search.
     */
    public Search createSearch(final Query query, final List<FacetRequest> facets) {
        search = new Search(query, facets);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query, a set
     * of facets (in {@link FacetRequest} form) and a custom
     * sort.
     * @param qquery The query to be executed during the search.
     * @param ffacets The facets for which to search.
     * @param ssort The custom sort order.
     */
    public Search createSearch(final Query query, final List<FacetRequest> facets, final Sort sort) {
        search = new Search(query, facets, sort);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query.
     * @param qquery The query to be executed during the search.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query) throws ParseException {
        search = new Search(query);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query and
     * a custom sort order.
     * @param qquery The query to be executed during the search.
     * @param ssort The custom sort order.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query, final Sort sort) throws ParseException {
        search = new Search(query, sort);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query and
     * an analyzer.
     * @param qquery The query to be executed during the search.
     * @param aanalyzer The analyzer with which to parse the
     * query.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query, final Analyzer analyzer) throws ParseException {
        search = new Search(query, analyzer);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query and a
     * set of facets (in {@link String} form).
     * @param qquery The query to be executed during the search.
     * @param ffacets The facets for which to search.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query, final Map<String, Integer> facets) throws ParseException {
        search = new Search(query, facets);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query, a set
     * of facets (in {@link String} form with integer counts) and a custom
     * sort.
     * @param qquery The query to be executed during the search.
     * @param ffacets The facets for which to search.
     * @param ssort The custom sort order.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query, final Map<String, Integer> facets, final Sort sort)
            throws ParseException {
        search = new Search(query, facets, sort);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query,
     * a set of facets (in {@link String} form with integer counts)
     * and an analyzer.
     * @param qquery The query to be executed during the search.
     * @param ffacets The facets for which to search.
     * @param aanalyzer The analyzer with which to parse the
     * query.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query, final Map<String, Integer> facets, final Analyzer analyzer)
            throws ParseException {
        search = new Search(query, facets, analyzer);
        return search;
    }

    /**
     * Initializes a new instance of Search with a query,
     * a set of facets (in {@link String} form with integer counts), an
     * analyzer and a custom sort.
     * @param qquery The query to be executed during the search.
     * @param ffacets The facets for which to search.
     * @param aanalyzer The analyzer with which to parse the
     * query.
     * @param ssort The custom sort order.
     * @throws ParseException A fatal exception occurred while
     * parsing the String query.
     */
    public Search createSearch(final String query, final Map<String, Integer> facets, final Analyzer analyzer,
            final Sort sort) throws ParseException {
        search = new Search(query, facets, analyzer, sort);
        return search;
    }

    /**
     * Gets this Searcher's {@link Search} object.  This will be null
     * if the search hasn't been initialized using one of the
     * {@link Searcher#createSearch} methods.
     * @return This Searcher's {@link Search} instance.
     */
    public Search getSearch() {
        return search;
    }

    /**
     * Gets a {@link Collector} instance for the current search that
     * can be used with an {@link IndexSearcher} to return results from
     * the index.
     * @return A {@link Collector} suitable for use with an {@link
     * IndexSearcher}.
     * @throws IOException A fatal exception occurred while interacting with
     * the index.
     */
    public TopFieldCollector getCollector() throws IOException {
        if (search == null) {
            return null;
        } else {
            return search.getCollector();
        }
    }

    /**
     * Given a maximum hit count, returns a Collector instance for the current
     * search that can be used with an IndexSearcher to return results from
     * the index.
     * @param count The maximum hit count, or number of documents, to return
     * from the search.
     * @return A Collector suitable for use with an IndexSearcher.
     * @throws IOException A fatal exception occurred while interacting with
     * the index.
     */
    public TopFieldCollector getCollector(final int count) throws IOException {
        if (search == null) {
            return null;
        } else {
            return search.getCollector(count);
        }
    }

    /**
     * Gets a FacetsCollector instance for the current search.
     * @return The {@link FacetsCollector} containing the requested
     * facets for this search.
     */
    public FacetsCollector getFacetsCollector() {
        if (search == null) {
            return null;
        } else {
            return search.getFacetsCollector(indexReader, taxonomyReader);
        }
    }

    /**
     * Provided a search has been created, this method will execute
     * that search with a given {@link Collector}.
     * @param results The {@link Collector} in which to aggregate results.
     * @throws IOException A fatal exception occurred while interacting
     * with the index.
     */
    public void search(final Collector results) throws IOException {
        if (search != null) {
            indexSearcher.search(search.getQuery(), results);
        }
    }

    /**
     * Provided a search has been created, this method will execute
     * that search and aggregate its results into the Search object's
     * collectors.  These collectors may be accessed using the
     * accessors of the Search object.
     * @throws IOException A fatal exception occurred while interacting
     * with the index.
     */
    public void search() throws IOException {
        if (search != null) {
            final Collector collector = search.getCollector();
            final FacetsCollector facetsCollector = search.getFacetsCollector(indexReader, taxonomyReader);
            indexSearcher.search(search.getQuery(), MultiCollector.wrap(collector, facetsCollector));
        }
    }

    /**
     * Provided a search has been created, this method will execute
     * that search with a non-default max hit count
     * and aggregate its results into the Search object's
     * collectors.  These collectors may be accessed using the
     * accessors of the Search object.
     * @param count The maximum hit count to allow the search to return.
     * @throws IOException A fatal exception occurred while interacting
     * with the index.
     */
    public void search(final int count) throws IOException {
        if (search != null) {
            Collector collector = search.getCollector(count);
            FacetsCollector facetsCollector = search.getFacetsCollector(indexReader, taxonomyReader);
            indexSearcher.search(search.getQuery(), MultiCollector.wrap(collector, facetsCollector));
        }
    }

    /**
     * Obtains a document from the index by numeric ID.  Generally
     * only useful after a search has been executed and a collection
     * of document IDs has been obtained.
     * @param docID The numeric ID of the Document to retrieve from the index.
     * @return The Lucene Document in the index corresponding to the ID.
     * @throws IOException A fatal exception has occurred while interacting
     * with the index.
     */
    public Document doc(final int docID) throws IOException {
        if (indexSearcher != null) {
            return indexSearcher.doc(docID);
        } else {
            return null;
        }
    }

    /**
     * Closes and disposes of open resources.
     * @throws IOException A fatal exception occurred while
     * attempting to close index readers.
     */
    public void dispose() throws IOException {
        if (initialized) {
            indexReader.close();
            taxonomyReader.close();
            initialized = false;
        }
    }
}