com.jivesoftware.forum.database.DbQuery.java Source code

Java tutorial

Introduction

Here is the source code for com.jivesoftware.forum.database.DbQuery.java

Source

/**
 * $RCSfile: DbQuery.java,v $
 * $Revision: 1.1.1.1 $
 * $Date: 2002/09/09 13:50:57 $
 *
 * New Jive  from Jdon.com.
 *
 * This software is the proprietary information of CoolServlets, Inc.
 * Use is subject to license terms.
 */

package com.jivesoftware.forum.database;

import java.util.Date;
import java.util.Iterator;
import java.io.IOException;
import java.io.File;

import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.standard.*;
import org.apache.lucene.document.*;
import org.apache.lucene.store.*;
import org.apache.lucene.search.*;
import org.apache.lucene.queryParser.*;
import org.apache.lucene.index.*;

import com.jivesoftware.forum.*;

/**
 * Database implementation of the Query interface using Lucene.
 */
public class DbQuery implements com.jivesoftware.forum.Query {

    /**
     * The query String to use for searching. Set it the empty String by
     * default so that if the user fails to set a query String, there won't
     * be errors.
     */
    private String queryString = "";
    private java.util.Date beforeDate = null;
    private java.util.Date afterDate = null;
    private User user = null;
    private ForumThread thread = null;

    /**
     * The results of the query as an array of message ID's.
     */
    private long[] results = null;

    private Forum[] forums;
    private DbForumFactory factory;

    /**
     * The maximum number of results to return with a search.
     */
    private static final int MAX_RESULTS_SIZE = 1500;

    private static String indexPath = null;
    private static IndexReader reader;
    private static Searcher searcher;
    private static Directory searchDirectory = null;
    private static long indexLastModified;

    public DbQuery(Forum[] forums, DbForumFactory factory) {
        this.forums = forums;
        this.factory = factory;
    }

    public void setQueryString(String queryString) {
        this.queryString = queryString;
        //reset results
        results = null;
    }

    public String getQueryString() {
        return queryString;
    }

    public void setBeforeDate(java.util.Date beforeDate) {
        this.beforeDate = beforeDate;
        //reset results
        results = null;
    }

    public java.util.Date getBeforeDate() {
        return beforeDate;
    }

    public void setAfterDate(java.util.Date afterDate) {
        this.afterDate = afterDate;
        //reset results
        results = null;
    }

    public java.util.Date getAfterDate() {
        return afterDate;
    }

    public User getFilteredUser() {
        return user;
    }

    public void filterOnUser(User user) {
        this.user = user;
        results = null;
    }

    public ForumThread getFilteredThread() {
        return thread;
    }

    public void filterOnThread(ForumThread thread) {
        this.thread = thread;
        results = null;
    }

    public int resultCount() {
        if (results == null) {
            executeQuery();
        }
        return results.length;
    }

    public Iterator results() {
        if (results == null) {
            executeQuery();
        }
        return new DatabaseObjectIterator(JiveGlobals.MESSAGE, results, factory);
    }

    public Iterator results(int startIndex, int numResults) {
        if (results == null) {
            executeQuery();
        }

        int endIndex = startIndex + numResults - 1;
        if (endIndex > results.length - 1) {
            endIndex = results.length - 1;
        }

        int length = endIndex - startIndex + 1;
        if (length < 0) {
            return new DatabaseObjectIterator(JiveGlobals.MESSAGE, new long[0], factory);
        } else {
            long[] resultsSubset = new long[length];
            for (int i = 0; i < length; i++) {
                resultsSubset[i] = results[startIndex + i];
            }
            return new DatabaseObjectIterator(JiveGlobals.MESSAGE, resultsSubset, factory);
        }
    }

