You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ep...@apache.org on 2026/10/20 20:31:45 UTC
(solr) branch main updated: SOLR-16396: Align v2 full-config section keys with pluralised forms (#5175)
This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new d5a17012b2c SOLR-16396: Align v2 full-config section keys with pluralised forms (#5175)
d5a17012b2c is described below
commit d5a17012b2cf9e3ba903693e57b14fb6933aa1c7
Author: Jalaz Kumar <ja...@gmail.com>
AuthorDate: Wed Oct 21 02:01:38 2026 +0530
SOLR-16396: Align v2 full-config section keys with pluralised forms (#5175)
Co-authored-by: Eric Pugh <ep...@opensourceconnections.com>
Co-authored-by: Claude Sonnet 5 <no...@anthropic.com>
---
.../org/apache/solr/handler/SolrConfigHandler.java | 4 +-
.../apache/solr/handler/admin/api/GetConfig.java | 46 +++++++++++++++++++++-
.../apache/solr/core/TestSolrConfigHandler.java | 33 +++++++++++-----
.../handler/admin/api/GetConfigSolrCloudTest.java | 3 +-
.../solr/handler/admin/api/GetConfigTest.java | 8 +++-
.../configuration-guide/pages/config-api.adoc | 8 +++-
6 files changed, 86 insertions(+), 16 deletions(-)
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 2b2080a0673..341ecc2f096 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -190,7 +190,9 @@ public class SolrConfigHandler extends RequestHandlerBase
if (parts.size() == 1) {
// this is the whole config. sent out the whole payload
V2ApiUtils.squashIntoSolrResponseWithoutHeader(
- resp, new GetConfig(req).getConfig(req.getParams().getBool("expandParams", false)));
+ resp,
+ new GetConfig(req)
+ .getConfigWithInternalNames(req.getParams().getBool("expandParams", false)));
} else {
if (ConfigOverlay.NAME.equals(parts.get(1))) {
V2ApiUtils.squashIntoSolrResponseWithoutHeader(resp, new GetConfig(req).getOverlay());
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java
index b8efea0a11c..5978cb8e0f2 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java
@@ -24,6 +24,7 @@ import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_P
import jakarta.inject.Inject;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -70,13 +71,25 @@ public class GetConfig extends JerseyResource implements ConfigApi.Get {
public static final String REQUEST_HANDLERS = "requestHandlers";
- // v2-only renames: external (URL/response-key) component name -> internal SolrConfig section
- // name. v1's GET /config/{component} keeps using the internal names unchanged.
+ // v2-only renames for /config/{component}(/{componentName}): external name -> internal
+ // SolrConfig section name. Excludes requestHandler, which has its own dedicated endpoint instead
+ // of routing through these paths.
private static final Map<String, String> EXTERNAL_TO_INTERNAL_COMPONENT_NAME =
Map.of(
"searchComponents", "searchComponent",
"queryResponseWriters", "queryResponseWriter");
+ // Same, but for the full /config dump's top-level keys, which also renames requestHandler.
+ // Derived from the map above so the two can't drift apart.
+ private static final Map<String, String> EXTERNAL_TO_INTERNAL_FULL_CONFIG_NAME =
+ buildFullConfigNameMap();
+
+ private static Map<String, String> buildFullConfigNameMap() {
+ Map<String, String> map = new HashMap<>(EXTERNAL_TO_INTERNAL_COMPONENT_NAME);
+ map.put(REQUEST_HANDLERS, SolrRequestHandler.TYPE);
+ return Map.copyOf(map);
+ }
+
private static String toInternalComponentName(String externalName) {
return EXTERNAL_TO_INTERNAL_COMPONENT_NAME.getOrDefault(externalName, externalName);
}
@@ -90,6 +103,15 @@ public class GetConfig extends JerseyResource implements ConfigApi.Get {
return internalName;
}
+ private static String toExternalFullConfigName(String internalName) {
+ for (Map.Entry<String, String> e : EXTERNAL_TO_INTERNAL_FULL_CONFIG_NAME.entrySet()) {
+ if (e.getValue().equals(internalName)) {
+ return e.getKey();
+ }
+ }
+ return internalName;
+ }
+
private final SolrQueryRequest solrQueryRequest;
@Inject
@@ -100,11 +122,31 @@ public class GetConfig extends JerseyResource implements ConfigApi.Get {
@Override
@PermissionName(CONFIG_READ_PERM)
public ConfigInfoResponse getConfig(boolean expandParams) {
+ final var response = instantiateJerseyResponse(ConfigInfoResponse.class);
+ response.config = renameToExternalFullConfigNames(buildConfigMap(expandParams));
+ return response;
+ }
+
+ /**
+ * Same as {@link #getConfig(boolean)}, but keeps internal section names (e.g. {@code
+ * searchComponent}, {@code requestHandler}) instead of the v2-only pluralized forms. Used by
+ * {@link SolrConfigHandler}'s legacy v1 {@code GET /config}, which must preserve the original
+ * names for backward compatibility.
+ */
+ public ConfigInfoResponse getConfigWithInternalNames(boolean expandParams) {
final var response = instantiateJerseyResponse(ConfigInfoResponse.class);
response.config = buildConfigMap(expandParams);
return response;
}
+ private Map<String, Object> renameToExternalFullConfigNames(Map<String, Object> configMap) {
+ Map<String, Object> reMappedConfigMap = new NullKeyTolerantMap("children");
+ for (Map.Entry<String, Object> e : configMap.entrySet()) {
+ reMappedConfigMap.put(toExternalFullConfigName(e.getKey()), e.getValue());
+ }
+ return reMappedConfigMap;
+ }
+
@Override
@PermissionName(CONFIG_READ_PERM)
public ConfigOverlayResponse getOverlay() {
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 4cb5b8889b6..1f16c3ab7ff 100644
--- a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
+++ b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
@@ -98,13 +98,24 @@ public class TestSolrConfigHandler extends RestTestBase {
solrTestRule.reset();
}
+ /**
+ * The full "/config" response pluralizes some section names (e.g. searchComponent ->
+ * searchComponents, requestHandler -> requestHandlers) under the V2 API (see
+ * GetConfig#getConfig), but not under V1.
+ */
+ private static String fullConfigKey(
+ RestTestHarness harness, String internalName, String externalName) {
+ return harness.getBaseURL().contains("/____v2/") ? externalName : internalName;
+ }
+
public void testProperty() throws Exception {
RestTestHarness harness = restTestHarness;
+ String requestHandlerKey = fullConfigKey(harness, "requestHandler", "requestHandlers");
MapWriter confMap = getRespMap(harness, "/config");
- assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/luke"), null));
- assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/info"), null));
- assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/file"), null));
- assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/ping"), null));
+ assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/luke"), null));
+ assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/info"), null));
+ assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/file"), null));
+ assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/ping"), null));
String payload =
"{\n"
@@ -188,6 +199,10 @@ public class TestSolrConfigHandler extends RestTestBase {
}
public static void reqhandlertests(RestTestHarness writeHarness) throws Exception {
+ // The per-component endpoints used elsewhere in this test are unaffected by the full-config
+ // pluralization below, since they already use the pluralized name.
+ String searchComponentKey = fullConfigKey(writeHarness, "searchComponent", "searchComponents");
+ String requestHandlerKey = fullConfigKey(writeHarness, "requestHandler", "requestHandlers");
String payload =
"{\n"
+ "'create-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy'}\n"
@@ -332,7 +347,7 @@ public class TestSolrConfigHandler extends RestTestBase {
testForResponseElement(
writeHarness,
"/config",
- asList("config", "searchComponent", "tc", "class"),
+ asList("config", searchComponentKey, "tc", "class"),
"org.apache.solr.handler.component.TermsComponent",
TIMEOUT_S);
payload =
@@ -343,14 +358,14 @@ public class TestSolrConfigHandler extends RestTestBase {
testForResponseElement(
writeHarness,
"/config",
- asList("config", "searchComponent", "tc", "class"),
+ asList("config", searchComponentKey, "tc", "class"),
"org.apache.solr.handler.component.TermVectorComponent",
TIMEOUT_S);
payload = "{\n" + "'delete-searchcomponent' : 'tc'" + "}";
runConfigCommand(writeHarness, "/config", payload);
testForResponseElement(
- writeHarness, "/config", asList("config", "searchComponent", "tc"), null, TIMEOUT_S);
+ writeHarness, "/config", asList("config", searchComponentKey, "tc"), null, TIMEOUT_S);
// <valueSourceParser name="countUsage"
// class="org.apache.solr.core.CountUsageValueSourceParser"/>
payload =
@@ -444,7 +459,7 @@ public class TestSolrConfigHandler extends RestTestBase {
testForResponseElement(
writeHarness,
"/config",
- asList("config", "searchComponent", "myspellcheck", "spellchecker", "class"),
+ asList("config", searchComponentKey, "myspellcheck", "spellchecker", "class"),
"solr.DirectSolrSpellChecker",
TIMEOUT_S);
@@ -463,7 +478,7 @@ public class TestSolrConfigHandler extends RestTestBase {
testForResponseElement(
writeHarness,
"/config",
- asList("config", "requestHandler", "/dump100", "class"),
+ asList("config", requestHandlerKey, "/dump100", "class"),
"org.apache.solr.handler.DumpRequestHandler",
TIMEOUT_S);
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
index 20dbd09d1a6..8ae8b63afbf 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
@@ -58,7 +58,8 @@ public class GetConfigSolrCloudTest extends SolrCloudTestCase {
assertTrue(response.config.containsKey("luceneMatchVersion"));
assertTrue(response.config.containsKey("updateHandler"));
assertTrue(response.config.containsKey("query"));
- assertTrue(response.config.containsKey("requestHandler"));
+ assertFalse(response.config.containsKey("requestHandler"));
+ assertTrue(response.config.containsKey("requestHandlers"));
}
@Test
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java
index 4407845d155..c9c836405f8 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java
@@ -55,8 +55,12 @@ public class GetConfigTest extends SolrTestCase {
assertTrue(response.config.containsKey("luceneMatchVersion"));
assertTrue(response.config.containsKey("updateHandler"));
assertTrue(response.config.containsKey("query"));
- assertTrue(response.config.containsKey("requestHandler"));
- assertTrue(response.config.containsKey("searchComponent"));
+ assertFalse(response.config.containsKey("requestHandler"));
+ assertTrue(response.config.containsKey("requestHandlers"));
+
+ assertFalse(response.config.containsKey("searchComponent"));
+ assertTrue(response.config.containsKey("searchComponents"));
+
assertTrue(response.config.containsKey("updateProcessor"));
}
diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc
index 01c043ae6a6..cbf7836a4f2 100644
--- a/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc
+++ b/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc
@@ -74,7 +74,9 @@ V2 API::
http://localhost:8983/api/collections/techproducts/config
----
-The response is the same merged configuration as the V1 API, with one difference: a plugin's unnamed child elements are grouped under a self-describing `children` key instead of an empty string, since Solr's V2 JSON serialization library does not allow empty-string keys:
+The response is the same merged configuration as the V1 API, with three differences.
+
+First, a plugin's unnamed child elements are grouped under a self-describing `children` key instead of an empty string, since Solr's V2 JSON serialization library does not allow empty-string keys:
[source,json]
----
@@ -87,6 +89,10 @@ The response is the same merged configuration as the V1 API, with one difference
]
}]
----
+
+Second, the `searchComponent` and `queryResponseWriter` sections are renamed to their pluralized forms, `searchComponents` and `queryResponseWriters`, as described below.
+
+Third, the `requestHandler` section is similarly renamed to `requestHandlers`, matching the dedicated `/config/requestHandlers` endpoint described below.
====
======