From f0204cbd188c60044960d08406ce6aa377069bcf Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 19 Aug 2026 18:05:35 +0300 Subject: [PATCH 1/2] SOLR-18373: remove NamedList.asShallowMap/get(String,int) and SolrParams.toNamedList Four deprecated members, 30 call sites over five compile rounds. The replacement for asShallowMap is the SimpleOrderedMap(MapWriter) constructor, which both deprecation notes point at: NamedList implements MapWriter, and SimpleOrderedMap extends NamedList and implements Map, so it can stand in wherever a Map was wanted. get(String, int) becomes indexOf(name, start) with getVal(idx), which is the same loop - indexOf's body is character-for-character the removed method's except that it returns the index. And SolrParams.toNamedList becomes new SimpleOrderedMap<>(params), whose writeMap applies the identical String-versus-String[] rule. What the deprecation notes do not say, and what had to be read out of the deleted code. asShallowMap was a hybrid, not a view. get, put, remove, clear and containsKey were live against the backing NamedList, but entrySet(), keySet() and values() all returned asMap(1) - a depth-1 COPY that collapsed duplicate keys into a List and converted nested NamedList values to Maps. So the migration is behaviour-preserving for the read-only sites and changes duplicate handling for the ones that stream over entrySet: SolrXmlConfig would now let Collectors.toMap throw on duplicate coreAdminHandlerActions entries rather than stringify a collapsed List, and LTRThreadModule would remove both copies of a duplicated threadModule key rather than one. Both are arguably fixes; neither is reachable with the flat scalar values those sites actually see. containsKey is the other axis: the removed view's was get(key) != null, SimpleOrderedMap's is indexOf(key) >= 0, and they differ for a key present with a null value. Reached only at PackageManager, where a top-level null params cannot occur. Two sites were not read-only at all - QueryComponent and CombinedQueryComponent both did getResponseHeader().asShallowMap().put(...), i.e. a write-through. They now do the same thing explicitly, indexOf then add-or-setVal, which is what the deleted put did. Neither setPartialResults (add-only-if-absent, and the key may already hold "omitted") nor the file's neighbouring remove-then-add idiom is equivalent, the latter because it also moves the key to the end of the header and changes serialized order. Two things that looked like format risks and were not, both settled by reading rather than assuming. JavaBinCodec writes ORDERED_MAP for a SimpleOrderedMap and NAMED_LST for a plain NamedList, so swapping the type inside an update request or a response header looks like a wire change - but the removed toNamedList() already constructed a SimpleOrderedMap, so the tag was already ORDERED_MAP. And SolrParams.writeMap is the canonical serialisation used everywhere else; it differs from the removed method only in skipping a parameter whose value array is empty, which toNamedList() emitted as an empty array. One site where the documented replacement would have been a bug. SolrJacksonMapper registers a StdSerializer and did writeObject(value.asShallowMap()); handing it a SimpleOrderedMap, which IS a NamedList, would dispatch straight back into the same serializer forever. It uses asMap(0) instead - a plain LinkedHashMap that leaves nested NamedLists for Jackson to dispatch one level at a time, which is what the anonymous Map did. Left better than found. NamedListTest.testShallowMap tested only the removed method's write-through and is deleted; in its place SimpleOrderedMapTest gains the invariant every migrated call site now depends on - that the MapWriter constructor copies, so mutating the copy does not reach the source and adding to the source does not reach the copy. A trap confirms it is not vacuous: asserting view semantics instead fails exactly that test, 1 of 17, with zero compile errors. DefaultSchemaSuggester needed no wrapper at all: fieldProps is already a SimpleOrderedMap, so dropping .asShallowMap() passes the same instance and even preserves the write-through. Verified: compileJava and compileTestJava for the whole build, spotlessCheck, ecjLintMain and ecjLintTest on solrj and core, renderJavadoc on both, and every changed test class - 8 classes, 71 tests, 0 failures, 1 skipped. Two findings parked rather than touched, both pre-existing: SolrQueryResponse.getResponseHeader declares NamedList while its body casts to SimpleOrderedMap, so widening that return type would collapse both write-through hunks to one line each - but it is a public and binary-incompatible API change, so it belongs to its own ticket. And PackageManager tests a top-level "params" key while SolrConfigHandler puts the paramset under "response", so packageParamsExist appears to be permanently false. AI-assisted (Claude Sonnet 5) --- ...sshallowmap-and-solrparams-tonamedlist.yml | 8 ++ .../java/org/apache/solr/core/SolrCore.java | 4 +- .../org/apache/solr/core/SolrXmlConfig.java | 11 +- .../solr/handler/DumpRequestHandler.java | 2 +- .../component/CombinedQueryComponent.java | 11 +- .../handler/component/QueryComponent.java | 11 +- .../designer/DefaultSchemaSuggester.java | 4 +- .../apache/solr/jersey/SolrJacksonMapper.java | 3 +- .../solr/packagemanager/PackageManager.java | 21 ++- .../apache/solr/update/IndexFingerprint.java | 2 +- .../org/apache/solr/util/PivotListEntry.java | 3 +- .../search/facet/TestCloudJSONFacetSKG.java | 3 +- .../test/org/apache/solr/util/TestUtils.java | 4 +- .../org/apache/solr/ltr/LTRThreadModule.java | 3 +- .../client/solrj/impl/CloudSolrClient.java | 6 +- .../request/JavaBinUpdateRequestCodec.java | 4 +- .../solrj/response/schema/SchemaResponse.java | 15 +- .../apache/solr/common/params/SolrParams.java | 25 ---- .../apache/solr/common/util/NamedList.java | 136 +----------------- .../solrj/impl/CloudHttp2SolrClientTest.java | 6 +- .../solr/common/util/NamedListTest.java | 18 --- .../common/util/SimpleOrderedMapTest.java | 18 +++ 22 files changed, 93 insertions(+), 225 deletions(-) create mode 100644 changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml diff --git a/changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml b/changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml new file mode 100644 index 000000000000..1af2c914738a --- /dev/null +++ b/changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml @@ -0,0 +1,8 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: Remove the deprecated NamedList.asShallowMap(), asShallowMap(boolean) and get(String, int) methods, and SolrParams.toNamedList(). Use the SimpleOrderedMap(MapWriter) constructor where a Map is needed - note it copies rather than returning a live view - and indexOf(String, int) with getVal(int) to scan from an index. +type: removed +authors: + - name: Serhiy Bzhezytskyy +links: + - name: SOLR-18373 + url: https://issues.apache.org/jira/browse/SOLR-18373 diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index 5f7e1f268dc6..9b1c7662a52b 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -3028,9 +3028,9 @@ public static void postDecorateResponse( + "'"); } if (echoParams == EchoParamStyle.EXPLICIT) { - responseHeader.add("params", req.getOriginalParams().toNamedList()); + responseHeader.add("params", new SimpleOrderedMap<>(req.getOriginalParams())); } else if (echoParams == EchoParamStyle.ALL) { - responseHeader.add("params", req.getParams().toNamedList()); + responseHeader.add("params", new SimpleOrderedMap<>(req.getParams())); } } } diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java index aa9ba040bf9b..9c66ca96e7ed 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java +++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java @@ -45,6 +45,7 @@ import org.apache.solr.common.util.DOMUtil; import org.apache.solr.common.util.EnvUtils; 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.common.util.Utils; import org.apache.solr.logging.LogWatcherConfig; @@ -127,11 +128,11 @@ public static NodeConfig fromConfig( // It should go inside the fillSolrSection method but // since it is arranged as a separate section it is placed here Map coreAdminHandlerActions = - readNodeListAsNamedList(root.get("coreAdminHandlerActions"), "") - .asShallowMap() - .entrySet() - .stream() - .collect(Collectors.toMap(Entry::getKey, item -> item.getValue().toString())); + new SimpleOrderedMap<>( + readNodeListAsNamedList( + root.get("coreAdminHandlerActions"), "")) + .entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, item -> item.getValue().toString())); UpdateShardHandlerConfig updateConfig; if (deprecatedUpdateConfig == null) { diff --git a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java index d96b33517ca2..c0e43985a7e7 100644 --- a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java @@ -43,7 +43,7 @@ public class DumpRequestHandler extends RequestHandlerBase { @SuppressWarnings({"unchecked"}) public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { // Show params - rsp.add("params", req.getParams().toNamedList()); + rsp.add("params", new SimpleOrderedMap<>(req.getParams())); String[] parts = req.getParams().getParams("urlTemplateValues"); if (parts != null && parts.length > 0) { Map map = new LinkedHashMap<>(); diff --git a/solr/core/src/java/org/apache/solr/handler/component/CombinedQueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/CombinedQueryComponent.java index 28b4a80bb6ec..060169720661 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/CombinedQueryComponent.java +++ b/solr/core/src/java/org/apache/solr/handler/component/CombinedQueryComponent.java @@ -455,10 +455,13 @@ protected void mergeIds(ResponseBuilder rb, ShardRequest sreq) { populateNextCursorMarkFromMergedShards(rb); if (thereArePartialResults) { - rb.rsp - .getResponseHeader() - .asShallowMap() - .put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, Boolean.TRUE); + NamedList header = rb.rsp.getResponseHeader(); + int idx = header.indexOf(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY); + if (idx == -1) { + header.add(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, Boolean.TRUE); + } else { + header.setVal(idx, Boolean.TRUE); + } } if (segmentTerminatedEarly != null) { final Object existingSegmentTerminatedEarly = diff --git a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java index 3c8c2a7a5bdd..7e4f35c2a232 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java +++ b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java @@ -1237,10 +1237,13 @@ protected void mergeIds(ResponseBuilder rb, ShardRequest sreq) { populateNextCursorMarkFromMergedShards(rb); if (thereArePartialResults) { - rb.rsp - .getResponseHeader() - .asShallowMap() - .put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, Boolean.TRUE); + NamedList header = rb.rsp.getResponseHeader(); + int idx = header.indexOf(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY); + if (idx == -1) { + header.add(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, Boolean.TRUE); + } else { + header.setVal(idx, Boolean.TRUE); + } } if (segmentTerminatedEarly != null) { final Object existingSegmentTerminatedEarly = diff --git a/solr/core/src/java/org/apache/solr/handler/designer/DefaultSchemaSuggester.java b/solr/core/src/java/org/apache/solr/handler/designer/DefaultSchemaSuggester.java index 543b7a77af16..0d0d974d1a81 100644 --- a/solr/core/src/java/org/apache/solr/handler/designer/DefaultSchemaSuggester.java +++ b/solr/core/src/java/org/apache/solr/handler/designer/DefaultSchemaSuggester.java @@ -186,9 +186,7 @@ public ManagedIndexSchema adaptExistingFieldToData( fieldProps.add("multiValued", true); fieldProps.remove("name"); fieldProps.remove("type"); - schema = - schema.replaceField( - schemaField.getName(), schemaField.getType(), fieldProps.asShallowMap()); + schema = schema.replaceField(schemaField.getName(), schemaField.getType(), fieldProps); } // TODO: other "healing" type operations here ... but we have to be careful about overriding // explicit user changes such as a user making a text field a string field, we wouldn't want to diff --git a/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java b/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java index 7f57715993c3..18702de3f3af 100644 --- a/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java +++ b/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java @@ -70,7 +70,8 @@ public NamedListSerializer(Class nlClazz) { @Override public void serialize(NamedList value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeObject(value.asShallowMap()); + // Not SimpleOrderedMap: it IS a NamedList, so this serializer would recurse on it. + gen.writeObject(value.asMap(0)); } } } diff --git a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java index 128538e643bc..d29a2f73001b 100644 --- a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java +++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java @@ -59,6 +59,7 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.Pair; +import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.Utils; import org.apache.solr.filestore.DistribFileStore; @@ -288,7 +289,7 @@ public Map getPackagesDeployedAsClusterLevelPlugins // Cluster props doesn't exist, that means there are no cluster level plugins installed. result = Map.of(); } else { - result = response.asShallowMap(); + result = new SimpleOrderedMap<>(response); } } catch (SolrServerException | IOException ex) { throw new SolrException(ErrorCode.SERVER_ERROR, ex); @@ -421,16 +422,14 @@ private Pair, List> deployCollectionPackage( // Get package params try { - boolean packageParamsExist = - solrClient - .request( - new GenericV2SolrRequest( - SolrRequest.METHOD.GET, - PackageUtils.getCollectionParamsPath(collection) + "/packages") - .setRequiresCollection( - false) /* Making a collection-request, but already baked into path */) - .asShallowMap() - .containsKey("params"); + NamedList collectionParams = + solrClient.request( + new GenericV2SolrRequest( + SolrRequest.METHOD.GET, + PackageUtils.getCollectionParamsPath(collection) + "/packages") + .setRequiresCollection( + false) /* Making a collection-request, but already baked into path */); + boolean packageParamsExist = new SimpleOrderedMap<>(collectionParams).containsKey("params"); SolrCLI.postJsonToSolr( solrClient, PackageUtils.getCollectionParamsPath(collection), diff --git a/solr/core/src/java/org/apache/solr/update/IndexFingerprint.java b/solr/core/src/java/org/apache/solr/update/IndexFingerprint.java index 1323c9eb083c..4d4ee4664feb 100644 --- a/solr/core/src/java/org/apache/solr/update/IndexFingerprint.java +++ b/solr/core/src/java/org/apache/solr/update/IndexFingerprint.java @@ -200,7 +200,7 @@ public static IndexFingerprint fromObject(Object o) { if (o instanceof Map) { map = (Map) o; } else if (o instanceof NamedList) { - map = ((NamedList) o).asShallowMap(); + map = new SimpleOrderedMap<>((NamedList) o); } else { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type " + o); } diff --git a/solr/core/src/java/org/apache/solr/util/PivotListEntry.java b/solr/core/src/java/org/apache/solr/util/PivotListEntry.java index 74457def7981..3238dbad7f55 100644 --- a/solr/core/src/java/org/apache/solr/util/PivotListEntry.java +++ b/solr/core/src/java/org/apache/solr/util/PivotListEntry.java @@ -80,6 +80,7 @@ public T extract(NamedList pivotList) { } // otherwise... // scan starting at the min/optional index - return pivotList.get(this.getName(), this.minIndex); + final int idx = pivotList.indexOf(this.getName(), this.minIndex); + return idx == -1 ? null : pivotList.getVal(idx); } } diff --git a/solr/core/src/test/org/apache/solr/search/facet/TestCloudJSONFacetSKG.java b/solr/core/src/test/org/apache/solr/search/facet/TestCloudJSONFacetSKG.java index 48ccb4a133f3..3695c732e508 100644 --- a/solr/core/src/test/org/apache/solr/search/facet/TestCloudJSONFacetSKG.java +++ b/solr/core/src/test/org/apache/solr/search/facet/TestCloudJSONFacetSKG.java @@ -44,6 +44,7 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.embedded.JettySolrRunner; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -499,7 +500,7 @@ private void assertFacetSKGsAreCorrect( assertEquals( "Unexpected keys in facet response", expectedKeys, - actualFacetResponse.asShallowMap().keySet()); + new SimpleOrderedMap<>(actualFacetResponse).keySet()); } } diff --git a/solr/core/src/test/org/apache/solr/util/TestUtils.java b/solr/core/src/test/org/apache/solr/util/TestUtils.java index 1228ff0c8bd2..d0da76dc4198 100644 --- a/solr/core/src/test/org/apache/solr/util/TestUtils.java +++ b/solr/core/src/test/org/apache/solr/util/TestUtils.java @@ -70,9 +70,9 @@ public void testNamedLists() { assertEquals("one", map.getName(0)); map.setName(0, "ONE"); assertEquals("ONE", map.getName(0)); - assertEquals(Integer.valueOf(100), map.get("one", 1)); + assertEquals(Integer.valueOf(100), map.getVal(map.indexOf("one", 1))); assertEquals(4, map.indexOf(null, 1)); - assertNull(map.get(null, 1)); + assertNull(map.getVal(map.indexOf(null, 1))); map = new SimpleOrderedMap<>(); map.add("one", 1); diff --git a/solr/modules/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java b/solr/modules/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java index dccae8bb3194..43dad63e2a1a 100644 --- a/solr/modules/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java +++ b/solr/modules/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java @@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.plugin.NamedListInitializedPlugin; @@ -88,7 +89,7 @@ private static NamedList extractThreadModuleParams(NamedList args) { // remove consumed keys only once iteration is complete // since NamedList iterator does not support 'remove' - for (Object key : extractedArgs.asShallowMap().keySet()) { + for (Object key : new SimpleOrderedMap<>(extractedArgs).keySet()) { args.remove(CONFIG_PREFIX + key); } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java index 220f1a72f646..ac4f1401a504 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java @@ -723,7 +723,11 @@ protected NamedList requestWithRetryOnStaleState( resp = sendRequest(request, inputCollections); // to avoid an O(n) operation we always add STATE_VERSION to the last and try to read it from // there - Object o = resp == null || resp.size() == 0 ? null : resp.get(STATE_VERSION, resp.size() - 1); + Object o = null; + if (resp != null && resp.size() > 0) { + final int stateVersionIdx = resp.indexOf(STATE_VERSION, resp.size() - 1); + o = stateVersionIdx == -1 ? null : resp.getVal(stateVersionIdx); + } if (o != null && o instanceof Map invalidStates) { // remove this because no one else needs this and tests would fail if they are comparing // responses diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/JavaBinUpdateRequestCodec.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/JavaBinUpdateRequestCodec.java index 6ce10ef381b8..9f9b82a32e66 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/JavaBinUpdateRequestCodec.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/JavaBinUpdateRequestCodec.java @@ -35,6 +35,7 @@ import org.apache.solr.common.util.DataInputInputStream; import org.apache.solr.common.util.JavaBinCodec; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; /** * Provides methods for marshalling an UpdateRequest to a NamedList which can be serialized in the @@ -56,7 +57,8 @@ public class JavaBinUpdateRequestCodec { public void marshal(UpdateRequest updateRequest, OutputStream os) throws IOException { NamedList nl = new NamedList<>(); - NamedList params = updateRequest.getParams().toNamedList(); + // SimpleOrderedMap serializes with the same JavaBin ORDERED_MAP tag as before. + NamedList params = new SimpleOrderedMap<>(updateRequest.getParams()); if (updateRequest.getCommitWithin() != -1) { params.add("commitWithin", updateRequest.getCommitWithin()); } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/schema/SchemaResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/schema/SchemaResponse.java index 7f34859f0a31..e2ca9d835f17 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/schema/SchemaResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/schema/SchemaResponse.java @@ -24,6 +24,7 @@ import org.apache.solr.client.solrj.request.schema.FieldTypeDefinition; import org.apache.solr.client.solrj.response.SolrResponseBase; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; /** * This class is used to wrap the response messages retrieved from Solr Schema API. @@ -267,7 +268,7 @@ public static class SchemaNameResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - schemaName = SchemaResponse.getSchemaName(response.asShallowMap()); + schemaName = SchemaResponse.getSchemaName(new SimpleOrderedMap<>(response)); } public String getSchemaName() { @@ -282,7 +283,7 @@ public static class SchemaVersionResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - schemaVersion = SchemaResponse.getSchemaVersion(response.asShallowMap()); + schemaVersion = SchemaResponse.getSchemaVersion(new SimpleOrderedMap<>(response)); } public float getSchemaVersion() { @@ -314,7 +315,7 @@ public static class FieldsResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - fields = SchemaResponse.getFields(response.asShallowMap()); + fields = SchemaResponse.getFields(new SimpleOrderedMap<>(response)); } public List> getFields() { @@ -361,7 +362,7 @@ public static class UniqueKeyResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - uniqueKey = SchemaResponse.getSchemaUniqueKey(response.asShallowMap()); + uniqueKey = SchemaResponse.getSchemaUniqueKey(new SimpleOrderedMap<>(response)); } public String getUniqueKey() { @@ -376,7 +377,7 @@ public static class GlobalSimilarityResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - similarity = SchemaResponse.getSimilarity(response.asShallowMap()); + similarity = SchemaResponse.getSimilarity(new SimpleOrderedMap<>(response)); } public Map getSimilarity() { @@ -391,7 +392,7 @@ public static class CopyFieldsResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - copyFields = SchemaResponse.getCopyFields(response.asShallowMap()); + copyFields = SchemaResponse.getCopyFields(new SimpleOrderedMap<>(response)); } public List> getCopyFields() { @@ -423,7 +424,7 @@ public static class FieldTypesResponse extends SolrResponseBase { public void setResponse(NamedList response) { super.setResponse(response); - fieldTypes = SchemaResponse.getFieldTypeRepresentations(response.asShallowMap()); + fieldTypes = SchemaResponse.getFieldTypeRepresentations(new SimpleOrderedMap<>(response)); } public List getFieldTypes() { diff --git a/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java b/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java index 9243350766ed..7116f4a5db94 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java @@ -31,8 +31,6 @@ import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; /** @@ -411,29 +409,6 @@ public static SolrParams wrapAppended(SolrParams params, SolrParams defaults) { return AppendedSolrParams.wrapAppended(params, defaults); } - /** - * Convert this to a NamedList of unique keys with either String or String[] values depending on - * how many values there are for the parameter. - * - * @deprecated see {@link SimpleOrderedMap#SimpleOrderedMap(MapWriter)} - */ - @Deprecated - public NamedList toNamedList() { - final SimpleOrderedMap result = new SimpleOrderedMap<>(); - - for (Iterator it = getParameterNamesIterator(); it.hasNext(); ) { - final String name = it.next(); - final String[] values = getParams(name); - if (values.length == 1) { - result.add(name, values[0]); - } else { - // currently, no reason not to use the same array - result.add(name, values); - } - } - return result; - } - /** * Returns this SolrParams as a proper URL encoded string, starting with {@code "?"}, if not * empty. diff --git a/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java b/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java index e4f27c4025b6..0412d13faa76 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.function.BiConsumer; import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; @@ -251,10 +250,10 @@ public int indexOf(String name) { * * @return null if not found or if the value stored was null. * @see #indexOf - * @see #get(String,int) */ public T get(String name) { - return get(name, 0); + final int idx = indexOf(name); + return idx == -1 ? null : getVal(idx); } /** Like {@link #get(String)} but returns a default value if it would be null. */ @@ -263,31 +262,6 @@ public T getOrDefault(String name, T def) { return val == null ? def : val; } - /** - * Gets the value for the first instance of the specified name found starting at the specified - * index. - * - *