    /**
     * Execute the query and store the results in the results array.
     */
    private void executeQuery() {
        try {
            Searcher searcher = getSearcher();
            if (searcher == null) {
                // Searcher can be null if the index doesn't exist.
                results = new long[0];
                return;
            }

            org.apache.lucene.search.Query bodyQuery = QueryParser.parse(queryString, "body",
                    DbSearchManager.analyzer);

            org.apache.lucene.search.Query subjectQuery = QueryParser.parse(queryString, "subject",
                    DbSearchManager.analyzer);

            BooleanQuery comboQuery = new BooleanQuery();
            comboQuery.add(subjectQuery, false, false);
            comboQuery.add(bodyQuery, false, false);

            MultiFilter multiFilter = new MultiFilter(3);
            int filterCount = 0;

            // Forum filter -- we can ignore filtering if we are searching all
            // forums in the system.
            if (factory.getForumCount() != forums.length) {
                String[] forumIDs = new String[forums.length];
                for (int i = 0; i < forumIDs.length; i++) {
                    forumIDs[i] = Long.toString(forums[i].getID());
                }
                multiFilter.add(new FieldFilter("forumID", forumIDs));
                filterCount++;
            }

            //Date filter
            if (beforeDate != null || afterDate != null) {
                if (beforeDate != null && afterDate != null) {
                    multiFilter.add(new DateFilter("creationDate", beforeDate, afterDate));
                    filterCount++;
                } else if (beforeDate == null) {
                    multiFilter.add(DateFilter.After("creationDate", afterDate));
                    filterCount++;
                } else {
                    multiFilter.add(DateFilter.Before("creationDate", beforeDate));
                    filterCount++;
                }
            }

            // User filter
            if (user != null) {
                String userID = Long.toString(user.getID());
                multiFilter.add(new FieldFilter("userID", userID));
                filterCount++;
            }

            // Thread filter
            if (thread != null) {
                String threadID = Long.toString(thread.getID());
                multiFilter.add(new FieldFilter("threadID", threadID));
                filterCount++;
            }

            Hits hits;
            // Only apply filters if any are defined.
            if (filterCount > 0) {
                hits = searcher.search(comboQuery, multiFilter);
            } else {
                hits = searcher.search(comboQuery);
            }
            // Don't return more search results than the maximum number allowed.
            int numResults = hits.length() < MAX_RESULTS_SIZE ? hits.length() : MAX_RESULTS_SIZE;
            long[] messages = new long[numResults];
            for (int i = 0; i < numResults; i++) {
                messages[i] = Long.parseLong(((Document) hits.doc(i)).get("messageID"));
            }
            results = messages;
        } catch (Exception e) {
            e.printStackTrace();
            results = new long[0];
        }
    }

    /**
     * Returns a Lucene Searcher that can be used to execute queries. Lucene
     * can handle index reading even while updates occur. However, in order
     * for index changes to be reflected in search results, the reader must
     * be re-opened whenever the modifiedDate changes.<p>
     *
     * The location of the index is the "search" subdirectory in [jiveHome]. If
     * jiveHome is not set correctly, this method will fail.
     *
     * @return a Searcher that can be used to execute queries.
     */
    private static Searcher getSearcher() throws IOException {
        if (indexPath == null) {
            //Get path of where search index should be. It should be
            //the search subdirectory of [jiveHome].
            String jiveHome = JiveGlobals.getJiveHome();
            if (jiveHome == null) {
                System.err.println("ERROR: the jiveHome property is not set.");
                throw new IOException("Unable to open index for searching " + "because jiveHome was not set.");
            }
            indexPath = jiveHome + File.separator + "search";
        }

        if (searcher == null) {
            //Acquire a lock -- analyzer is a convenient object to do this on.
            synchronized (DbSearchManager.analyzer) {
                if (searcher == null) {
                    if (indexExists(indexPath)) {
                        searchDirectory = FSDirectory.getDirectory(indexPath, false);
                        reader = IndexReader.open(searchDirectory);
                        indexLastModified = reader.lastModified(searchDirectory);
                        searcher = new IndexSearcher(reader);
                    }
                    //Otherwise, the index doesn't exist, so return null.
                    else {
                        return null;
                    }
                }
            }
        }
        if (reader.lastModified(searchDirectory) > indexLastModified) {
            synchronized (DbSearchManager.analyzer) {
                if (reader.lastModified(searchDirectory) > indexLastModified) {
                    if (indexExists(indexPath)) {
                        indexLastModified = reader.lastModified(searchDirectory);
                        //We need to close the indexReader because it has changed.
                        //Re-opening it will make changes visible.
                        reader.close();

                        searchDirectory = FSDirectory.getDirectory(indexPath, false);
                        reader = IndexReader.open(searchDirectory);
                        searcher = new IndexSearcher(reader);
                    }
                    //Otherwise, the index doesn't exist, so return null.
                    else {
                        return null;
                    }
                }
            }
        }
        return searcher;
    }

    /**
     * Returns true if the search index exists at the specified path.
     *
     * @param indexPath the path to check for the search index at.
     */
    private static boolean indexExists(String indexPath) {
        //Lucene always creates a file called "segments" -- if it exists, we
        //assume that the search index exists.
        File segments = new File(indexPath + File.separator + "segments");
        return segments.exists();
    }
}