ead.editor.model.ModelIndex.java Source code

Java tutorial

Introduction

Here is the source code for ead.editor.model.ModelIndex.java

Source

/**
 * eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
 *    <e-UCM> research group.
 *
 *    Copyright 2005-2010 <e-UCM> research group.
 *
 *    You can access a list of all the contributors to eAdventure at:
 *          http://e-adventure.e-ucm.es/contributors
 *
 *    <e-UCM> is a research group of the Department of Software Engineering
 *          and Artificial Intelligence at the Complutense University of Madrid
 *          (School of Computer Science).
 *
 *          C Profesor Jose Garcia Santesmases sn,
 *          28040 Madrid (Madrid), Spain.
 *
 *          For more info please visit:  <http://e-adventure.e-ucm.es> or
 *          <http://www.e-ucm.es>
 *
 * ****************************************************************************
 *
 *  This file is part of eAdventure, version 2.0
 *
 *      eAdventure is free software: you can redistribute it and/or modify
 *      it under the terms of the GNU Lesser General Public License as published by
 *      the Free Software Foundation, either version 3 of the License, or
 *      (at your option) any later version.
 *
 *      eAdventure 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 Lesser General Public License for more details.
 *
 *      You should have received a copy of the GNU Lesser General Public License
 *      along with eAdventure.  If not, see <http://www.gnu.org/licenses/>.
 */

package ead.editor.model;

import ead.editor.model.nodes.DependencyNode;
import java.io.IOException;
import java.util.*;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
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.RAMDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Allows easy search operations on the model. Uses Lucene for indexing and
 * retrieval.
 *
 * @author mfreire
 */
public class ModelIndex {

    private static final Logger logger = LoggerFactory.getLogger("ModelIndex");

    public static final String editorIdFieldName = "editor-id";

    /**
     * Lucene index
     */
    private Directory searchIndex;
    /**
     * Lucene updater
     */
    private IndexWriter indexWriter;
    /**
     * Max search hits in an ordered query
     */
    private static final int MAX_SEARCH_HITS = 100;
    /**
     * Query parser for 'all fields' queries
     */
    private QueryParser queryParser;
    /**
     * Field analyzer
     */
    private Analyzer searchAnalyzer;

    /**
    * Configure Lucene indexing
    */
    public ModelIndex() {
        try {
            searchIndex = new RAMDirectory();
            // use a very simple analyzer; no fancy stopwords, stemming, ...
            searchAnalyzer = new WhitespaceAnalyzer(Version.LUCENE_35);
            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, searchAnalyzer);
            indexWriter = new IndexWriter(searchIndex, config);
        } catch (Exception e) {
            logger.error("Could not initialize search index (?)", e);
            throw new IllegalArgumentException("Could not initialize search index (?)", e);
        }
    }

    /**
     * Adds a property to a node.
     * @param e the node
     * @param field name
     * @param value of property
     * @param searchable if this field is to be indexed and used in "anywhere"
     * searches
     */
    public void addProperty(DependencyNode e, String field, String value, boolean searchable) {

        e.getDoc().add(new Field(field, value, Store.YES, searchable ? Index.ANALYZED : Index.NO));
    }

    /**
     * Index an DependencyNode for later search
     */
    public void firstIndexUpdate(Collection<DependencyNode> nodes) {
        for (DependencyNode e : nodes) {
            Document doc = e.getDoc();
            logger.trace("Writing index for {} of class {}",
                    new Object[] { e.getId(), e.getContent().getClass().getSimpleName() });
            try {
                indexWriter.addDocument(doc);
            } catch (Exception ex) {
                logger.error("Error adding search information for node {}", e.getId(), ex);
            }
        }
        try {
            indexWriter.commit();
        } catch (Exception ex) {
            logger.error("Error commiting search information", ex);
        }
    }

    /**
     * Lazily create or return the query parser
     */
    private QueryParser getQueryAllParser() {

        if (queryParser == null) {
            try {
                IndexReader reader = IndexReader.open(searchIndex);

                ArrayList<String> al = new ArrayList<String>(reader.getFieldNames(IndexReader.FieldOption.INDEXED));
                String[] allFields = al.toArray(new String[al.size()]);
                if (logger.isDebugEnabled()) {
                    Arrays.sort(allFields);
                    logger.debug("enumerating indexed fields");
                    for (String name : allFields) {
                        logger.debug("  indexed field: '{}'", name);
                    }
                }
                queryParser = new MultiFieldQueryParser(Version.LUCENE_35, allFields, searchAnalyzer);
            } catch (IOException ioe) {
                logger.error("Error constructing query parser", ioe);
            }
        }
        return queryParser;
    }

    /**
     * Get names of all indexed fields.
     * @return names of all indexed fields.
     */
    public List<String> getIndexedFieldNames() {
        try {
            IndexReader reader = IndexReader.open(searchIndex);
            return new ArrayList<String>(reader.getFieldNames(IndexReader.FieldOption.INDEXED));
        } catch (IOException ioe) {
            throw new IllegalArgumentException("Error finding names of indexable fields", ioe);
        }
    }

    /**
     * Get a (sorted) list of nodes that match a query
     */
    public List<DependencyNode> searchAll(String queryText, Map<Integer, DependencyNode> nodesById) {

        ArrayList<DependencyNode> nodes = new ArrayList<DependencyNode>();
        try {
            IndexReader reader = IndexReader.open(searchIndex);
            Query query = getQueryAllParser().parse(queryText);
            IndexSearcher searcher = new IndexSearcher(reader);
            TopScoreDocCollector collector = TopScoreDocCollector.create(MAX_SEARCH_HITS, true);
            searcher.search(query, collector);
            ScoreDoc[] hits = collector.topDocs().scoreDocs;
            for (ScoreDoc hit : hits) {
                String nodeId = searcher.doc(hit.doc).get(editorIdFieldName);
                nodes.add(nodesById.get(Integer.parseInt(nodeId)));
            }
            searcher.close();
        } catch (Exception e) {
            logger.error("Error parsing or looking up query '{}' in index", queryText, e);
        }

        return nodes;
    }

    /**
     * Get a (sorted) list of nodes that match a query
     */
    public List<DependencyNode> search(String field, String queryText, Map<Integer, DependencyNode> nodesById) {

        ArrayList<DependencyNode> nodes = new ArrayList<DependencyNode>();
        try {
            IndexReader reader = IndexReader.open(searchIndex);
            Query query = new QueryParser(Version.LUCENE_35, field, searchAnalyzer).parse(queryText);
            IndexSearcher searcher = new IndexSearcher(reader);
            TopScoreDocCollector collector = TopScoreDocCollector.create(MAX_SEARCH_HITS, true);
            searcher.search(query, collector);
            ScoreDoc[] hits = collector.topDocs().scoreDocs;
            for (ScoreDoc hit : hits) {
                String nodeId = searcher.doc(hit.doc).get(editorIdFieldName);
                nodes.add(nodesById.get(Integer.parseInt(nodeId)));
            }
            searcher.close();
        } catch (Exception e) {
            logger.error("Error parsing or looking up query '{}' in index", queryText, e);
        }

        return nodes;
    }
}