package org.apache.solr.rest.schema;
/*
* 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.
*/
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.rest.GETable;
import org.apache.solr.rest.PUTable;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.ManagedIndexSchema;
import org.apache.solr.schema.SchemaField;
import org.noggit.ObjectBuilder;
import org.restlet.data.MediaType;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* This class responds to requests at /solr/(corename)/schema/fields/(fieldname)
* where "fieldname" is the name of a field.
* <p/>
* The GET method returns properties for the given fieldname.
* The "includeDynamic" query parameter, if specified, will cause the
* dynamic field matching the given fieldname to be returned if fieldname
* is not explicitly declared in the schema.
* <p/>
* The PUT method accepts field addition requests in JSON format.
*/
public class FieldResource extends BaseFieldResource implements GETable, PUTable {
private static final Logger log = LoggerFactory.getLogger(FieldResource.class);
private boolean includeDynamic;
private String fieldName;
public FieldResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
if (isExisting()) {
includeDynamic = getSolrRequest().getParams().getBool(INCLUDE_DYNAMIC_PARAM, false);
fieldName = (String) getRequestAttributes().get(IndexSchema.NAME);
try {
fieldName = null == fieldName ? "" : urlDecode(fieldName.trim()).trim();
} catch (UnsupportedEncodingException e) {
throw new ResourceException(e);
}
}
}
@Override
public Representation get() {
try {
if (fieldName.isEmpty()) {
final String message = "Field name is missing";
throw new SolrException(ErrorCode.BAD_REQUEST, message);
} else {
final SchemaField field;
if (includeDynamic) {
field = getSchema().getFieldOrNull(fieldName);
} else {
field = getSchema().getFields().get(fieldName);
}
if (null == field) {
final String message = "Field '" + fieldName + "' not found.";
throw new SolrException(ErrorCode.NOT_FOUND, message);
} else {
getSolrResponse().add(IndexSchema.FIELD, getFieldProperties(field));
}
}
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
/**
* Accepts JSON add field request, to URL
*/
@Override
public Representation put(Representation entity) {
try {
if (!getSchema().isMutable()) {
final String message = "This IndexSchema is not mutable.";
throw new SolrException(ErrorCode.BAD_REQUEST, message);
} else {
if (null == entity.getMediaType()) {
entity.setMediaType(MediaType.APPLICATION_JSON);
}
if (!entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
String message = "Only media type " + MediaType.APPLICATION_JSON.toString() + " is accepted."
+ " Request has media type " + entity.getMediaType().toString() + ".";
log.error(message);
throw new SolrException(ErrorCode.BAD_REQUEST, message);
} else {
Object object = ObjectBuilder.fromJSON(entity.getText());
if (!(object instanceof Map)) {
String message = "Invalid JSON type " + object.getClass().getName() + ", expected Map of the form"
+ " (ignore the backslashes): {\"type\":\"text_general\", ...}, either with or"
+ " without a \"name\" mapping. If the \"name\" is specified, it must match the"
+ " name given in the request URL: /schema/fields/(name)";
log.error(message);
throw new SolrException(ErrorCode.BAD_REQUEST, message);
} else {
Map<String, Object> map = (Map<String, Object>) object;
if (1 == map.size() && map.containsKey(IndexSchema.FIELD)) {
map = (Map<String, Object>) map.get(IndexSchema.FIELD);
}
String bodyFieldName;
if (null != (bodyFieldName = (String) map.remove(IndexSchema.NAME)) && !fieldName.equals(bodyFieldName)) {
String message = "Field name in the request body '" + bodyFieldName
+ "' doesn't match field name in the request URL '" + fieldName + "'";
log.error(message);
throw new SolrException(ErrorCode.BAD_REQUEST, message);
} else {
String fieldType;
if (null == (fieldType = (String) map.remove(IndexSchema.TYPE))) {
String message = "Missing '" + IndexSchema.TYPE + "' mapping.";
log.error(message);
throw new SolrException(ErrorCode.BAD_REQUEST, message);
} else {
ManagedIndexSchema oldSchema = (ManagedIndexSchema) getSchema();
Object copies = map.get(IndexSchema.COPY_FIELDS);
List<String> copyFieldNames = null;
if (copies != null) {
if (copies instanceof List) {
copyFieldNames = (List<String>) copies;
} else if (copies instanceof String) {
copyFieldNames = Collections.singletonList(copies.toString());
} else {
String message = "Invalid '" + IndexSchema.COPY_FIELDS + "' type.";
log.error(message);
throw new SolrException(ErrorCode.BAD_REQUEST, message);
}
}
if (copyFieldNames != null) {
map.remove(IndexSchema.COPY_FIELDS);
}
SchemaField newField = oldSchema.newField(fieldName, fieldType, map);
IndexSchema newSchema = oldSchema.addField(newField, copyFieldNames);
getSolrCore().setLatestSchema(newSchema);
}
}
}
}
}
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}