001    /*
002     * $Revision: 14986 $ $Date: 2009-03-20 21:41:45 +0100 (Fri, 20 Mar 2009) $ This file is part of M y C o R e See http://www.mycore.de/ for details. This program
003     * is free software; you can use it, redistribute it and / or modify it under the terms of the GNU General Public License (GPL) as published by the Free
004     * 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,
005     * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
006     * more details. You should have received a copy of the GNU General Public License along with this program, in a file called gpl.txt or license.txt. If not,
007     * write to the Free Software Foundation Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
008     */
009    
010    package org.mycore.services.fieldquery;
011    
012    import java.util.Collection;
013    import java.util.Iterator;
014    import java.util.List;
015    
016    import org.apache.log4j.Logger;
017    
018    import org.mycore.common.MCRCache;
019    import org.mycore.common.MCRConfiguration;
020    import org.mycore.common.events.MCREvent;
021    import org.mycore.common.events.MCREventHandler;
022    import org.mycore.common.events.MCREventHandlerBase;
023    import org.mycore.datamodel.ifs.MCRFile;
024    import org.mycore.datamodel.common.MCRLinkTableManager;
025    import org.mycore.datamodel.metadata.MCRObject;
026    import org.mycore.parsers.bool.MCRCondition;
027    
028    /**
029     * Abstract base class for searchers and indexers. Searcher implementations for a specific backend must be implemented as a subclass. This class implements
030     * MCREventHandler. Indexers can easily be implemented by overwriting the two methods addToIndex and removeFromIndex. Searchers are implemented by overwriting
031     * the method search. Searchers that do not need indexing or do this on their own can simply ignore the add/remove methods.
032     * 
033     * @author Frank Lützenkirchen
034     */
035    public abstract class MCRSearcher extends MCREventHandlerBase implements MCREventHandler {
036        /** The logger */
037        public static Logger LOGGER = Logger.getLogger(MCRSearcher.class);
038    
039        /** The unique searcher ID for this MCRSearcher implementation */
040        protected String ID;
041    
042        /** The prefix of all properties in mycore.properties for this searcher */
043        protected String prefix;
044    
045        /** The ID of the index this searcher handles * */
046        protected String index;
047    
048        protected static MCRCache RETURN_ID_CACHE = new MCRCache(MCRConfiguration.instance().getInt("MCR.Searcher.ReturnID.Cache", 100),
049                "MCRSearcher ReturnID Cache");
050    
051        /**
052         * Initializes the searcher and sets its unique ID.
053         * 
054         * @param ID
055         *            the non-null unique ID of this searcher instance
056         */
057        public void init(String ID) {
058            this.ID = ID;
059            this.prefix = "MCR.Searcher." + ID + ".";
060            this.index = MCRConfiguration.instance().getString(prefix + "Index");
061        }
062    
063        /**
064         * Returns the unique store ID that was set for this store instance
065         * 
066         * @return the unique store ID that was set for this store instance
067         */
068        public String getID() {
069            return ID;
070        }
071    
072        /**
073         * Returns the ID of the index this searcher is configured for.
074         */
075        public String getIndex() {
076            return index;
077        }
078    
079        public String getReturnID(MCRFile file) {
080            // Maybe fieldquery is used in application without link table manager
081            if (MCRConfiguration.instance().getString("MCR.Persistence.LinkTable.Store.Class", null) == null)
082                return file.getID();
083    
084            String ownerID = file.getOwnerID();
085            String returnID = (String) RETURN_ID_CACHE.get(ownerID);
086            if (returnID != null)
087                return returnID;
088    
089            Collection<String> list = MCRLinkTableManager.instance().getSourceOf(ownerID, MCRLinkTableManager.ENTRY_TYPE_DERIVATE);
090            if ((list == null) || (list.isEmpty()))
091                return file.getID();
092    
093            // Return ID of MCRObject this MCRFile belongs to
094            returnID = list.iterator().next();
095            RETURN_ID_CACHE.put(ownerID, returnID);
096            return returnID;
097        }
098    
099        protected void handleFileCreated(MCREvent evt, MCRFile file) {
100            String entryID = file.getID();
101            String returnID = getReturnID(file);
102            List<MCRFieldValue> fields = MCRData2Fields.buildFields(file, index);
103            addToIndex(entryID, returnID, fields);
104        }
105    
106        protected void handleFileUpdated(MCREvent evt, MCRFile file) {
107            String entryID = file.getID();
108            String returnID = getReturnID(file);
109            List<MCRFieldValue> fields = MCRData2Fields.buildFields(file, index);
110            removeFromIndex(entryID);
111            addToIndex(entryID, returnID, fields);
112        }
113    
114        protected void handleFileDeleted(MCREvent evt, MCRFile file) {
115            String entryID = file.getID();
116            removeFromIndex(entryID);
117        }
118    
119        protected void handleFileRepaired(MCREvent evt, MCRFile file) {
120            handleFileUpdated(evt, file);
121        }
122    
123        protected void handleObjectCreated(MCREvent evt, MCRObject obj) {
124            String entryID = obj.getId().getId();
125            List<MCRFieldValue> fields = MCRData2Fields.buildFields(obj, index);
126            addToIndex(entryID, entryID, fields);
127        }
128    
129        protected void handleObjectUpdated(MCREvent evt, MCRObject obj) {
130            String entryID = obj.getId().getId();
131            List<MCRFieldValue> fields = MCRData2Fields.buildFields(obj, index);
132            removeFromIndex(entryID);
133            addToIndex(entryID, entryID, fields);
134        }
135    
136        protected void handleObjectDeleted(MCREvent evt, MCRObject obj) {
137            String entryID = obj.getId().getId();
138            removeFromIndex(entryID);
139        }
140    
141        protected void handleObjectRepaired(MCREvent evt, MCRObject obj) {
142            handleObjectCreated(evt, obj);
143        }
144    
145        protected void undoObjectCreated(MCREvent evt, MCRObject obj) {
146            handleObjectDeleted(evt, obj);
147        }
148    
149        protected void undoObjectDeleted(MCREvent evt, MCRObject obj) {
150            handleObjectCreated(evt, obj);
151        }
152    
153        /**
154         * Adds field values to the search index. Searchers that need an indexer must overwrite this method to store the values in their backend index. If this
155         * class is configured as event handler, this method is automatically called when objects are created or updated. The field values have been extracted from
156         * the object's data as defined by searchfields.xml
157         * 
158         * @param entryID
159         *            the unique ID of this entry in the index
160         * @param returnID
161         *            the ID to return as result of a search (MCRHit ID)
162         * @param fields
163         *            a List of MCRFieldValue objects
164         */
165        public void addToIndex(String entryID, String returnID, List<MCRFieldValue> fields) {
166        }
167    
168        /**
169         * Removes the values of the given entry from the backend index. Searchers that need an indexer must overwrite this method to delete the values in their
170         * backend index. If this class is configured as event handler, this method is automatically called when objects are deleted or updated.
171         * 
172         * @param entryID
173         *            the unique ID of this entry in the index
174         */
175        public void removeFromIndex(String entryID) {
176        }
177    
178        /**
179         * Executes a query on this searcher. The query MUST only refer to fields that are managed by this searcher.
180         * 
181         * @param cond
182         *            the query condition
183         * @param maxResults
184         *            the maximum number of results to return, 0 means all results
185         * @param sortBy
186         *            a not-null list of MCRSortBy sort criteria. The list is empty if the results should not be sorted
187         * @param addSortData
188         *            if false, backend should sort results itself while executing the query. If this is not possible or the parameter is true, backend should not
189         *            sort the results itself, but only store the data of the fields in the sortBy list which are needed to sort later
190         * @return the query results
191         */
192        public abstract MCRResults search(MCRCondition condition, int maxResults, List<MCRSortBy> sortBy, boolean addSortData);
193    
194        /**
195         * Adds field values needed for sorting for those hits that do not have sort data set already. Subclasses must overwrite this method, otherwise sorting
196         * results will not always work correctly. The default implementation in this class does nothing.
197         * 
198         * @param hits
199         *            the MCRHit objects that do not have sort data set
200         * @param sortBy
201         *            the MCRFieldDef fields that are sort criteria
202         */
203        public void addSortData(Iterator<MCRHit> hits, List<MCRSortBy> sortBy) {
204        }
205    
206        /**
207         * Removes all entries from index.
208         */
209        public void clearIndex() {
210        }
211    
212        /**
213         * Removes all entries of a field with a given value from index.
214         */
215        public void clearIndex(String fieldname, String value) {
216        }
217    
218        /**
219         * Inform Searcher what is going on. Searcher can use this to speed up indexing. MCRLuceneSearcher for example uses a Ramdirectory rebuild insert ... finish
220         */
221        public void notifySearcher(String mode) {
222        }
223    
224    }