You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2016/02/23 13:11:03 UTC

lucene-solr git commit: SOLR-8698: params.json can now have appends and invariants as well. 'useParams' specified in the requestHandler is always applied

Repository: lucene-solr
Updated Branches:
  refs/heads/master a33236aa0 -> 9ce2d0d25


SOLR-8698: params.json can now have appends and invariants as well. 'useParams' specified in the requestHandler is always applied


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9ce2d0d2
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9ce2d0d2
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9ce2d0d2

Branch: refs/heads/master
Commit: 9ce2d0d25df7d619ef9ea16a8b5e7f5341d3fc71
Parents: a33236a
Author: Noble Paul <no...@apache.org>
Authored: Tue Feb 23 17:40:49 2016 +0530
Committer: Noble Paul <no...@apache.org>
Committed: Tue Feb 23 17:40:49 2016 +0530

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   5 +
 .../org/apache/solr/core/RequestParams.java     | 126 +++++++++++++------
 .../apache/solr/handler/SolrConfigHandler.java  |  24 ++--
 .../org/apache/solr/util/SolrPluginUtils.java   |  74 +++++++----
 .../solr/core/BlobStoreTestRequestHandler.java  |  16 +--
 .../apache/solr/core/TestSolrConfigHandler.java |   2 +-
 .../apache/solr/handler/TestReqParamsAPI.java   |  36 +++++-
 .../handler/TestSolrConfigHandlerCloud.java     |   7 +-
 8 files changed, 204 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 525e8d5..65820f1 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -78,6 +78,8 @@ Upgrading from Solr 5.x
 * SolrIndexSearcher.QueryCommand and QueryResult were moved to their own classes. If you reference them
   in your code, you should import them under o.a.s.search (or use your IDE's "Organize Imports").
 
+* SOLR-8698: 'useParams' attribute specified in request handler cannot be overridden from request params
+
 Detailed Change List
 ----------------------
 
@@ -155,6 +157,9 @@ New Features
 
 * SOLR-8522: Make it possible to use ip fragments in replica placement rules , such as ip_1, ip_2 etc (Arcadius Ahouansou, noble)
 
+* SOLR-8698: params.json can now specify 'appends' and 'invariants' (noble)
+
+
 Bug Fixes
 ----------------------
 * SOLR-8386: Add field option in the new admin UI schema page loads up even when no schemaFactory has been

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/java/org/apache/solr/core/RequestParams.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/RequestParams.java b/solr/core/src/java/org/apache/solr/core/RequestParams.java
index d27a1c6..6712106 100644
--- a/solr/core/src/java/org/apache/solr/core/RequestParams.java
+++ b/solr/core/src/java/org/apache/solr/core/RequestParams.java
@@ -26,6 +26,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.google.common.collect.ImmutableMap;
 import org.apache.solr.cloud.ZkSolrResourceLoader;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.MapSolrParams;
@@ -37,6 +38,9 @@ import org.noggit.ObjectBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.common.util.Utils.getDeepCopy;
+
 /**
  * The class encapsulates the request time parameters . This is immutable and any changes performed
  * returns a copy of the Object with the changed values
@@ -45,7 +49,7 @@ public class RequestParams implements MapSerializable {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private final Map data;
-  private final Map<String, VersionedParams> paramsets = new LinkedHashMap<>();
+  private final Map<String, ParamSet> paramsets = new LinkedHashMap<>();
   private final int znodeVersion;
 
   public RequestParams(Map data, int znodeVersion) {
@@ -57,15 +61,28 @@ public class RequestParams implements MapSerializable {
         Map.Entry e = (Map.Entry) o;
         if (e.getValue() instanceof Map) {
           Map value = (Map) e.getValue();
-          Map copy = getMapCopy(value);
-          Map meta = (Map) copy.remove("");
-          this.paramsets.put((String) e.getKey(), new VersionedParams(Collections.unmodifiableMap(copy), meta));
+          this.paramsets.put((String) e.getKey(), createParamSet(value, 0l));
         }
       }
     }
     this.znodeVersion = znodeVersion;
   }
 
+  public static ParamSet createParamSet(Map map, Long version) {
+    Map copy = getDeepCopy(map, 3);
+    Map meta = (Map) copy.remove("");
+    if (meta == null && version != null) {
+      meta = Collections.singletonMap("v", version);
+    }
+    Map invariants = (Map) copy.remove(INVARIANTS);
+    Map appends = (Map) copy.remove(APPENDS);
+    return new ParamSet(copy, invariants, appends, meta);
+  }
+
+  /**
+   * This converts Lists to arrays of strings. Because Solr expects
+   * params to be String[]
+   */
   private static Map getMapCopy(Map value) {
     Map copy = new LinkedHashMap<>();
     for (Object o1 : value.entrySet()) {
@@ -92,10 +109,15 @@ public class RequestParams implements MapSerializable {
     return copy;
   }
 
-  public VersionedParams getParams(String name) {
+  public ParamSet getParams(String name) {
     return paramsets.get(name);
   }
 
+  public VersionedParams getParams(String name, String type) {
+    ParamSet paramSet = paramsets.get(name);
+    return paramSet == null ? null : paramSet.getParams(type);
+  }
+
   public int getZnodeVersion() {
     return znodeVersion;
   }
@@ -112,32 +134,12 @@ public class RequestParams implements MapSerializable {
     return result;
   }
 
-  public RequestParams setParams(String name, Map values) {
-    Map deepCopy = Utils.getDeepCopy(data, 3);
+  public RequestParams setParams(String name, ParamSet paramSet) {
+    Map deepCopy = getDeepCopy(data, 3);
     Map p = (Map) deepCopy.get(NAME);
     if (p == null) deepCopy.put(NAME, p = new LinkedHashMap());
-    if (values == null) {
-      p.remove(name);
-    } else {
-      Map old = (Map) p.get(name);
-      long version = 0;
-      Map meta = null;
-      if (old != null) {
-        meta = (Map) old.get("");
-        if (meta != null) {
-          Long oldVersion = (Long) old.get("v");
-          if (oldVersion != null) version = oldVersion.longValue() + 1;
-        }
-        meta = new LinkedHashMap<>(meta);
-      } else {
-        meta = new LinkedHashMap<>();
-      }
-
-      meta.put("v", version);
-      values = new LinkedHashMap<>(values);
-      values.put("", meta);
-      p.put(name, values);
-    }
+    if (paramSet == null) p.remove(name);
+    else p.put(name, paramSet.toMap());
     return new RequestParams(deepCopy, znodeVersion);
   }
 
@@ -146,7 +148,7 @@ public class RequestParams implements MapSerializable {
       ZkSolrResourceLoader resourceLoader = (ZkSolrResourceLoader) loader;
       try {
         Stat stat = resourceLoader.getZkController().getZkClient().exists(resourceLoader.getConfigSetZkPath() + "/" + RequestParams.RESOURCE, null, true);
-        log.debug("latest version of {} in ZK  is : {}", resourceLoader.getConfigSetZkPath() + "/" + RequestParams.RESOURCE, stat == null ? "": stat.getVersion());
+        log.debug("latest version of {} in ZK  is : {}", resourceLoader.getConfigSetZkPath() + "/" + RequestParams.RESOURCE, stat == null ? "" : stat.getVersion());
         if (stat == null) {
           requestParams = new RequestParams(Collections.EMPTY_MAP, -1);
         } else if (requestParams == null || stat.getVersion() > requestParams.getZnodeVersion()) {
@@ -199,22 +201,70 @@ public class RequestParams implements MapSerializable {
   public static final String USEPARAM = "useParams";
   public static final String NAME = "params";
   public static final String RESOURCE = "params.json";
+  public static final String APPENDS = "_appends_";
+  public static final String INVARIANTS = "_invariants_";
 
-  public static class VersionedParams extends MapSolrParams {
-    Map meta;
+  public static class ParamSet implements MapSerializable {
+    private final Map defaults, appends, invariants;
+    Map<String, VersionedParams> paramsMap;
+    public final Map meta;
 
-    public VersionedParams(Map<String, String> map, Map meta) {
-      super(map);
+    ParamSet(Map defaults, Map invariants, Map appends, Map meta) {
+      this.defaults = defaults;
+      this.invariants = invariants;
+      this.appends = appends;
+      ImmutableMap.Builder<String, VersionedParams> builder = ImmutableMap.<String, VersionedParams>builder().put(PluginInfo.DEFAULTS,
+          new VersionedParams(defaults, this));
+      if (appends != null) builder.put(PluginInfo.APPENDS, new VersionedParams(appends, this));
+      if (invariants != null) builder.put(PluginInfo.INVARIANTS, new VersionedParams(invariants, this));
+      paramsMap = builder.build();
       this.meta = meta;
     }
 
-    public Map getRawMap() {
-      return meta;
+    public Long getVersion() {
+      return meta == null ? 0l : (Long) meta.get("v");
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+      LinkedHashMap result = new LinkedHashMap();
+      result.putAll(defaults);
+      if (appends != null) result.put(APPENDS, appends);
+      if (invariants != null) result.put(INVARIANTS, invariants);
+      if(meta != null) result.put("", meta);
+      return result;
     }
 
 
-    public Long getVersion() {
-      return meta == null ? 0l : (Long) meta.get("v");
+    public ParamSet update(Map map) {
+      ParamSet p = createParamSet(map, null);
+      return new ParamSet(
+          mergeMaps(getDeepCopy(defaults, 2), p.defaults),
+          mergeMaps(getDeepCopy(invariants, 2), p.invariants),
+          mergeMaps(getDeepCopy(appends, 2), p.appends),
+          mergeMaps(getDeepCopy(meta, 2), singletonMap("v", getVersion() + 1))
+      );
+    }
+
+    private static Map mergeMaps(Map m1, Map m2) {
+      if (m1 == null && m2 == null) return null;
+      if (m1 == null) return m2;
+      if (m2 == null) return m1;
+      m1.putAll(m2);
+      return m1;
+    }
+
+    public VersionedParams getParams(String type) {
+      return paramsMap.get(type);
+    }
+  }
+
+  public static class VersionedParams extends MapSolrParams {
+    final ParamSet paramSet;
+
+    public VersionedParams(Map map, ParamSet paramSet) {
+      super(getMapCopy(map));
+      this.paramSet = paramSet;
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index a566c0e..4a6e553 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
@@ -74,6 +75,7 @@ import org.apache.solr.util.plugin.SolrCoreAware;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static java.util.Collections.singletonList;
 import static org.apache.solr.common.util.Utils.makeMap;
 import static org.apache.solr.common.params.CoreAdminParams.NAME;
@@ -169,11 +171,11 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
         } else if (RequestParams.NAME.equals(parts.get(1))) {
           if (parts.size() == 3) {
             RequestParams params = req.getCore().getSolrConfig().getRequestParams();
-            MapSolrParams p = params.getParams(parts.get(2));
+            RequestParams.ParamSet p = params.getParams(parts.get(2));
             Map m = new LinkedHashMap<>();
             m.put(ZNODEVER, params.getZnodeVersion());
             if (p != null) {
-              m.put(RequestParams.NAME, makeMap(parts.get(2), p.getMap()));
+              m.put(RequestParams.NAME, makeMap(parts.get(2), p.toMap()));
             }
             resp.add(SolrQueryResponse.NAME, m);
           } else {
@@ -289,7 +291,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
 
               Map val = null;
               String key = entry.getKey();
-              if (key == null || key.trim().isEmpty()) {
+              if (isNullOrEmpty(key)) {
                 op.addError("null key ");
                 continue;
               }
@@ -312,13 +314,17 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
                 continue;
               }
 
-              MapSolrParams old = params.getParams(key);
+              RequestParams.ParamSet old = params.getParams(key);
               if (op.name.equals(UPDATE)) {
-                LinkedHashMap m = new LinkedHashMap(old.getMap());
-                m.putAll(val);
-                val = m;
+                if (old == null) {
+                  op.addError(formatString("unknown paramset {} cannot update ", key));
+                  continue;
+                }
+                params = params.setParams(key, old.update(val));
+              } else {
+                Long version = old == null ? 0 : old.getVersion() + 1;
+                params = params.setParams(key, RequestParams.createParamSet(val, version));
               }
-              params = params.setParams(key, val);
 
             }
             break;
@@ -350,7 +356,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
         if (ops.isEmpty()) {
           ZkController.touchConfDir(zkLoader);
         } else {
-          log.info("persisting params version : {}", params.toMap());
+          log.debug("persisting params version : {}", Utils.toJSONString(params.toMap()));
           int latestVersion = ZkController.persistConfigResourceToZooKeeper(zkLoader,
               params.getZnodeVersion(),
               RequestParams.RESOURCE,

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java b/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java
index debec54..b30cc06 100644
--- a/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java
+++ b/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java
@@ -49,6 +49,7 @@ import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.core.PluginInfo;
 import org.apache.solr.core.RequestParams;
 import org.apache.solr.handler.component.HighlightComponent;
 import org.apache.solr.handler.component.ResponseBuilder;
@@ -80,6 +81,11 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableMap;
 
+import static org.apache.solr.core.PluginInfo.APPENDS;
+import static org.apache.solr.core.PluginInfo.DEFAULTS;
+import static org.apache.solr.core.PluginInfo.INVARIANTS;
+import static org.apache.solr.core.RequestParams.USEPARAM;
+
 /**
  * <p>Utilities that may be of use to RequestHandlers.</p>
  *
@@ -125,7 +131,7 @@ public class SolrPluginUtils {
   }
 
   private static final MapSolrParams maskUseParams = new MapSolrParams(ImmutableMap.<String, String>builder()
-      .put(RequestParams.USEPARAM, "")
+      .put(USEPARAM, "")
       .build());
 
   /**
@@ -145,10 +151,16 @@ public class SolrPluginUtils {
 
   public static void setDefaults(SolrRequestHandler handler, SolrQueryRequest req, SolrParams defaults,
                                  SolrParams appends, SolrParams invariants) {
-
-    List<String> paramNames = null;
-    String useParams = req.getParams().get(RequestParams.USEPARAM);
+    String useParams = (String) req.getContext().get(USEPARAM);
+    if(useParams != null) {
+      RequestParams rp = req.getCore().getSolrConfig().getRequestParams();
+      defaults = applyParamSet(rp, defaults, useParams, DEFAULTS);
+      appends = applyParamSet(rp, appends, useParams, APPENDS);
+      invariants = applyParamSet(rp, invariants, useParams, INVARIANTS);
+    }
+    useParams = req.getParams().get(USEPARAM);
     if (useParams != null && !useParams.isEmpty()) {
+      RequestParams rp = req.getCore().getSolrConfig().getRequestParams();
       // now that we have expanded the request macro useParams with the actual values
       // it makes no sense to keep it visible now on.
       // distrib request sends all params to the nodes down the line and
@@ -157,19 +169,27 @@ public class SolrPluginUtils {
       // value as an empty string to other nodes we get the desired benefit of
       // overriding the useParams specified in the requestHandler directly
       req.setParams(SolrParams.wrapDefaults(maskUseParams, req.getParams()));
+      defaults = applyParamSet(rp, defaults, useParams, DEFAULTS);
+      appends = applyParamSet(rp, appends, useParams, APPENDS);
+      invariants = applyParamSet(rp, invariants, useParams, INVARIANTS);
     }
-    if (useParams == null) useParams = (String) req.getContext().get(RequestParams.USEPARAM);
-    if (useParams != null && !useParams.isEmpty()) paramNames = StrUtils.splitSmart(useParams, ',');
-    if (paramNames != null) {
-      for (String name : paramNames) {
-        SolrParams requestParams = req.getCore().getSolrConfig().getRequestParams().getParams(name);
-        if (requestParams != null) {
-          defaults = SolrParams.wrapDefaults(requestParams, defaults);
-        }
+    RequestUtil.processParams(handler, req, defaults, appends, invariants);
+  }
+
+  private static SolrParams applyParamSet(RequestParams requestParams,
+                                          SolrParams defaults, String paramSets, String type) {
+    if (paramSets == null) return defaults;
+    for (String name : StrUtils.splitSmart(paramSets, ',')) {
+      RequestParams.VersionedParams params = requestParams.getParams(name, type);
+      if (type.equals(DEFAULTS)) {
+        defaults = SolrParams.wrapDefaults(params, defaults);
+      } else if (type.equals(INVARIANTS)) {
+        defaults = SolrParams.wrapAppended(params, defaults);
+      } else {
+        defaults = SolrParams.wrapAppended(params, defaults);
       }
     }
-
-    RequestUtil.processParams(handler, req, defaults, appends, invariants);
+    return defaults;
   }
 
 
@@ -323,14 +343,14 @@ public class SolrPluginUtils {
           DocList results,
           boolean dbgQuery,
           boolean dbgResults)
-          throws IOException 
+          throws IOException
   {
     NamedList dbg = new SimpleOrderedMap();
     doStandardQueryDebug(req, userQuery, query, dbgQuery, dbg);
     doStandardResultsDebug(req, query, results, dbgResults, dbg);
     return dbg;
   }
-  
+
 
   public static void doStandardQueryDebug(
           SolrQueryRequest req,
@@ -352,7 +372,7 @@ public class SolrPluginUtils {
       dbg.add("parsedquery_toString", query.toString());
     }
   }
-  
+
   public static void doStandardResultsDebug(
           SolrQueryRequest req,
           Query query,
@@ -520,7 +540,7 @@ public class SolrPluginUtils {
       if(in.length()==0) {
         continue;
       }
-      
+
       String[] bb = whitespacePattern.split(in);
       for (String s : bb) {
         String[] bbb = caratPattern.split(s);
@@ -530,7 +550,7 @@ public class SolrPluginUtils {
     return out;
   }
   /**
-  
+
   /**
    * Like {@link #parseFieldBoosts}, but allows for an optional slop value prefixed by "~".
    *
@@ -788,10 +808,10 @@ public class SolrPluginUtils {
     }
     return s.toString().replace("\"","");
   }
-  
+
   /**
    * Adds to {@code dest} all the not-null elements of {@code entries} that have non-null names
-   * 
+   *
    * @param entries The array of entries to be added to the {@link NamedList} {@code dest}
    * @param dest The {@link NamedList} instance where the not-null elements of entries are added
    * @return Returns The {@code dest} input object
@@ -883,7 +903,7 @@ public class SolrPluginUtils {
      */
     @Override
     protected Query getFieldQuery(String field, String queryText, boolean quoted)
-      throws SyntaxError {
+        throws SyntaxError {
 
       if (aliases.containsKey(field)) {
 
@@ -1018,7 +1038,7 @@ public class SolrPluginUtils {
 
       Document luceneDoc = searcher.doc(docid, fields);
       SolrDocument doc = new SolrDocument();
-      
+
       for( IndexableField field : luceneDoc) {
         if (null == fields || fields.contains(field.name())) {
           SchemaField sf = schema.getField( field.name() );
@@ -1065,9 +1085,9 @@ public class SolrPluginUtils {
   }
 
    /**
-   * Given the integer purpose of a request generates a readable value corresponding 
-   * the request purposes (there can be more than one on a single request). If 
-   * there is a purpose parameter present that's not known this method will 
+   * Given the integer purpose of a request generates a readable value corresponding
+   * the request purposes (there can be more than one on a single request). If
+   * there is a purpose parameter present that's not known this method will
    * return {@value #UNKNOWN_VALUE}
    * @param reqPurpose Numeric request purpose
    * @return a comma separated list of purposes or {@value #UNKNOWN_VALUE}
@@ -1088,7 +1108,7 @@ public class SolrPluginUtils {
       }
       return UNKNOWN_VALUE;
   }
-  
+
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/test/org/apache/solr/core/BlobStoreTestRequestHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/core/BlobStoreTestRequestHandler.java b/solr/core/src/test/org/apache/solr/core/BlobStoreTestRequestHandler.java
index 893533e..ff5e1cb 100644
--- a/solr/core/src/test/org/apache/solr/core/BlobStoreTestRequestHandler.java
+++ b/solr/core/src/test/org/apache/solr/core/BlobStoreTestRequestHandler.java
@@ -23,31 +23,31 @@ import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.util.plugin.SolrCoreAware;
 
-public class BlobStoreTestRequestHandler extends DumpRequestHandler implements Runnable, SolrCoreAware{
+public class BlobStoreTestRequestHandler extends DumpRequestHandler implements Runnable, SolrCoreAware {
 
   private SolrCore core;
 
-  private long version = 1;
+  private long version = 0;
   private String watchedVal = null;
 
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
     super.handleRequestBody(req, rsp);
     rsp.add("class", this.getClass().getName());
-    rsp.add("x",watchedVal);
+    rsp.add("x", watchedVal);
   }
 
   @Override
   public void run() {
     RequestParams p = core.getSolrConfig().getRequestParams();
-    RequestParams.VersionedParams v = p.getParams("watched");
-    if(v== null){
+    RequestParams.ParamSet v = p.getParams("watched");
+    if (v == null) {
       watchedVal = null;
-      version=-1;
+      version = -1;
       return;
     }
-    if(v.getVersion() != version){
-      watchedVal =  v.getMap().get("x");
+    if (v.getVersion() != version) {
+      watchedVal = v.getParams(PluginInfo.DEFAULTS).get("x");
     }
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
index 6aa54da..9f80cbe 100644
--- a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
+++ b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
@@ -585,7 +585,7 @@ public class TestSolrConfigHandler extends RestTestBase {
         "/dump1?wt=json&useParams=y",
         null,
         Arrays.asList("params", "a"),
-        null,
+        "A val",
         5);
 
     TestSolrConfigHandler.testForResponseElement(

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java b/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
index d38122d..a4bc0ea 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
@@ -20,8 +20,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Predicate;
 
-import org.apache.lucene.util.LuceneTestCase;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
@@ -171,6 +171,7 @@ public class TestReqParamsAPI extends AbstractFullDistribZkTestBase {
         "CY val",
         10);
     compareValues(result, 20l, asList("response", "params", "y", "i"));
+    compareValues(result, null, asList("response", "params", "y", "a"));
 
 
     result = TestSolrConfigHandler.testForResponseElement(null,
@@ -181,7 +182,7 @@ public class TestReqParamsAPI extends AbstractFullDistribZkTestBase {
         "CY val",
         5);
     compareValues(result, "BY val", asList("params", "b"));
-    compareValues(result, null, asList("params", "a"));
+    compareValues(result, "A val", asList("params", "a"));
     compareValues(result, Arrays.asList("val 1", "val 2"), asList("params", "d"));
     compareValues(result, "20", asList("params", "i"));
     payload = " {\n" +
@@ -226,6 +227,37 @@ public class TestReqParamsAPI extends AbstractFullDistribZkTestBase {
         "P val",
         10);
     compareValues(result, null, asList("response", "params", "y", "c"));
+    compareValues(result, 2l, asList("response", "params", "y", "","v"));
+    compareValues(result, 0l, asList("response", "params", "x", "","v"));
+
+    payload = "{update :{x : {_appends_ :{ add : 'first' },  _invariants_ : {fixed: f }}}}";
+    TestSolrConfigHandler.runConfigCommand(writeHarness, "/config/params?wt=json", payload);
+
+    result = TestSolrConfigHandler.testForResponseElement(
+        null,
+        urls.get(random().nextInt(urls.size())),
+        "/config/params?wt=json",
+        cloudClient,
+        asList("response", "params", "x", "_appends_", "add"),
+        "first",
+        10);
+    compareValues(result, "f", asList("response", "params", "x", "_invariants_", "fixed"));
+
+
+    result = TestSolrConfigHandler.testForResponseElement(null,
+        urls.get(random().nextInt(urls.size())),
+        "/dump1?wt=json&fixed=changeit&add=second",
+        cloudClient,
+        asList("params", "fixed"),
+        "f",
+        5);
+    compareValues(result, new Predicate() {
+      @Override
+      public boolean test(Object o) {
+        List l = (List) o;
+        return l.contains("first") && l.contains("second");
+      }
+    }, asList("params", "add"));
 
     payload = " {'delete' : 'y'}";
     TestSolrConfigHandler.runConfigCommand(writeHarness, "/config/params?wt=json", payload);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9ce2d0d2/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java b/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java
index ea6eb52..76ba9c5 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java
@@ -21,6 +21,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Predicate;
 
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
@@ -288,8 +289,12 @@ public class TestSolrConfigHandlerCloud extends AbstractFullDistribZkTestBase {
   }
 
   public static void compareValues(Map result, Object expected, List<String> jsonPath) {
+    Object val = Utils.getObjectByPath(result, false, jsonPath);
     assertTrue(StrUtils.formatString("Could not get expected value  {0} for path {1} full output {2}", expected, jsonPath, getAsString(result)),
-        Objects.equals(expected, Utils.getObjectByPath(result, false, jsonPath)));
+        expected instanceof Predicate ?
+            ((Predicate)expected ).test(val) :
+            Objects.equals(expected, val)
+        );
   }
 
 }