You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by gs...@apache.org on 2013/07/08 16:05:34 UTC

svn commit: r1500744 - in /lucene/dev/branches/branch_4x: ./ solr/ solr/core/ solr/core/src/java/org/apache/solr/rest/schema/ solr/core/src/java/org/apache/solr/schema/ solr/core/src/test/org/apache/solr/rest/schema/

Author: gsingers
Date: Mon Jul  8 14:05:33 2013
New Revision: 1500744

URL: http://svn.apache.org/r1500744
Log:
SOLR-5010: merge from trunk

Modified:
    lucene/dev/branches/branch_4x/   (props changed)
    lucene/dev/branches/branch_4x/solr/   (props changed)
    lucene/dev/branches/branch_4x/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_4x/solr/core/   (props changed)
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
    lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/rest/schema/TestManagedSchemaFieldResource.java

Modified: lucene/dev/branches/branch_4x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/CHANGES.txt?rev=1500744&r1=1500743&r2=1500744&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_4x/solr/CHANGES.txt Mon Jul  8 14:05:33 2013
@@ -130,6 +130,8 @@ New Features
 * SOLR-5003: CSV Update Handler supports optionally adding the line number/row id to 
   a document (gsingers)
 
+* SOLR-5010: Add support for creating copy fields to the Fields REST API (gsingers)
+
 Bug Fixes
 ----------------------
 

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java?rev=1500744&r1=1500743&r2=1500744&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java Mon Jul  8 14:05:33 2013
@@ -33,6 +33,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedSet;
@@ -126,6 +127,7 @@ public class FieldCollectionResource ext
           throw new SolrException(ErrorCode.BAD_REQUEST, message);
         } else {
           Object object = ObjectBuilder.fromJSON(entity.getText());
+          Map<String, String> copyFields = new HashMap<String, String>();
           if ( ! (object instanceof List)) {
             String message = "Invalid JSON type " + object.getClass().getName() + ", expected List of the form"
                 + " (ignore the backslashes): [{\"name\":\"foo\",\"type\":\"text_general\", ...}, {...}, ...]";
@@ -148,9 +150,24 @@ public class FieldCollectionResource ext
                 log.error(message);
                 throw new SolrException(ErrorCode.BAD_REQUEST, message);
               }
+              // copyFields:"comma separated list of destination fields"
+              String copyTo = (String)map.get(IndexSchema.COPY_FIELDS);
+              if (copyTo != null){
+                map.remove(IndexSchema.COPY_FIELDS);
+                copyFields.put(fieldName, copyTo);
+              }
               newFields.add(oldSchema.newField(fieldName, fieldType, map));
             }
             IndexSchema newSchema = oldSchema.addFields(newFields);
+            for (Map.Entry<String, String> entry : copyFields.entrySet()) {
+              //key is the source, value is a comma separated list of targets
+              String [] splits = entry.getValue().split(",");
+              if (splits != null && splits.length > 0){
+                for (int i = 0; i < splits.length; i++) {
+                  newSchema.registerCopyField(entry.getKey(), splits[i].trim());
+                }
+              }
+            }
             getSolrCore().setLatestSchema(newSchema);
           }
         }

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java?rev=1500744&r1=1500743&r2=1500744&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java Mon Jul  8 14:05:33 2013
@@ -31,6 +31,9 @@ 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.Map;
 
 /**
@@ -44,9 +47,9 @@ import java.util.Map;
  * <p/>
  * The PUT method accepts field addition requests in JSON format.
  */
