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 }