SOLR-619 -- refactored IndexSchema so it allows registering copy fields within SolrCoreAware.inform()

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@678050 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan McKinley 2008-07-18 21:43:42 +00:00
parent ae123e592a
commit bed156e2e1
2 changed files with 169 additions and 86 deletions

View File

@ -152,15 +152,29 @@ public final class IndexSchema {
* <p>
* Modifying this Map (or any item in it) will affect the real schema
* </p>
*
* <p>
* NOTE: this function is not thread safe. However, it is safe to use within the standard
* <code>inform( SolrCore core )</code> function for <code>SolrCoreAware</code> classes.
* Outside <code>inform</code>, this could potentially throw a ConcurrentModificationException
* </p>
*/
public Map<String,SchemaField> getFields() { return fields; }
/**
* Provides direct access to the Map containing all Field Types
* in the index, keyed on fild type name.
* in the index, keyed on field type name.
*
* <p>
* Modifying this Map (or any item in it) will affect the real schema
* Modifying this Map (or any item in it) will affect the real schema. However if you
* make any modifications, be sure to call {@link IndexSchema#refreshAnalyzers()} to
* update the Analyzers for the registered fields.
* </p>
*
* <p>
* NOTE: this function is not thread safe. However, it is safe to use within the standard
* <code>inform( SolrCore core )</code> function for <code>SolrCoreAware</code> classes.
* Outside <code>inform</code>, this could potentially throw a ConcurrentModificationException
* </p>
*/
public Map<String,FieldType> getFieldTypes() { return fieldTypes; }
@ -287,8 +301,19 @@ public final class IndexSchema {
}
return f;
}
/**
* This will re-create the Analyzers. If you make any modifications to
* the Field map ({@link IndexSchema#getFields()}, this function is required
* to synch the internally cached field analyzers.
*
* @since solr 1.3
*/
public void refreshAnalyzers()
{
analyzer = new SolrIndexAnalyzer();
queryAnalyzer = new SolrQueryAnalyzer();
}
private class SolrIndexAnalyzer extends Analyzer {
protected final HashMap<String,Analyzer> analyzers;
@ -567,9 +592,8 @@ public final class IndexSchema {
/////////////// parse out copyField commands ///////////////
// Map<String,ArrayList<SchemaField>> cfields = new HashMap<String,ArrayList<SchemaField>>();
// expression = "/schema/copyField";
ArrayList<DynamicCopy> dCopies = new ArrayList<DynamicCopy>();
dynamicCopyFields = new DynamicCopy[] {};
expression = "//copyField";
nodes = (NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
@ -580,48 +604,7 @@ public final class IndexSchema {
String source = DOMUtil.getAttr(attrs,"source","copyField definition");
String dest = DOMUtil.getAttr(attrs,"dest", "copyField definition");
boolean sourceIsPattern = isWildCard(source);
boolean destIsPattern = isWildCard(dest);
log.fine("copyField source='"+source+"' dest='"+dest+"'");
SchemaField d = getField(dest);
if(sourceIsPattern) {
if( destIsPattern ) {
DynamicField df = null;
for( DynamicField dd : dynamicFields ) {
if( dd.regex.equals( dest ) ) {
df = dd;
break;
}
}
if( df == null ) {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "copyField dynamic destination must match a dynamicField." );
}
dCopies.add(new DynamicDestCopy(source, df ));
}
else {
dCopies.add(new DynamicCopy(source, d));
}
}
else if( destIsPattern ) {
String msg = "copyField only supports a dynamic destination if the source is also dynamic" ;
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
}
else {
// retrieve the field to force an exception if it doesn't exist
SchemaField f = getField(source);
SchemaField[] destArr = copyFields.get(source);
if (destArr==null) {
destArr=new SchemaField[]{d};
} else {
destArr = (SchemaField[])append(destArr,d);
}
copyFields.put(source,destArr);
copyFieldTargetCounts.put(d, (copyFieldTargetCounts.containsKey(d) ? copyFieldTargetCounts.get(d) + 1 : 1));
}
registerCopyField(source, dest);
}
for (Map.Entry<SchemaField, Integer> entry : copyFieldTargetCounts.entrySet()) {
@ -632,11 +615,6 @@ public final class IndexSchema {
}
}
log.finest("Dynamic Copied Fields:" + dCopies);
// stuff it in a normal array for faster access
dynamicCopyFields = (DynamicCopy[])dCopies.toArray(new DynamicCopy[dCopies.size()]);
} catch (SolrException e) {
SolrConfig.severeErrors.add( e );
throw e;
@ -646,13 +624,86 @@ public final class IndexSchema {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Schema Parsing Failed",e,false);
}
analyzer = new SolrIndexAnalyzer();
queryAnalyzer = new SolrQueryAnalyzer();
// create the field analyzers
refreshAnalyzers();
}
/**
* <p>
* NOTE: this function is not thread safe. However, it is safe to use within the standard
* <code>inform( SolrCore core )</code> function for <code>SolrCoreAware</code> classes.
* Outside <code>inform</code>, this could potentially throw a ConcurrentModificationException
* </p>
*
* @see SolrCoreAware
*/
public void registerCopyField( String source, String dest )
{
boolean sourceIsPattern = isWildCard(source);
boolean destIsPattern = isWildCard(dest);
log.fine("copyField source='"+source+"' dest='"+dest+"'");
SchemaField d = getField(dest);
if(sourceIsPattern) {
if( destIsPattern ) {
DynamicField df = null;
for( DynamicField dd : dynamicFields ) {
if( dd.regex.equals( dest ) ) {
df = dd;
break;
}
}
if( df == null ) {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "copyField dynamic destination must match a dynamicField." );
}
registerDynamicCopyField(new DynamicDestCopy(source, df ));
}
else {
registerDynamicCopyField(new DynamicCopy(source, d));
}
}
else if( destIsPattern ) {
String msg = "copyField only supports a dynamic destination if the source is also dynamic" ;
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
}
else {
// retrieve the field to force an exception if it doesn't exist
SchemaField f = getField(source);
SchemaField[] destArr = copyFields.get(source);
if (destArr==null) {
destArr=new SchemaField[]{d};
} else {
destArr = (SchemaField[])append(destArr,d);
}
copyFields.put(source,destArr);
copyFieldTargetCounts.put(d, (copyFieldTargetCounts.containsKey(d) ? copyFieldTargetCounts.get(d) + 1 : 1));
}
}
private void registerDynamicCopyField( DynamicCopy dcopy )
{
if( dynamicCopyFields == null ) {
dynamicCopyFields = new DynamicCopy[] {dcopy};
}
else {
int i=0;
DynamicCopy[] old = dynamicCopyFields;
dynamicCopyFields = new DynamicCopy[dynamicCopyFields.length+1];
for( DynamicCopy dc : old ) {
dynamicCopyFields[i++] = dc;
}
dynamicCopyFields[i++] = dcopy;
old = null;
}
log.finest("Dynamic Copy Field:" + dcopy );
}
private static Object[] append(Object[] orig, Object item) {
Object[] newArr = (Object[])java.lang.reflect.Array.newInstance(orig.getClass().getComponentType(), orig.length+1);
System.arraycopy(orig, 0, newArr, 0, orig.length);
System.arraycopy(orig, 0, newArr, 0, orig.length);
newArr[orig.length] = item;
return newArr;
}
@ -864,18 +915,18 @@ public final class IndexSchema {
private DynamicField[] dynamicFields;
public SchemaField[] getDynamicFieldPrototypes() {
SchemaField[] df = new SchemaField[dynamicFields.length];
for (int i=0;i<dynamicFields.length;i++) {
df[i] = dynamicFields[i].prototype;
}
return df;
SchemaField[] df = new SchemaField[dynamicFields.length];
for (int i=0;i<dynamicFields.length;i++) {
df[i] = dynamicFields[i].prototype;
}
return df;
}
public String getDynamicPattern(String fieldName) {
for (DynamicField df : dynamicFields) {
if (df.matches(fieldName)) return df.regex;
}
return null;
for (DynamicField df : dynamicFields) {
if (df.matches(fieldName)) return df.regex;
}
return null;
}
/**
@ -1024,19 +1075,19 @@ public final class IndexSchema {
*/
public SchemaField[] getCopySources(String destField) {
SchemaField f = getField(destField);
if (!isCopyFieldTarget(f)) {
return new SchemaField[0];
}
List<SchemaField> sf = new ArrayList<SchemaField>();
for (Map.Entry<String, SchemaField[]> cfs : copyFields.entrySet()) {
for (SchemaField cf : cfs.getValue()) {
if (cf.getName().equals(destField)) {
sf.add(getField(cfs.getKey()));
}
}
}
return sf.toArray(new SchemaField[1]);
SchemaField f = getField(destField);
if (!isCopyFieldTarget(f)) {
return new SchemaField[0];
}
List<SchemaField> sf = new ArrayList<SchemaField>();
for (Map.Entry<String, SchemaField[]> cfs : copyFields.entrySet()) {
for (SchemaField cf : cfs.getValue()) {
if (cf.getName().equals(destField)) {
sf.add(getField(cfs.getKey()));
}
}
}
return sf.toArray(new SchemaField[1]);
}
/**
* Get all copy fields, both the static and the dynamic ones.
@ -1091,10 +1142,3 @@ public final class IndexSchema {
}
}

View File

@ -20,8 +20,10 @@ package org.apache.solr.schema;
import java.util.HashMap;
import java.util.Map;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest;
@ -90,4 +92,41 @@ public class IndexSchemaTest extends AbstractSolrTestCase {
assertTrue("wrong class", similarity instanceof MockConfigurableSimilarity);
assertEquals("is there an echo?", ((MockConfigurableSimilarity)similarity).getPassthrough());
}
public void testRuntimeFieldCreation()
{
// any field manipulation needs to happen when you know the core will not
// be accepting any requests. Typically this is done within the inform()
// method. Since this is a single threaded test, we can change the fields
// willi-nilly
SolrCore core = h.getCore();
IndexSchema schema = core.getSchema();
final String fieldName = "runtimefield";
SchemaField sf = new SchemaField( fieldName, schema.getFieldTypes().get( "string" ) );
schema.getFields().put( fieldName, sf );
// also register a new copy field (from our new field)
schema.registerCopyField( fieldName, "dynamic_runtime" );
schema.refreshAnalyzers();
assertU(adoc("id", "10", "title", "test", fieldName, "aaa"));
assertU(commit());
SolrQuery query = new SolrQuery( fieldName+":aaa" );
query.set( "indent", "true" );
SolrQueryRequest req = new LocalSolrQueryRequest( core, query );
assertQ("Make sure they got in", req
,"//*[@numFound='1']"
,"//result/doc[1]/int[@name='id'][.='10']"
);
// Check to see if our copy field made it out safely
query.setQuery( "dynamic_runtime:aaa" );
assertQ("Make sure they got in", req
,"//*[@numFound='1']"
,"//result/doc[1]/int[@name='id'][.='10']"
);
}
}