-public class FieldResource extends BaseFieldResource implements GETable,PUTable {
+public class FieldResource extends BaseFieldResource implements GETable, PUTable {
   private static final Logger log = LoggerFactory.getLogger(FieldResource.class);
-  
+
   private boolean includeDynamic;
   private String fieldName;
 
@@ -59,7 +62,7 @@ public class FieldResource extends BaseF
     super.doInit();
     if (isExisting()) {
       includeDynamic = getSolrRequest().getParams().getBool(INCLUDE_DYNAMIC_PARAM, false);
-      fieldName = (String)getRequestAttributes().get(IndexSchema.NAME);
+      fieldName = (String) getRequestAttributes().get(IndexSchema.NAME);
       try {
         fieldName = null == fieldName ? "" : urlDecode(fieldName.trim()).trim();
       } catch (UnsupportedEncodingException e) {
@@ -97,53 +100,61 @@ public class FieldResource extends BaseF
   }
 
   /**
-   * Accepts JSON add field request, to URL  
+   * Accepts JSON add field request, to URL
    */
   @Override
   public Representation put(Representation entity) {
     try {
-      if ( ! getSchema().isMutable()) {
+      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)) {
+        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() + ".";
+              + "  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)) {
+          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)";
+                + " (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;
+            Map<String, Object> map = (Map<String, Object>) object;
             if (1 == map.size() && map.containsKey(IndexSchema.FIELD)) {
-              map = (Map<String,Object>)map.get(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 + "'";
+            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))) {
+              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();
+                ManagedIndexSchema oldSchema = (ManagedIndexSchema) getSchema();
+                String copyTo = (String) map.get(IndexSchema.COPY_FIELDS);
+                Collection<String> copyFieldNames = Collections.emptySet();
+                if (copyTo != null) {
+                  map.remove(IndexSchema.COPY_FIELDS);
+                  String [] tmp = copyTo.split(",");
+                  copyFieldNames = new HashSet<String>(tmp.length);
+                  Collections.addAll(copyFieldNames, tmp);
+                }
                 SchemaField newField = oldSchema.newField(fieldName, fieldType, map);
-                ManagedIndexSchema newSchema = oldSchema.addField(newField);
+                IndexSchema newSchema = oldSchema.addField(newField, copyFieldNames);
                 getSolrCore().setLatestSchema(newSchema);
               }
             }

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java?rev=1500744&r1=1500743&r2=1500744&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java Mon Jul  8 14:05:33 2013
@@ -1435,9 +1435,23 @@ public class IndexSchema {
   }
 
   /**
+   * Copies this schema, adds the given field to the copy, then persists the new schema.
+   *
+   * @param newField the SchemaField to add
+   * @param copyFieldNames 0 or more names of targets to copy this field to
+   * @return a new IndexSchema based on this schema with newField added
+   * @see #newField(String, String, Map)
+   */
+  public IndexSchema addField(SchemaField newField, Collection<String> copyFieldNames) {
+    String msg = "This IndexSchema is not mutable.";
+    log.error(msg);
+    throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+  }
+
+  /**
    * Copies this schema, adds the given fields to the copy, then persists the new schema.
    *
-   * @param newFields the SchemaFields to add 
+   * @param newFields the SchemaFields to add
    * @return a new IndexSchema based on this schema with newFields added
    * @see #newField(String, String, Map)
    */
@@ -1448,6 +1462,20 @@ public class IndexSchema {
   }
 
   /**
+   * Copies this schema, adds the given fields to the copy, then persists the new schema.
+   *
+   * @param newFields the SchemaFields to add
+   * @param copyFieldNames 0 or more names of targets to copy this field to
+   * @return a new IndexSchema based on this schema with newFields added
+   * @see #newField(String, String, Map)
+   */
+  public IndexSchema addFields(Collection<SchemaField> newFields, Map<String, Collection<String>> copyFieldNames) {
+    String msg = "This IndexSchema is not mutable.";
+    log.error(msg);
+    throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+  }
+
+  /**
    * Returns a SchemaField if the given fieldName does not already 
    * exist in this schema, and does not match any dynamic fields 
    * in this schema.  The resulting SchemaField can be used in a call

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java?rev=1500744&r1=1500743&r2=1500744&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java Mon Jul  8 14:05:33 2013
@@ -40,6 +40,7 @@ import java.io.OutputStreamWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 
 /** Solr-managed schema - non-user-editable, but can be mutable via internal and external REST API requests. */
@@ -165,27 +166,40 @@ public final class ManagedIndexSchema ex
   }
 
   @Override
-  public ManagedIndexSchema addField(SchemaField newField) {
+  public IndexSchema addField(SchemaField newField) {
     return addFields(Arrays.asList(newField));
   }
 
+  @Override
+  public IndexSchema addField(SchemaField newField, Collection<String> copyFieldNames) {
+    return addFields(Arrays.asList(newField), Collections.singletonMap(newField.getName(), copyFieldNames));
+  }
+
   public class FieldExistsException extends SolrException {
     public FieldExistsException(ErrorCode code, String msg) {
       super(code, msg);
     }
   }
-  
+
   @Override
-  public ManagedIndexSchema addFields(Collection<SchemaField> newFields) {
+  public IndexSchema addFields(Collection<SchemaField> newFields) {
+    return addFields(newFields, Collections.<String, Collection<String>>emptyMap());
+  }
+
+  @Override
+  public IndexSchema addFields(Collection<SchemaField> newFields, Map<String, Collection<String>> copyFieldNames) {
     ManagedIndexSchema newSchema = null;
     if (isMutable) {
       boolean success = false;
+      if (copyFieldNames == null){
+        copyFieldNames = Collections.emptyMap();
+      }
       while ( ! success) { // optimistic concurrency
         // even though fields is volatile, we need to synchronize to avoid two addFields
         // happening concurrently (and ending up missing one of them)
         synchronized (getSchemaUpdateLock()) {
           newSchema = shallowCopy(true);
-          
+
           for (SchemaField newField : newFields) {
             if (null != newSchema.getFieldOrNull(newField.getName())) {
               String msg = "Field '" + newField.getName() + "' already exists.";
@@ -201,6 +215,12 @@ public final class ManagedIndexSchema ex
               log.debug("{} is required in this schema", newField.getName());
               newSchema.requiredFields.add(newField);
             }
+            Collection<String> copyFields = copyFieldNames.get(newField.getName());
+            if (copyFields != null) {
+              for (String copyField : copyFields) {
+                newSchema.registerCopyField(newField.getName(), copyField);
+              }
+            }
           }
           // Run the callbacks on SchemaAware now that everything else is done
           for (SchemaAware aware : newSchema.schemaAware) {

Modified: lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/rest/schema/TestManagedSchemaFieldResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/rest/schema/TestManagedSchemaFieldResource.java?rev=1500744&r1=1500743&r2=1500744&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/rest/schema/TestManagedSchemaFieldResource.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/rest/schema/TestManagedSchemaFieldResource.java Mon Jul  8 14:05:33 2013
@@ -103,6 +103,29 @@ public class TestManagedSchemaFieldResou
   }
 
   @Test
+  public void testAddCopyField() throws Exception {
+    assertQ("/schema/fields/newfield2?indent=on&wt=xml",
+            "count(/response/lst[@name='field']) = 0",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '404'",
+            "/response/lst[@name='error']/int[@name='code'] = '404'");
+
+    assertJPut("/schema/fields/fieldA",
+        "{\"type\":\"text\",\"stored\":\"false\"}",
+        "/responseHeader/status==0");
+    assertJPut("/schema/fields/fieldB",
+        "{\"type\":\"text\",\"stored\":\"false\", \"copyFields\":\"fieldA\"}",
+        "/responseHeader/status==0");
+
+    assertQ("/schema/fields/fieldB?indent=on&wt=xml",
+            "count(/response/lst[@name='field']) = 1",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '0'");
+    assertQ("/schema/copyfields/?indent=on&wt=xml&source.fl=fieldB",
+        "count(/response/arr[@name='copyFields']/lst) = 1"
+    );
+
+  }
+
+  @Test
   public void testPostMultipleFields() throws Exception {
     assertQ("/schema/fields/newfield1?indent=on&wt=xml",
             "count(/response/lst[@name='field']) = 0",
@@ -142,5 +165,35 @@ public class TestManagedSchemaFieldResou
         "count(/response/result[@name='response']/doc/*) = 1",
         "/response/result[@name='response']/doc/str[@name='id'][.='456']");
   }
+
+  @Test
+  public void testPostCopy() throws Exception {
+    assertJPost("/schema/fields",
+              "[{\"name\":\"fieldA\",\"type\":\"text\",\"stored\":\"false\"},"
+               + "{\"name\":\"fieldB\",\"type\":\"text\",\"stored\":\"false\"},"
+               + " {\"name\":\"fieldC\",\"type\":\"text\",\"stored\":\"false\", \"copyFields\":\"fieldB\"}]",
+                "/responseHeader/status==0");
+    assertQ("/schema/copyfields/?indent=on&wt=xml&source.fl=fieldC",
+        "count(/response/arr[@name='copyFields']/lst) = 1"
+    );
+    assertJPost("/schema/fields",
+              "[{\"name\":\"fieldD\",\"type\":\"text\",\"stored\":\"false\"},"
+               + "{\"name\":\"fieldE\",\"type\":\"text\",\"stored\":\"false\"},"
+               + " {\"name\":\"fieldF\",\"type\":\"text\",\"stored\":\"false\", \"copyFields\":\"fieldD,fieldE\"}]",
+                "/responseHeader/status==0");
+    assertQ("/schema/copyfields/?indent=on&wt=xml&source.fl=fieldF",
+        "count(/response/arr[@name='copyFields']/lst) = 2"
+    );
+    assertJPost("/schema/fields",
+              "[{\"name\":\"fieldG\",\"type\":\"text\",\"stored\":\"false\"},"
+               + "{\"name\":\"fieldH\",\"type\":\"text\",\"stored\":\"false\"},"
+               + " {\"name\":\"fieldI\",\"type\":\"text\",\"stored\":\"false\", \"copyFields\":\"fieldG,   fieldH   \"}]",
+                "/responseHeader/status==0");
+    assertQ("/schema/copyfields/?indent=on&wt=xml&source.fl=fieldF",
+        "count(/response/arr[@name='copyFields']/lst) = 2"
+    );
+
+  }
+
 }