Index: topia-security/src/java/org/codelutin/topia/index/IndexEntry.java diff -u /dev/null topia-security/src/java/org/codelutin/topia/index/IndexEntry.java:1.1 --- /dev/null Tue Oct 17 13:50:45 2006 +++ topia-security/src/java/org/codelutin/topia/index/IndexEntry.java Tue Oct 17 13:50:39 2006 @@ -0,0 +1,93 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * IndexEntry.java + * + * Created: 13 oct. 06 11:07:50 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/17 13:50:39 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.index; + + +/** + * This class permit to store on result search with score + * + * @author poussin + */ +public class IndexEntry implements Comparable { + + float score; + String topiaId; + + /** + * + */ + public IndexEntry(float score, String topiaId) { + this.score = score; + this.topiaId = topiaId; + } + + /** + * @return the score + */ + public float getScore() { + return this.score; + } + + /** + * @return the topiaId + */ + public String getTopiaId() { + return this.topiaId; + } + + /** + * Order is inverse of natural order, 1 compareTo 0 return -1 not 1 + * this permit to have the stronger score at beginning of sorted list + * + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(IndexEntry o) { + if (getScore() < o.getScore()) { + return 1; + } else if (getScore() > o.getScore()) { + return -1; + } else { + return 0; + } + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + String result = "score: " + getScore() + " topiaId: " + getTopiaId(); + return result; + } +} + + Index: topia-security/src/java/org/codelutin/topia/index/LuceneIndexer.java diff -u /dev/null topia-security/src/java/org/codelutin/topia/index/LuceneIndexer.java:1.1 --- /dev/null Tue Oct 17 13:50:45 2006 +++ topia-security/src/java/org/codelutin/topia/index/LuceneIndexer.java Tue Oct 17 13:50:39 2006 @@ -0,0 +1,323 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * LuceneIndexer.java + * + * Created: 8 oct. 06 15:48:37 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/17 13:50:39 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.index; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.SimpleAnalyzer; +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.index.Term; +import org.apache.lucene.queryParser.ParseException; +import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Searcher; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.framework.TopiaContextImplementor; +import org.codelutin.topia.persistence.TopiaId; +import org.hibernate.HibernateException; +import org.hibernate.metadata.ClassMetadata; + +/** + * To use this indexer you must have two properties defined in config file: + *
  • topia.index.engin=org.codelutin.topia.framework.LuceneIndexer + *
  • topia.index.lucene.directory=[/path/to/index/directory] + * + * Pour l'utilisation: + *
    + * SortedSet<IndexEntry> result = context.getIndexEngin().search("quelque chose")
    + * SortedSet<IndexEntry> result = context.getIndexEngin().search("class:org.codelutin.chorem.entities.Person name:poussin")
    + * // ou equivalent avec une map 
    + * Map m = new HashMap();
    + * m.put("class", "org.codelutin.chorem.entities.Person");
    + * m.put("name", "poussin");
    + * SortedSet<IndexEntry> result = context.getIndexEngin().search(m);
    + * 
    + * + * @author poussin + * + */ +public class LuceneIndexer implements TopiaIndexImplementor { + + private static final String TOPIA_ID = "topiaId"; + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(LuceneIndexer.class); + + protected File directory = null; + protected TopiaContextImplementor context; + /** contient les objets a reindexer car creer, modifier ou supprimer. key: id, value: fields values or null for deletion */ + protected Map indexationMap = new HashMap(); + + /** + * + */ + public LuceneIndexer() { + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaService#getServiceName() + */ + public String getServiceName() { + return TopiaIndexHelper.SERVICE_NAME; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaService#getPersistenceClasses() + */ + public String getPersistenceClasses() { + return null; + } + + public void recordForIndexation(Object id, Object [] fields) { + if (id == null) { + log.warn("Id must not be null, this object will be not indexed"); + } else { + indexationMap.put(id, fields); + } + } + + public void init(TopiaContextImplementor context) { + this.context = context; + Properties prop = context.getConfig(); + String dirname = prop.getProperty("topia.index.lucene.directory"); + directory = new File(dirname); + directory.mkdirs(); + } + + public void clearLastRecordedIndexation() { + indexationMap.clear(); + } + + public void doIndexation() { + try { + boolean create = false; + if (!IndexReader.indexExists(directory)) { + // si l'index n'existe pas, on force la creation + create = true; + } else { + IndexReader reader = IndexReader.open(directory); + + for (Map.Entry e : indexationMap.entrySet()) { + String id = e.getKey().toString(); + removeIndex(reader, id); + } + reader.close(); + } + + IndexWriter writer = new IndexWriter(directory, new SimpleAnalyzer(), create); + for (Map.Entry e : indexationMap.entrySet()) { + String id = e.getKey().toString(); + Object [] fields = e.getValue(); + if (fields != null) { + try { + index(writer, id, fields); + } catch (HibernateException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't index: " + id); + if (log.isDebugEnabled()) { + log.debug("StackTrace", eee); + } + } + } catch (TopiaNotFoundException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't index: " + id); + if (log.isDebugEnabled()) { + log.debug("StackTrace", eee); + } + } + } + } + } + writer.close(); + } catch (IOException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't index"); + if (log.isDebugEnabled()) { + log.debug("StackTrace", eee); + } + } + } + } + + /** + * @param id identifiant de l'objet a indexer + * @param fields les champs de l'objet + * @throws IOException + * @throws TopiaNotFoundException + * @throws HibernateException + */ + private void index(IndexWriter writer, String id, Object [] fields) throws IOException, HibernateException, TopiaNotFoundException { + String classname = TopiaId.getClassNameAsString(id); + ClassMetadata cm = context.getHibernateFactory().getClassMetadata(classname + "Impl"); + String [] names = cm.getPropertyNames(); + + org.apache.lucene.document.Document doc = new org.apache.lucene.document.Document(); + doc.add(new Field("class", classname, Field.Store.YES, Field.Index.TOKENIZED)); + doc.add(new Field("topiaId", id, Field.Store.YES, Field.Index.TOKENIZED)); + StringBuffer all = new StringBuffer(); + for (int i=0; i 0) { + doc.add(new Field("__all__", all.toString(), Field.Store.NO, Field.Index.TOKENIZED)); + } + writer.addDocument(doc); + } + + /** + * @param ididentifiant de l'objet a supprimer + * @throws IOException + */ + private void removeIndex(IndexReader reader, String id) throws IOException { + Term term = new Term(TOPIA_ID, id); + reader.deleteDocuments(term); + } + + public SortedSet search(String queryText) { + Hits hits = null; + if (IndexReader.indexExists(directory)) { + try { + Searcher searcher = new IndexSearcher(directory.getAbsolutePath()); + Analyzer analyzer = new SimpleAnalyzer(); + + if (queryText.equals("") || queryText.length() == 0) { + // la chaine passée en parametre est vide ! + log.debug("requete vide, pas de resultat a renvoyer."); + } else { + QueryParser parser = new QueryParser("__all__", analyzer); + Query luceneQuery = parser.parse(queryText); + if (log.isDebugEnabled()) { + log.debug("Recherche du terme : " + + luceneQuery.toString()); + } + hits = searcher.search(luceneQuery); + searcher.close(); + } + } catch (IOException ioe) { + if (log.isDebugEnabled()) { + log.debug(ioe.getMessage(), ioe); + } + } catch (ParseException pe) { + if (log.isDebugEnabled()) { + log.debug(pe.getMessage(), pe); + } + } + } else { + // l'index n'a pas encore ete créé ! + if (log.isDebugEnabled()) { + log.debug("Index inexistant, pas de resultats à renvoyer !"); + } + } + //retourne les resultats trouves + TreeSet result = new TreeSet(); + if (hits != null) { + for (int i=0; i search(Map query) { + SortedSet result = null; + // construction de la requete lucene + String queryText = ""; + Iterator keys = query.keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + String value = (String) query.get(key); + if (value != null) { + // on decoupe la valeur pour separer les chaines, + // sinon lucene cherche la chaine complete au lieu + // des mots + String[] st = value.split("\\s"); + for (int i = 0; i < st.length; i++) { + String token = st[i]; + if ((token != null) && (!token.equals(""))) { + queryText += key + ":" + st[i] + " "; + } + } + } + } + result = search(queryText); + + //retourne les resultats trouves + return result; + } +} + + Index: topia-security/src/java/org/codelutin/topia/index/NotIndexable.java diff -u /dev/null topia-security/src/java/org/codelutin/topia/index/NotIndexable.java:1.1 --- /dev/null Tue Oct 17 13:50:45 2006 +++ topia-security/src/java/org/codelutin/topia/index/NotIndexable.java Tue Oct 17 13:50:39 2006 @@ -0,0 +1,44 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * NotIndexable.java + * + * Created: 13 oct. 06 20:51:59 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/17 13:50:39 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.index; + + +/** + * All entity that you don't want to be indexed must implements this interface + * + * @author poussin + */ +public interface NotIndexable { + +} + + Index: topia-security/src/java/org/codelutin/topia/index/TopiaIndexHelper.java diff -u /dev/null topia-security/src/java/org/codelutin/topia/index/TopiaIndexHelper.java:1.1 --- /dev/null Tue Oct 17 13:50:45 2006 +++ topia-security/src/java/org/codelutin/topia/index/TopiaIndexHelper.java Tue Oct 17 13:50:39 2006 @@ -0,0 +1,62 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * IndexHelper.java + * + * Created: 16 oct. 06 19:40:40 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/17 13:50:39 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.index; + +import org.codelutin.topia.TopiaContext; +import org.codelutin.topia.framework.TopiaContextImplementor; +import org.codelutin.topia.history.TopiaHistoryService; + + +/** + * @author poussin + * + */ + +public class TopiaIndexHelper { + + public static final String SERVICE_NAME = "index"; + + static public boolean indexEnabled(TopiaContext context) { + TopiaContextImplementor tci = (TopiaContextImplementor)context; + boolean result = tci.serviceEnabled(SERVICE_NAME); + return result; + } + + public static TopiaIndexService get(TopiaContext context) { + TopiaContextImplementor tci = (TopiaContextImplementor)context; + TopiaIndexService result = (TopiaIndexService)tci.getService(SERVICE_NAME); + return result; + } + +} + + Index: topia-security/src/java/org/codelutin/topia/index/TopiaIndexImplementor.java diff -u /dev/null topia-security/src/java/org/codelutin/topia/index/TopiaIndexImplementor.java:1.1 --- /dev/null Tue Oct 17 13:50:45 2006 +++ topia-security/src/java/org/codelutin/topia/index/TopiaIndexImplementor.java Tue Oct 17 13:50:39 2006 @@ -0,0 +1,62 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * IndexEnginImplementor.java + * + * Created: 13 oct. 06 11:38:28 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/17 13:50:39 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.index; + + + +/** + * Class used internaly in framework, when you want implement new indexation + * engin, you must used this interface + * + * @author poussin + */ +public interface TopiaIndexImplementor extends TopiaIndexService { + + /** + * During object modification in transaction, this method is called to + * store all modification, but modification must no be done on index + * + * @param id topiaId object + * @param fields object field value + */ + public void recordForIndexation(Object id, Object [] fields); + /** + * This method is called after commit, to indicate to index all record + * entry add with addForIndexation method + */ + public void doIndexation(); + /** + * called after commit and rollback to clear the modification record + */ + public void clearLastRecordedIndexation(); + +} Index: topia-security/src/java/org/codelutin/topia/index/TopiaIndexService.java diff -u /dev/null topia-security/src/java/org/codelutin/topia/index/TopiaIndexService.java:1.1 --- /dev/null Tue Oct 17 13:50:45 2006 +++ topia-security/src/java/org/codelutin/topia/index/TopiaIndexService.java Tue Oct 17 13:50:39 2006 @@ -0,0 +1,84 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * IndexEngin.java + * + * Created: 8 oct. 06 17:15:00 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/17 13:50:39 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.index; + +import java.util.Map; +import java.util.SortedSet; + +import org.codelutin.topia.framework.TopiaService; + + +/** + * User visible interface for indexation engin + * + * To use indexer you must have properties defined in config file: + *
  • topia.index.engin=[class used to indexation] + *
  • possible specific property for index engin used + * + * Usage example: + *
    + * SortedSet<IndexEntry> result = context.getIndexEngin().search("quelque chose")
    + * SortedSet<IndexEntry> result = context.getIndexEngin().search("class:org.codelutin.chorem.entities.Person name:poussin")
    + * // or with map 
    + * Map m = new HashMap();
    + * m.put("class", "org.codelutin.chorem.entities.Person");
    + * m.put("name", "poussin");
    + * SortedSet<IndexEntry> result = context.getIndexEngin().search(m);
    + * 
    + * + + * @author poussin + */ +public interface TopiaIndexService extends TopiaService { + + /** + * Permet de faire une recherche, par exemple: + * + * class:org.codelutin.chorem.entities.Person name:poussin + * + * @param queryText + * @return une list triée, le premier élement a le plus gros score + */ + public SortedSet search(String queryText); + + /** + * Permet de faire une recherche + * + * @param query la cle de la map est le nom du champ sur lequel faire la + * recherche, et la valeur la valeur souhaitée pour ce champs + * @return une list triée, le premier élement a le plus gros score + */ + public SortedSet search(Map query); + +} + +