NOTE: this runs in linear time (it scans starting at the specified position until it finds - * the first pair with the specified name). - * - * @return null if not found or if the value stored was null. - * @see #indexOf - * @deprecated Use {@link #indexOf(String, int)} then {@link #getVal(int)}. - */ - @Deprecated - public T get(String name, int start) { - int sz = size(); - for (int i = start; i < sz; i++) { - String n = getName(i); - if (name == null) { - if (n == null) return getVal(i); - } else if (name.equals(n)) { - return getVal(i); - } - } - return null; - } - /** * Gets the values for the specified name * @@ -343,112 +317,6 @@ public NamedList getImmutableCopy() { return new NamedList<>(Collections.unmodifiableList(copy.nvPairs)); } - /** - * @deprecated Use {@link SimpleOrderedMap} instead. - */ - @Deprecated - public Map asShallowMap() { - return asShallowMap(false); - } - - /** - * @deprecated use {@link SimpleOrderedMap} instead of NamedList when a Map is required. - */ - @Deprecated - public Map asShallowMap(boolean allowDps) { - return new Map<>() { - @Override - public int size() { - return NamedList.this.size(); - } - - @Override - public boolean isEmpty() { - return size() == 0; - } - - @Override - public boolean containsKey(Object key) { - return NamedList.this.get((String) key) != null; - } - - @Override - public boolean containsValue(Object value) { - return false; - } - - @Override - public T get(Object key) { - return NamedList.this.get((String) key); - } - - @Override - public T put(String key, T value) { - if (allowDps) { - NamedList.this.add(key, value); - return null; - } - int idx = NamedList.this.indexOf(key, 0); - if (idx == -1) { - NamedList.this.add(key, value); - } else { - NamedList.this.setVal(idx, value); - } - return null; - } - - @Override - public T remove(Object key) { - return NamedList.this.remove((String) key); - } - - @Override - @SuppressWarnings({"unchecked"}) - public void putAll(Map m) { - boolean isEmpty = isEmpty(); - for (Object o : m.entrySet()) { - @SuppressWarnings({"rawtypes"}) - Map.Entry e = (Entry) o; - if (isEmpty) { // we know that there are no duplicates - add((String) e.getKey(), (T) e.getValue()); - } else { - put(e.getKey() == null ? null : e.getKey().toString(), (T) e.getValue()); - } - } - } - - @Override - public void clear() { - NamedList.this.clear(); - } - - @Override - @SuppressWarnings({"unchecked"}) - public Set keySet() { - // TODO implement more efficiently - return NamedList.this.asMap(1).keySet(); - } - - @Override - @SuppressWarnings({"unchecked", "rawtypes"}) - public Collection values() { - // TODO implement more efficiently - return NamedList.this.asMap(1).values(); - } - - @Override - public Set> entrySet() { - // TODO implement more efficiently - return NamedList.this.asMap(1).entrySet(); - } - - @Override - public void forEach(BiConsumer action) { - NamedList.this.forEach(action); - } - }; - } - @SuppressWarnings("rawtypes") public Map asMap(int maxDepth) { LinkedHashMap result = new LinkedHashMap<>(); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java index 88ae42fc32c4..75880a6bc9a1 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java @@ -864,9 +864,11 @@ public void stateVersionParamTest() throws Exception { COLLECTION + ":" + (coll.getZNodeVersion() - 1)); // an older version expect error QueryResponse rsp = solrClient.query(q); + final NamedList response = rsp.getResponse(); + final int stateVersionIdx = + response.indexOf(CloudSolrClient.STATE_VERSION, response.size() - 1); @SuppressWarnings({"rawtypes"}) - Map m = - (Map) rsp.getResponse().get(CloudSolrClient.STATE_VERSION, rsp.getResponse().size() - 1); + Map m = stateVersionIdx == -1 ? null : (Map) response.getVal(stateVersionIdx); assertNotNull( "Expected an extra information from server with the list of invalid collection states", m); diff --git a/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java b/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java index cadc88ed8908..57bfffdf0577 100644 --- a/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java +++ b/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import org.apache.solr.SolrTestCase; import org.apache.solr.common.SolrException; import org.junit.Test; @@ -192,21 +191,4 @@ public void testRecursive() { Object enltest4 = enl._get(List.of("key2"), null); assertNull(enltest4); } - - @Test - public void testShallowMap() { - NamedList nl = new NamedList<>(); - nl.add("key1", "Val1"); - Map m = nl.asShallowMap(); - m.put("key1", "Val1_"); - assertEquals("Val1_", nl.get("key1")); - assertEquals("Val1_", m.get("key1")); - assertEquals(0, nl.indexOf("key1", 0)); - m.putAll(Map.of("key1", "Val1__", "key2", "Val2")); - assertEquals("Val1__", nl.get("key1")); - assertEquals("Val1__", m.get("key1")); - assertEquals(0, nl.indexOf("key1", 0)); - assertEquals("Val2", nl.get("key2")); - assertEquals("Val2", m.get("key2")); - } } diff --git a/solr/solrj/src/test/org/apache/solr/common/util/SimpleOrderedMapTest.java b/solr/solrj/src/test/org/apache/solr/common/util/SimpleOrderedMapTest.java index b91f991251cc..f50216f48486 100644 --- a/solr/solrj/src/test/org/apache/solr/common/util/SimpleOrderedMapTest.java +++ b/solr/solrj/src/test/org/apache/solr/common/util/SimpleOrderedMapTest.java @@ -193,6 +193,24 @@ public void remove() { assertFalse(map.containsKey("two")); } + /** The MapWriter constructor copies rather than returning a live view. */ + @Test + public void testMapWriterConstructorCopiesRatherThanViewing() { + final NamedList source = new NamedList<>(); + source.add("one", 1); + + final SimpleOrderedMap copy = new SimpleOrderedMap<>(source); + assertEquals(Integer.valueOf(1), copy.get("one")); + + copy.put("one", 11); + assertEquals( + "mutating the copy must not reach the source", Integer.valueOf(1), source.get("one")); + assertEquals(Integer.valueOf(11), copy.get("one")); + + source.add("two", 2); + assertNull("adding to the source must not reach the copy", copy.get("two")); + } + private void setupData() { map.add("one", 1); map.add("two", 2); From bad3f17b57466c1894ad758cafe92d3f89d1692e Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Fri, 21 Aug 2026 08:05:45 +0300 Subject: [PATCH 2/2] SOLR-18373: drop the changelog entry Same shape as #4763 (David: not changelog-worthy) -- narrow, rarely-used NamedList/SolrParams methods, no observable behavior change. --- ...-namedlist-asshallowmap-and-solrparams-tonamedlist.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml diff --git a/changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml b/changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml deleted file mode 100644 index 1af2c914738a..000000000000 --- a/changelog/unreleased/SOLR-18373-remove-namedlist-asshallowmap-and-solrparams-tonamedlist.yml +++ /dev/null @@ -1,8 +0,0 @@ -# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc -title: Remove the deprecated NamedList.asShallowMap(), asShallowMap(boolean) and get(String, int) methods, and SolrParams.toNamedList(). Use the SimpleOrderedMap(MapWriter) constructor where a Map is needed - note it copies rather than returning a live view - and indexOf(String, int) with getVal(int) to scan from an index. -type: removed -authors: - - name: Serhiy Bzhezytskyy -links: - - name: SOLR-18373 - url: https://issues.apache.org/jira/browse/SOLR-18373