net.paissad.waqtsalat.utils.geoip.WorldCitiesLucene.java Source code

Java tutorial

Introduction

Here is the source code for net.paissad.waqtsalat.utils.geoip.WorldCitiesLucene.java

Source

/*
 * WaqtSalat, for indicating the muslim prayers times in most cities. Copyright
 * (C) 2011 Papa Issa DIAKHATE (paissad).
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * PLEASE DO NOT REMOVE THIS COPYRIGHT BLOCK.
 */

package net.paissad.waqtsalat.utils.geoip;

import static net.paissad.waqtsalat.WSConstants.LUCENE_INDEX_PATH;
import static net.paissad.waqtsalat.WSConstants.WORLDCITIES_TABLE_NAME;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.Platform;

import net.paissad.waqtsalat.factory.DBConnection;
import net.paissad.waqtsalat.utils.jdbc.JdbcUtils;

/**
 * This class contains convenient methods to create a Lucene search index from
 * GeoIP World-Cities database.
 * <p>
 * <b>Tutorial</b>:<br>
 * <a href="http://www.lucenetutorial.com/lucene-in-5-minutes.html">
 * http://www.lucenetutorial.com/lucene-in-5-minutes.html</a>
 * </p>
 * 
 * @author Papa Issa DIAKHATE (paissad)
 */
public class WorldCitiesLucene {

    private static final String FIELD_LOCATION = "location";
    private static final Version LUCENE_VERSION = Version.LUCENE_30;

    private static Logger logger = LoggerFactory.getLogger(WorldCitiesLucene.class);
    private static Directory indexDir;
    private static StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);

    // =========================================================================

    /**
     * Creates the Lucene index from the database.
     * 
     * @throws SQLException
     * @throws IOException
     * 
     */
    public static void createIndex() throws SQLException, IOException {

        long startTime = System.currentTimeMillis();
        connectToIndex();

        Connection conn = null;
        try {
            conn = DBConnection.getInstance();
            IndexWriter writer = new IndexWriter(getIndexDir(), analyzer, true,
                    IndexWriter.MaxFieldLength.UNLIMITED);
            logger.info("Indexing to directory '{}'.", getIndexDir().toString());
            addDocs(writer, conn);
            writer.optimize();
            writer.close();

        } finally {
            JdbcUtils.closeQuietly(conn);
            closeIndex();
        }

        long endTime = System.currentTimeMillis();
        int duration = (int) ((endTime - startTime) / 1000);
        logger.debug("Lucene index created in {} seconds.", duration);
    }

    // ======================================================================

    /**
     * @param queryStr
     *            The <tt>String</tt>(location) to search into the
     *            index/database.
     * @return A {@link List} of arrays of String containing the results.
     * 
     * @throws IOException
     * @throws ParseException
     * 
     */
    public static List<String[]> search(String queryStr) throws IOException, ParseException {

        try {
            List<String[]> locations = new ArrayList<String[]>();
            connectToIndex();
            int hitsPerPage = 500;

            // TODO: when the String entry is escaped, the search is less
            // productive ...
            // will try to figure it out in the future
            // queryStr = QueryParser.escape(queryStr);

            Query q = new QueryParser(LUCENE_VERSION, FIELD_LOCATION, analyzer).parse(queryStr);
            IndexSearcher searcher = new IndexSearcher(IndexReader.open(getIndexDir()));
            TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);
            searcher.search(q, collector);
            ScoreDoc[] hits = collector.topDocs().scoreDocs;
            logger.debug("Found {} hits.", hits.length);
            for (int i = 0; i < hits.length; i++) {
                int docID = hits[i].doc;
                Document doc = searcher.doc(docID);
                locations.add(doc.getValues(FIELD_LOCATION));
            }
            return locations;

        } finally {
            closeIndex();
        }
    }

    // =========================================================================

    /**
     * Connects to the Lucene index.
     * 
     * @throws IOException
     */
    private static void connectToIndex() throws IOException {
        if (getIndexDir() == null) {
            File destDir = new File(LUCENE_INDEX_PATH);
            if (Platform.isWindows()) {
                setIndexDir(new SimpleFSDirectory(destDir, null));
            } else {
                setIndexDir(new NIOFSDirectory(destDir, null));
            }
        } else {
            logger.trace("Already connected to the lucene index directory ({})", indexDir.toString());
        }
    }

    // =========================================================================

    /**
     * Closes the index if opened.
     * 
     * @throws IOException
     */
    private static void closeIndex() throws IOException {
        if (indexDir != null) {
            indexDir.close();
            indexDir = null;
        }
    }

    // =========================================================================

    /**
     * Adds documents from the database to the Lucene index.
     * <p>
     * <b>Note</b>: The connection is not closed into this method.
     * </p>
     * 
     * @param writer
     * @param conn
     * @throws SQLException
     * @throws IOException
     * 
     */
    private static void addDocs(final IndexWriter writer, final Connection conn) throws SQLException, IOException {

        String sql = "SELECT COUNTRY_NAME, CITY, REGION, LATITUDE, LONGITUDE FROM " + WORLDCITIES_TABLE_NAME + ";";
        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                Document doc = new Document();
                doc.add(new Field(FIELD_LOCATION, rs.getString("COUNTRY_NAME"), Field.Store.YES,
                        Field.Index.ANALYZED));
                doc.add(new Field(FIELD_LOCATION, rs.getString("CITY"), Field.Store.YES, Field.Index.ANALYZED));
                doc.add(new Field(FIELD_LOCATION, rs.getString("REGION"), Field.Store.YES, Field.Index.NO));
                doc.add(new Field(FIELD_LOCATION, rs.getString("LATITUDE"), Field.Store.YES, Field.Index.NO));
                doc.add(new Field(FIELD_LOCATION, rs.getString("LONGITUDE"), Field.Store.YES, Field.Index.NO));
                writer.addDocument(doc);
            }

        } finally {
            JdbcUtils.closeQuietly(stmt);
            JdbcUtils.closeQuietly(rs);
        }
    }

    // =========================================================================
    // Getters / Setters ...

    private static Directory getIndexDir() {
        return indexDir;
    }

    private static void setIndexDir(Directory indexDir) {
        WorldCitiesLucene.indexDir = indexDir;
    }

    // =========================================================================

}