blob: 762609709a1e3c8cebdb32ab6869a0e3cdf759ef [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.update;
import org.apache.lucene.index.IndexReader.AtomicReaderContext;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.Scorer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Vector;
import java.io.IOException;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.FieldType;
import org.apache.solr.common.SolrException;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.apache.solr.core.*;
/**
* <code>UpdateHandler</code> handles requests to change the index
* (adds, deletes, commits, optimizes, etc).
*
*
* @since solr 0.9
*/
public abstract class UpdateHandler implements SolrInfoMBean {
protected final static Logger log = LoggerFactory.getLogger(UpdateHandler.class);
protected final SolrCore core;
protected final IndexSchema schema;
protected final SchemaField idField;
protected final FieldType idFieldType;
protected final Term idTerm; // prototype term to avoid interning fieldname
protected Vector<SolrEventListener> commitCallbacks = new Vector<SolrEventListener>();
protected Vector<SolrEventListener> optimizeCallbacks = new Vector<SolrEventListener>();
private void parseEventListeners() {
final Class<SolrEventListener> clazz = SolrEventListener.class;
final String label = "Event Listener";
for (PluginInfo info : core.getSolrConfig().getPluginInfos(SolrEventListener.class.getName())) {
String event = info.attributes.get("event");
if ("postCommit".equals(event)) {
SolrEventListener obj = core.createInitInstance(info,clazz,label,null);
commitCallbacks.add(obj);
log.info("added SolrEventListener for postCommit: " + obj);
} else if ("postOptimize".equals(event)) {
SolrEventListener obj = core.createInitInstance(info,clazz,label,null);
optimizeCallbacks.add(obj);
log.info("added SolrEventListener for postOptimize: " + obj);
}
}
}
protected void callPostCommitCallbacks() {
for (SolrEventListener listener : commitCallbacks) {
listener.postCommit();
}
}
protected void callPostOptimizeCallbacks() {
for (SolrEventListener listener : optimizeCallbacks) {
listener.postCommit();
}
}
public UpdateHandler(SolrCore core) {
this.core=core;
schema = core.getSchema();
idField = schema.getUniqueKeyField();
idFieldType = idField!=null ? idField.getType() : null;
idTerm = idField!=null ? new Term(idField.getName(),"") : null;
parseEventListeners();
}
protected SolrIndexWriter createMainIndexWriter(String name, boolean removeAllExisting) throws IOException {
return new SolrIndexWriter(name,core.getNewIndexDir(), core.getDirectoryFactory(), removeAllExisting, schema, core.getSolrConfig().mainIndexConfig, core.getDeletionPolicy(), core.getCodecProvider());
}
protected final Term idTerm(String readableId) {
// to correctly create the Term, the string needs to be run
// through the Analyzer for that field.
return new Term(idField.getName(), idFieldType.toInternal(readableId));
}
protected final String getIndexedId(Document doc) {
if (idField == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Operation requires schema to have a unique key field");
// Right now, single valued fields that require value transformation from external to internal (indexed)
// form have that transformation already performed and stored as the field value.
IndexableField[] id = doc.getFields( idField.getName() );
if (id == null || id.length < 1)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Document is missing mandatory uniqueKey field: " + idField.getName());
if( id.length > 1 )
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Document contains multiple values for uniqueKey field: " + idField.getName());
return idFieldType.storedToIndexed( id[0] );
}
protected final String getIndexedIdOptional(Document doc) {
if (idField == null) return null;
IndexableField f = doc.getField(idField.getName());
if (f == null) return null;
return idFieldType.storedToIndexed(f);
}
public abstract int addDoc(AddUpdateCommand cmd) throws IOException;
public abstract void delete(DeleteUpdateCommand cmd) throws IOException;
public abstract void deleteByQuery(DeleteUpdateCommand cmd) throws IOException;
public abstract int mergeIndexes(MergeIndexesCommand cmd) throws IOException;
public abstract void commit(CommitUpdateCommand cmd) throws IOException;
public abstract void rollback(RollbackUpdateCommand cmd) throws IOException;
public abstract void close() throws IOException;
static class DeleteHitCollector extends Collector {
public int deleted=0;
public final SolrIndexSearcher searcher;
private int docBase;
public DeleteHitCollector(SolrIndexSearcher searcher) {
this.searcher = searcher;
}
@Override
public void collect(int doc) {
try {
searcher.getIndexReader().deleteDocument(doc + docBase);
deleted++;
} catch (IOException e) {
// don't try to close the searcher on failure for now...
// try { closeSearcher(); } catch (Exception ee) { SolrException.log(log,ee); }
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Error deleting doc# "+doc,e,false);
}
}
@Override
public boolean acceptsDocsOutOfOrder() {
return false;
}
@Override
public void setNextReader(AtomicReaderContext context) throws IOException {
docBase = context.docBase;
}
@Override
public void setScorer(Scorer scorer) throws IOException {
}
}
/**
* NOTE: this function is not thread safe. However, it is safe to call within the
* <code>inform( SolrCore core )</code> function for <code>SolrCoreAware</code> classes.
* Outside <code>inform</code>, this could potentially throw a ConcurrentModificationException
*
* @see SolrCoreAware
*/
public void registerCommitCallback( SolrEventListener listener )
{
commitCallbacks.add( listener );
}
/**
* NOTE: this function is not thread safe. However, it is safe to call within the
* <code>inform( SolrCore core )</code> function for <code>SolrCoreAware</code> classes.
* Outside <code>inform</code>, this could potentially throw a ConcurrentModificationException
*
* @see SolrCoreAware
*/
public void registerOptimizeCallback( SolrEventListener listener )
{
optimizeCallbacks.add( listener );
}
}