package prefuse.data.search; import java.io.IOException; import java.util.logging.Logger; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.search.Hits; import prefuse.data.Tuple; import prefuse.util.StringLib; import prefuse.util.collections.IntObjectHashMap; /** * <p> * SearchTupleSet implementation that performs text searches on indexed Tuple * data using the Lucene search engine. * <a href="http://lucene.apache.org/">Lucene</a> is an open source search * engine supporting full text indexing and keyword search. Please refer to * the Lucene web page for more information. Note that for this class to be * used by prefuse applications, the Lucene classes must be included on the * application classpath. * </p> * * @version 1.0 * @author <a href="http://jheer.org">jeffrey heer</a> * @see prefuse.data.query.SearchQueryBinding */ public class KeywordSearchTupleSet extends SearchTupleSet { private static final Logger s_logger = Logger.getLogger(KeywordSearchTupleSet.class.getName()); protected IntObjectHashMap m_map = new IntObjectHashMap(); protected String m_query = ""; protected LuceneSearcher m_lucene = null; protected boolean m_storeTermVectors = false; protected int m_id = 1; /** * Creates a new KeywordSearchFocusSet using an in-memory search index. */ public KeywordSearchTupleSet() { m_lucene = new LuceneSearcher(); } /** * Creates a new TextSearchFocusSet with the given LuceneSearcher. * @param searcher the {@link LuceneSearcher} to use. */ public KeywordSearchTupleSet(LuceneSearcher searcher) { m_lucene = searcher; } /** * Returns the current search query, if any. * @return the currently active search query */ public String getQuery() { return m_query; } /** * Searches the indexed Tuple fields for matching keywords, using * the Lucene search engine. Matching Tuples are available as the * members of this TupleSet. * @param query the query string to search for */ public void search(String query) { if ( query == null ) query = ""; if ( query.equals(m_query) ) return; // no change Tuple[] rem = clearInternal(); m_query = query; query.trim(); if ( query.length() == 0 ) { this.fireTupleEvent(null, DELETE); return; } m_lucene.setReadMode(true); try { Hits hits = m_lucene.search(query); for ( int i=0; i < hits.length(); i++ ) { Tuple t = getMatchingTuple(hits.doc(i)); addInternal(t); } Tuple[] add = getTupleCount() > 0 ? toArray() : null; fireTupleEvent(add, rem); } catch (ParseException e) { s_logger.warning("Lucene query parse exception.\n"+ StringLib.getStackTrace(e)); } catch (IOException e) { s_logger.warning("Lucene IO exception.\n"+ StringLib.getStackTrace(e)); } } /** * Return the Tuple matching the given Lucene Document, if any. * @param d the Document to lookup. * @return the matching Tuple, or null if none. */ protected Tuple getMatchingTuple(Document d) { int id = Integer.parseInt(d.get(LuceneSearcher.ID)); return (Tuple)m_map.get(id); } /** * @see prefuse.data.search.SearchTupleSet#index(prefuse.data.Tuple, java.lang.String) */ public void index(Tuple t, String field) { m_lucene.setReadMode(false); String s; if ( (s=t.getString(field)) == null ) return; int id = m_id++; m_lucene.addDocument(getDocument(id, s)); m_map.put(id, t); } /** * Returns false, as unindexing values is not currently supported. * @see prefuse.data.search.SearchTupleSet#isUnindexSupported() */ public boolean isUnindexSupported() { return false; } /** * This method throws an exception, as unidexing is not supported. * @see prefuse.data.search.SearchTupleSet#unindex(prefuse.data.Tuple, java.lang.String) * @throws UnsupportedOperationException */ public void unindex(Tuple t, String attrName) { throw new UnsupportedOperationException(); } /** * Create a Lucene Document instance with the given document ID and text. * @param id the document ID * @param text the text the Document should contain * @return a new Lucene Document instance */ protected Document getDocument(int id, String text) { Document d = new Document(); d.add(Field.Text(LuceneSearcher.FIELD, text, m_storeTermVectors)); d.add(Field.Keyword(LuceneSearcher.ID, String.valueOf(id))); return d; } /** * Get the {@link LuceneSearcher} instance used by this class. * @return returns the backing lucene searcher. */ public LuceneSearcher getLuceneSearcher() { return m_lucene; } /** * Returns a copy of the mapping from Lucene document IDs to prefuse Tuple instances. * @return a copy of the map from lucene doc IDs to prefuse Tuples. */ public IntObjectHashMap getTupleMap() { return (IntObjectHashMap)m_map.clone(); } /** * Removes all search hits and clears out the index. * @see prefuse.data.tuple.TupleSet#clear() */ public void clear() { m_lucene = new LuceneSearcher(); super.clear(); } } // end of class KeywordSearchTupleSet