001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.update;
017:
018: import org.apache.lucene.index.Term;
019: import org.apache.lucene.document.Document;
020: import org.apache.lucene.document.Field;
021: import org.apache.lucene.document.Fieldable;
022: import org.apache.lucene.search.HitCollector;
023: import org.w3c.dom.NodeList;
024: import org.w3c.dom.Node;
025:
026: import java.util.logging.Logger;
027: import java.util.Vector;
028: import java.io.IOException;
029:
030: import org.apache.solr.search.SolrIndexSearcher;
031: import org.apache.solr.util.DOMUtil;
032: import org.apache.solr.schema.IndexSchema;
033: import org.apache.solr.schema.SchemaField;
034: import org.apache.solr.schema.FieldType;
035: import org.apache.solr.core.*;
036:
037: import javax.xml.xpath.XPathConstants;
038:
039: /**
040: * <code>UpdateHandler</code> handles requests to change the index
041: * (adds, deletes, commits, optimizes, etc).
042: *
043: * @author yonik
044: * @version $Id: UpdateHandler.java 542679 2007-05-29 22:28:21Z ryan $
045: * @since solr 0.9
046: */
047:
048: public abstract class UpdateHandler implements SolrInfoMBean {
049: protected final static Logger log = Logger
050: .getLogger(UpdateHandler.class.getName());
051:
052: protected final SolrCore core;
053: protected final IndexSchema schema;
054:
055: protected final SchemaField idField;
056: protected final FieldType idFieldType;
057:
058: protected Vector<SolrEventListener> commitCallbacks = new Vector<SolrEventListener>();
059: protected Vector<SolrEventListener> optimizeCallbacks = new Vector<SolrEventListener>();
060:
061: private void parseEventListeners() {
062: NodeList nodes = (NodeList) SolrConfig.config.evaluate(
063: "updateHandler/listener[@event=\"postCommit\"]",
064: XPathConstants.NODESET);
065: if (nodes != null) {
066: for (int i = 0; i < nodes.getLength(); i++) {
067: Node node = nodes.item(i);
068: try {
069: String className = DOMUtil.getAttr(node, "class");
070: SolrEventListener listener = (SolrEventListener) Config
071: .newInstance(className);
072: listener.init(DOMUtil.childNodesToNamedList(node));
073: // listener.init(DOMUtil.toMapExcept(node.getAttributes(),"class","synchronized"));
074: commitCallbacks.add(listener);
075: log.info("added SolrEventListener for postCommit: "
076: + listener);
077: } catch (Exception e) {
078: throw new SolrException(
079: SolrException.ErrorCode.SERVER_ERROR,
080: "error parsing event listevers", e, false);
081: }
082: }
083: }
084: nodes = (NodeList) SolrConfig.config.evaluate(
085: "updateHandler/listener[@event=\"postOptimize\"]",
086: XPathConstants.NODESET);
087: if (nodes != null) {
088: for (int i = 0; i < nodes.getLength(); i++) {
089: Node node = nodes.item(i);
090: try {
091: String className = DOMUtil.getAttr(node, "class");
092: SolrEventListener listener = (SolrEventListener) Config
093: .newInstance(className);
094: listener.init(DOMUtil.childNodesToNamedList(node));
095: optimizeCallbacks.add(listener);
096: log
097: .info("added SolarEventListener for postOptimize: "
098: + listener);
099: } catch (Exception e) {
100: throw new SolrException(
101: SolrException.ErrorCode.SERVER_ERROR,
102: "error parsing event listeners", e, false);
103: }
104: }
105: }
106: }
107:
108: protected void callPostCommitCallbacks() {
109: for (SolrEventListener listener : commitCallbacks) {
110: listener.postCommit();
111: }
112: }
113:
114: protected void callPostOptimizeCallbacks() {
115: for (SolrEventListener listener : optimizeCallbacks) {
116: listener.postCommit();
117: }
118: }
119:
120: public UpdateHandler(SolrCore core) {
121: this .core = core;
122: schema = core.getSchema();
123: idField = schema.getUniqueKeyField();
124: idFieldType = idField != null ? idField.getType() : null;
125:
126: parseEventListeners();
127: SolrInfoRegistry.getRegistry().put("updateHandler", this );
128: }
129:
130: protected SolrIndexWriter createMainIndexWriter(String name)
131: throws IOException {
132: SolrIndexWriter writer = new SolrIndexWriter(name, core
133: .getIndexDir(), false, schema, SolrCore.mainIndexConfig);
134: return writer;
135: }
136:
137: protected final Term idTerm(String readableId) {
138: // to correctly create the Term, the string needs to be run
139: // through the Analyzer for that field.
140: return new Term(idField.getName(), idFieldType
141: .toInternal(readableId));
142: }
143:
144: protected final String getIndexedId(Document doc) {
145: if (idField == null)
146: throw new SolrException(
147: SolrException.ErrorCode.BAD_REQUEST,
148: "Operation requires schema to have a unique key field");
149:
150: // Right now, single valued fields that require value transformation from external to internal (indexed)
151: // form have that transformation already performed and stored as the field value.
152: Fieldable[] id = doc.getFieldables(idField.getName());
153: if (id == null || id.length < 1)
154: throw new SolrException(
155: SolrException.ErrorCode.BAD_REQUEST,
156: "Document is missing uniqueKey field "
157: + idField.getName());
158: if (id.length > 1)
159: throw new SolrException(
160: SolrException.ErrorCode.BAD_REQUEST,
161: "Document specifies multiple unique ids! "
162: + idField.getName());
163:
164: return idFieldType.storedToIndexed(id[0]);
165: }
166:
167: protected final String getIndexedIdOptional(Document doc) {
168: if (idField == null)
169: return null;
170: Field f = doc.getField(idField.getName());
171: if (f == null)
172: return null;
173: return idFieldType.storedToIndexed(f);
174: }
175:
176: public abstract int addDoc(AddUpdateCommand cmd) throws IOException;
177:
178: public abstract void delete(DeleteUpdateCommand cmd)
179: throws IOException;
180:
181: public abstract void deleteByQuery(DeleteUpdateCommand cmd)
182: throws IOException;
183:
184: public abstract void commit(CommitUpdateCommand cmd)
185: throws IOException;
186:
187: public abstract void close() throws IOException;
188:
189: class DeleteHitCollector extends HitCollector {
190: public int deleted = 0;
191: public final SolrIndexSearcher searcher;
192:
193: public DeleteHitCollector(SolrIndexSearcher searcher) {
194: this .searcher = searcher;
195: }
196:
197: public void collect(int doc, float score) {
198: try {
199: searcher.getReader().deleteDocument(doc);
200: deleted++;
201: } catch (IOException e) {
202: // don't try to close the searcher on failure for now...
203: // try { closeSearcher(); } catch (Exception ee) { SolrException.log(log,ee); }
204: throw new SolrException(
205: SolrException.ErrorCode.SERVER_ERROR,
206: "Error deleting doc# " + doc, e, false);
207: }
208: }
209: }
210:
211: }
|