log.trace("reading field def "+name);
String type = DOMUtil.getAttr(attrs,"type","field " + name);
FieldType ft = fieldTypes.get(type);
if (ft==null) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Unknown fieldtype '" + type + "' specified on field " + name,false);
}
Map<String,String> args = DOMUtil.toMapExcept(attrs, "name", "type");
if( args.get( "required" ) != null ) {
explicitRequiredProp.put( name, Boolean.valueOf( args.get( "required" ) ) );
}
SchemaField f = SchemaField.create(name,ft,args);
if (node.getNodeName().equals("field")) {
SchemaField old = fields.put(f.getName(),f);
if( old != null ) {
String msg = "[schema.xml] Duplicate field definition for '"
+ f.getName() + "' ignoring: "+old.toString();
Throwable t = new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
SolrException.logOnce(log,null,t);
SolrConfig.severeErrors.add( t );
}
log.debug("field defined: " + f);
if( f.getDefaultValue() != null ) {
log.debug(name+" contains default value: " + f.getDefaultValue());
fieldsWithDefaultValue.add( f );
}
if (f.isRequired()) {
log.debug(name+" is required in this schema");
requiredFields.add(f);
}
} else if (node.getNodeName().equals("dynamicField")) {
// make sure nothing else has the same path
addDynamicField(dFields, f);
} else {
// we should never get here
throw new RuntimeException("Unknown field type");
}
}
//fields with default values are by definition required
//add them to required fields, and we only have to loop once
// in DocumentBuilder.getDoc()
requiredFields.addAll(getFieldsWithDefaultValue());
// OK, now sort the dynamic fields largest to smallest size so we don't get
// any false matches. We want to act like a compiler tool and try and match
// the largest string possible.
Collections.sort(dFields);
log.trace("Dynamic Field Ordering:" + dFields);
// stuff it in a normal array for faster access
dynamicFields = dFields.toArray(new DynamicField[dFields.size()]);
Node node = (Node) xpath.evaluate("/schema/similarity", document, XPathConstants.NODE);
if (node==null) {
similarityFactory = new SimilarityFactory() {
@Override
public Similarity getSimilarity() {
return Similarity.getDefault();
}
};
log.debug("using default similarity");
} else {
final Object obj = loader.newInstance(((Element) node).getAttribute("class"));
if (obj instanceof SimilarityFactory) {
// configure a factory, get a similarity back
SolrParams params = SolrParams.toSolrParams(DOMUtil.childNodesToNamedList(node));
similarityFactory = (SimilarityFactory)obj;
similarityFactory.init(params);
} else {
// just like always, assume it's a Similarlity and get a ClassCastException - reasonable error handling
similarityFactory = new SimilarityFactory() {
@Override
public Similarity getSimilarity() {
return (Similarity) obj;
}
};
}
if (similarityFactory instanceof SchemaAware){
schemaAware.add((SchemaAware) similarityFactory);
}
log.debug("using similarity factory" + similarityFactory.getClass().getName());
}
node = (Node) xpath.evaluate("/schema/defaultSearchField/text()", document, XPathConstants.NODE);
if (node==null) {
log.warn("no default search field specified in schema.");
} else {
defaultSearchFieldName=node.getNodeValue().trim();
// throw exception if specified, but not found or not indexed
if (defaultSearchFieldName!=null) {
SchemaField defaultSearchField = getFields().get(defaultSearchFieldName);
if ((defaultSearchField == null) || !defaultSearchField.indexed()) {
String msg = "default search field '" + defaultSearchFieldName + "' not defined or not indexed" ;
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
}
}
log.info("default search field is "+defaultSearchFieldName);
}
node = (Node) xpath.evaluate("/schema/solrQueryParser/@defaultOperator", document, XPathConstants.NODE);
if (node==null) {
log.debug("using default query parser operator (OR)");
} else {
queryParserDefaultOperator=node.getNodeValue().trim();
log.info("query parser default operator is "+queryParserDefaultOperator);
}
node = (Node) xpath.evaluate("/schema/uniqueKey/text()", document, XPathConstants.NODE);
if (node==null) {
log.warn("no uniqueKey specified in schema.");
} else {
uniqueKeyField=getIndexedField(node.getNodeValue().trim());
if (!uniqueKeyField.stored()) {
log.error("uniqueKey is not stored - distributed search will not work");
}
if (uniqueKeyField.multiValued()) {
log.error("uniqueKey should not be multivalued");
}
uniqueKeyFieldName=uniqueKeyField.getName();
uniqueKeyFieldType=uniqueKeyField.getType();
log.info("unique key field: "+uniqueKeyFieldName);
// Unless the uniqueKeyField is marked 'required=false' then make sure it exists
if( Boolean.FALSE != explicitRequiredProp.get( uniqueKeyFieldName ) ) {
uniqueKeyField.required = true;
requiredFields.add(uniqueKeyField);
}
}
/////////////// parse out copyField commands ///////////////
// Map<String,ArrayList<SchemaField>> cfields = new HashMap<String,ArrayList<SchemaField>>();
// expression = "/schema/copyField";
dynamicCopyFields = new DynamicCopy[] {};
expression = "//copyField";
nodes = (NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
for (int i=0; i<nodes.getLength(); i++) {
node = nodes.item(i);
NamedNodeMap attrs = node.getAttributes();
String source = DOMUtil.getAttr(attrs,"source","copyField definition");
String dest = DOMUtil.getAttr(attrs,"dest", "copyField definition");
String maxChars = DOMUtil.getAttr(attrs, "maxChars");
int maxCharsInt = CopyField.UNLIMITED;
if (maxChars != null) {
try {
maxCharsInt = Integer.parseInt(maxChars);
} catch (NumberFormatException e) {
log.warn("Couldn't parse maxChars attribute for copyField from "
+ source + " to " + dest + " as integer. The whole field will be copied.");
}
}
registerCopyField(source, dest, maxCharsInt);
}
for (Map.Entry<SchemaField, Integer> entry : copyFieldTargetCounts.entrySet()) {
if (entry.getValue() > 1 && !entry.getKey().multiValued()) {
log.warn("Field " + entry.getKey().name + " is not multivalued "+
"and destination for multiple copyFields ("+
entry.getValue()+")");
}
}
//Run the callbacks on SchemaAware now that everything else is done
for (SchemaAware aware : schemaAware) {
aware.inform(this);
}
} catch (SolrException e) {
SolrConfig.severeErrors.add( e );
throw e;
} catch(Exception e) {
// unexpected exception...
SolrConfig.severeErrors.add( e );
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Schema Parsing Failed: " + e.getMessage(), e,false);
}
// create the field analyzers
refreshAnalyzers();