This is an automated email from the ASF dual-hosted git repository.
dsmiley 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 55e3b8838b4 SOLR-18373: remove NamedList.asShallowMap/get(String,int)
and SolrParams.toNamedList (#4761)
55e3b8838b4 is described below
commit 55e3b8838b43c6da6e6f26f8a663f46abffe4ed7
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Fri Aug 28 06:05:55 2026 +0300
SOLR-18373: remove NamedList.asShallowMap/get(String,int) and
SolrParams.toNamedList (#4761)
And improve performance of the V2 API for serializing SimpleOrderedMap
(custom Jackson serializer).
---
.../src/java/org/apache/solr/core/SolrCore.java | 4 +-
.../java/org/apache/solr/core/SolrXmlConfig.java | 12 +-
.../apache/solr/handler/DumpRequestHandler.java | 2 +-
.../handler/component/CombinedQueryComponent.java | 18 ++-
.../solr/handler/component/QueryComponent.java | 25 ++--
.../handler/designer/DefaultSchemaSuggester.java | 4 +-
.../org/apache/solr/jersey/SolrJacksonMapper.java | 38 +++++-
.../apache/solr/packagemanager/PackageManager.java | 26 ++--
.../org/apache/solr/update/IndexFingerprint.java | 2 +-
.../java/org/apache/solr/util/PivotListEntry.java | 3 +-
.../handler/component/MockResponseBuilder.java | 4 +-
.../apache/solr/jersey/SolrJacksonMapperTest.java | 80 ++++++++++++
.../solr/search/facet/TestCloudJSONFacetSKG.java | 3 +-
.../src/test/org/apache/solr/util/TestUtils.java | 4 +-
.../java/org/apache/solr/ltr/LTRThreadModule.java | 3 +-
.../solr/client/solrj/impl/CloudSolrClient.java | 6 +-
.../solrj/request/JavaBinUpdateRequestCodec.java | 5 +-
.../solrj/response/schema/SchemaResponse.java | 15 +--
.../org/apache/solr/common/params/SolrParams.java | 25 ----
.../org/apache/solr/common/util/NamedList.java | 136 +--------------------
.../solrj/impl/CloudHttp2SolrClientTest.java | 6 +-
.../org/apache/solr/common/util/NamedListTest.java | 18 ---
.../solr/common/util/SimpleOrderedMapTest.java | 18 +++
23 files changed, 212 insertions(+), 245 deletions(-)
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 291d06eadce..c9efc7c98a5 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -3033,9 +3033,9 @@ public class SolrCore implements SolrInfoBean, Closeable {
+ "'");
}
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 aa9ba040bf9..de58f5f50aa 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
@@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -126,12 +127,11 @@ public class SolrXmlConfig {
// It should go inside the fillSolrSection method but
// since it is arranged as a separate section it is placed here
- Map<String, String> coreAdminHandlerActions =
- readNodeListAsNamedList(root.get("coreAdminHandlerActions"),
"<coreAdminHandlerActions>")
- .asShallowMap()
- .entrySet()
- .stream()
- .collect(Collectors.toMap(Entry::getKey, item ->
item.getValue().toString()));
+ Map<String, String> coreAdminHandlerActions = new LinkedHashMap<>();
+ for (Entry<String, Object> entry :
+ readNodeListAsNamedList(root.get("coreAdminHandlerActions"),
"<coreAdminHandlerActions>")) {
+ coreAdminHandlerActions.put(entry.getKey(), entry.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 d96b33517ca..c0e43985a7e 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<String, String> 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 28b4a80bb6e..54a73256e28 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,10 @@ public class CombinedQueryComponent extends
QueryComponent implements SolrCoreAw
populateNextCursorMarkFromMergedShards(rb);
if (thereArePartialResults) {
- rb.rsp
- .getResponseHeader()
- .asShallowMap()
- .put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY,
Boolean.TRUE);
+ updateResponseHeader(
+ rb.rsp.getResponseHeader(),
+ SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY,
+ Boolean.TRUE);
}
if (segmentTerminatedEarly != null) {
final Object existingSegmentTerminatedEarly =
@@ -472,12 +472,10 @@ public class CombinedQueryComponent extends
QueryComponent implements SolrCoreAw
SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY,
segmentTerminatedEarly);
} else if (!Boolean.TRUE.equals(existingSegmentTerminatedEarly) &&
segmentTerminatedEarly) {
- rb.rsp
- .getResponseHeader()
-
.remove(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY);
- rb.rsp
- .getResponseHeader()
-
.add(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY, true);
+ updateResponseHeader(
+ rb.rsp.getResponseHeader(),
+ SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY,
+ true);
}
}
if (maxHitsTerminatedEarly) {
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 3c8c2a7a5bd..9d51160bba2 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,10 @@ public class QueryComponent extends SearchComponent {
populateNextCursorMarkFromMergedShards(rb);
if (thereArePartialResults) {
- rb.rsp
- .getResponseHeader()
- .asShallowMap()
- .put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY,
Boolean.TRUE);
+ updateResponseHeader(
+ rb.rsp.getResponseHeader(),
+ SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY,
+ Boolean.TRUE);
}
if (segmentTerminatedEarly != null) {
final Object existingSegmentTerminatedEarly =
@@ -1255,14 +1255,10 @@ public class QueryComponent extends SearchComponent {
segmentTerminatedEarly);
} else if (!Boolean.TRUE.equals(existingSegmentTerminatedEarly)
&& Boolean.TRUE.equals(segmentTerminatedEarly)) {
- rb.rsp
- .getResponseHeader()
-
.remove(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY);
- rb.rsp
- .getResponseHeader()
- .add(
- SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY,
- segmentTerminatedEarly);
+ updateResponseHeader(
+ rb.rsp.getResponseHeader(),
+ SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY,
+ segmentTerminatedEarly);
}
}
if (maxHitsTerminatedEarly) {
@@ -1284,6 +1280,11 @@ public class QueryComponent extends SearchComponent {
}
}
+ @SuppressWarnings("unchecked")
+ protected static void updateResponseHeader(NamedList<Object> header, String
key, Object value) {
+ ((SimpleOrderedMap<Object>) header).put(key, value);
+ }
+
protected void setResultIdsAndResponseDocs(
ResponseBuilder rb,
ShardDocQueue shardDocQueue,
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 963cae66283..c212d5b9359 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
@@ -185,9 +185,7 @@ public class DefaultSchemaSuggester implements
SchemaSuggester {
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 7f57715993c..f72d541a5bf 100644
--- a/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java
+++ b/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java
@@ -27,7 +27,9 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import jakarta.ws.rs.ext.ContextResolver;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
+import java.util.Map;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SimpleOrderedMap;
/** Customizes the ObjectMapper settings used for
serialization/deserialization in Jersey */
@SuppressWarnings("rawtypes")
@@ -48,6 +50,7 @@ public class SolrJacksonMapper implements
ContextResolver<ObjectMapper> {
private static ObjectMapper createObjectMapper() {
final SimpleModule customTypeModule = new SimpleModule();
customTypeModule.addSerializer(new NamedListSerializer(NamedList.class));
+ customTypeModule.addSerializer(new
SimpleOrderedMapSerializer(SimpleOrderedMap.class));
return new ObjectMapper()
// TODO Should failOnUnknown=false be made available on a "permissive"
object mapper instead
@@ -70,7 +73,40 @@ public class SolrJacksonMapper implements
ContextResolver<ObjectMapper> {
@Override
public void serialize(NamedList value, JsonGenerator gen,
SerializerProvider provider)
throws IOException {
- gen.writeObject(value.asShallowMap());
+ // SimpleOrderedMap goes through SimpleOrderedMapSerializer below
instead. asMap(0) avoids
+ // recursing into this same serializer for plain NamedLists.
+ gen.writeObject(value.asMap(0));
+ }
+ }
+
+ /**
+ * Writes a {@link SimpleOrderedMap} out directly via its {@link Map}
entries, without the copy
+ * {@link NamedListSerializer} needs to dodge infinite recursion -- {@link
SimpleOrderedMap} is
+ * already a {@link Map}, so there is nothing to convert.
+ */
+ public static class SimpleOrderedMapSerializer extends
StdSerializer<SimpleOrderedMap> {
+
+ public SimpleOrderedMapSerializer() {
+ this(null);
+ }
+
+ public SimpleOrderedMapSerializer(Class<SimpleOrderedMap> somClazz) {
+ super(somClazz);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void serialize(SimpleOrderedMap value, JsonGenerator gen,
SerializerProvider provider)
+ throws IOException {
+ final Map<String, Object> map = (Map<String, Object>) value;
+ gen.writeStartObject();
+ for (Map.Entry<String, Object> entry : map.entrySet()) {
+ // defaultSerializeField() doesn't honor NON_NULL inclusion itself --
skip nulls here.
+ if (entry.getValue() != null) {
+ provider.defaultSerializeField(entry.getKey(), entry.getValue(),
gen);
+ }
+ }
+ gen.writeEndObject();
}
}
}
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 128538e643b..b8c38274f8d 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
@@ -278,7 +278,7 @@ public class PackageManager implements Closeable {
Map<String, String> packageVersions = new HashMap<>();
// map of package name to multiple values of pluginMeta(Map<String,
String>)
Map<String, Set<PluginMeta>> packagePlugins = new HashMap<>();
- Map<String, Object> result;
+ Object pluginsValue;
try {
NamedList<Object> response =
solrClient.request(
@@ -286,16 +286,16 @@ public class PackageManager implements Closeable {
Integer statusCode = (Integer) response._get(List.of("responseHeader",
"status"), null);
if (statusCode == null || statusCode == ErrorCode.NOT_FOUND.code) {
// Cluster props doesn't exist, that means there are no cluster level
plugins installed.
- result = Map.of();
+ pluginsValue = null;
} else {
- result = response.asShallowMap();
+ pluginsValue = response.get(ContainerPluginsApi.PLUGIN);
}
} catch (SolrServerException | IOException ex) {
throw new SolrException(ErrorCode.SERVER_ERROR, ex);
}
@SuppressWarnings({"unchecked"})
Map<String, Object> clusterPlugins =
- (Map<String, Object>) result.getOrDefault(ContainerPluginsApi.PLUGIN,
Map.of());
+ pluginsValue != null ? (Map<String, Object>) pluginsValue : Map.of();
for (Map.Entry<String, Object> entry : clusterPlugins.entrySet()) {
PluginMeta pluginMeta;
try {
@@ -421,16 +421,14 @@ public class PackageManager implements Closeable {
// 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<Object> 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 = collectionParams.get("params") != null;
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 1323c9eb083..4d4ee4664fe 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 class IndexFingerprint implements MapWriter {
if (o instanceof Map) {
map = (Map<String, Object>) o;
} else if (o instanceof NamedList) {
- map = ((NamedList<Object>) o).asShallowMap();
+ map = new SimpleOrderedMap<>((NamedList<Object>) 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 74457def798..3238dbad7f5 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 enum PivotListEntry {
}
// 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/handler/component/MockResponseBuilder.java
b/solr/core/src/test/org/apache/solr/handler/component/MockResponseBuilder.java
index 241ff703fb5..fa8bca48727 100644
---
a/solr/core/src/test/org/apache/solr/handler/component/MockResponseBuilder.java
+++
b/solr/core/src/test/org/apache/solr/handler/component/MockResponseBuilder.java
@@ -20,7 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.solr.common.params.ShardParams;
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.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.IndexSchema;
@@ -48,7 +48,7 @@ public class MockResponseBuilder extends ResponseBuilder {
SchemaField uniqueIdField = new SchemaField("id", new StrField());
// we need this because QueryComponent adds a property to it.
- NamedList<Object> responseHeader = new NamedList<>();
+ SimpleOrderedMap<Object> responseHeader = new SimpleOrderedMap<>();
// the mock implementations
Mockito.when(request.getSchema()).thenReturn(indexSchema);
diff --git
a/solr/core/src/test/org/apache/solr/jersey/SolrJacksonMapperTest.java
b/solr/core/src/test/org/apache/solr/jersey/SolrJacksonMapperTest.java
new file mode 100644
index 00000000000..a43fa36082f
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/jersey/SolrJacksonMapperTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.jersey;
+
+import static org.hamcrest.Matchers.equalTo;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.junit.Test;
+
+/** Unit tests for {@link SolrJacksonMapper}'s NamedList/SimpleOrderedMap
serialization. */
+public class SolrJacksonMapperTest extends SolrTestCaseJ4 {
+
+ @Test
+ public void testSimpleOrderedMapSerializesDirectlyWithoutRecursing() throws
Exception {
+ final SimpleOrderedMap<Object> top = new SimpleOrderedMap<>();
+ top.add("status", 0);
+
+ final NamedList<Object> nestedPlainNamedList = new NamedList<>();
+ nestedPlainNamedList.add("nestedKey", "nestedVal");
+ top.add("nested_plain_namedlist", nestedPlainNamedList);
+
+ final SimpleOrderedMap<Object> nestedSimpleOrderedMap = new
SimpleOrderedMap<>();
+ nestedSimpleOrderedMap.add("innerKey", 42);
+ top.add("nested_simple_ordered_map", nestedSimpleOrderedMap);
+
+ final ObjectMapper mapper = SolrJacksonMapper.getObjectMapper();
+ final String json = mapper.writeValueAsString(top);
+
+ assertThat(
+ json,
+ equalTo(
+ "{\"status\":0,"
+ + "\"nested_plain_namedlist\":{\"nestedKey\":\"nestedVal\"},"
+ + "\"nested_simple_ordered_map\":{\"innerKey\":42}}"));
+ }
+
+ @Test
+ public void testPlainNamedListStillSerializesViaAsMap() throws Exception {
+ final NamedList<Object> namedList = new NamedList<>();
+ namedList.add("key", "value");
+
+ final ObjectMapper mapper = SolrJacksonMapper.getObjectMapper();
+ final String json = mapper.writeValueAsString(namedList);
+
+ assertThat(json, equalTo("{\"key\":\"value\"}"));
+ }
+
+ @Test
+ public void testSimpleOrderedMapOmitsNullValuesLikeNamedListDoes() throws
Exception {
+ final NamedList<Object> namedListWithNull = new NamedList<>();
+ namedListWithNull.add("present", "val");
+ namedListWithNull.add("absent", null);
+
+ final SimpleOrderedMap<Object> somWithNull = new SimpleOrderedMap<>();
+ somWithNull.add("present", "val");
+ somWithNull.add("absent", null);
+
+ final ObjectMapper mapper = SolrJacksonMapper.getObjectMapper();
+ assertThat(mapper.writeValueAsString(namedListWithNull),
equalTo("{\"present\":\"val\"}"));
+ assertThat(mapper.writeValueAsString(somWithNull),
equalTo("{\"present\":\"val\"}"));
+ }
+}
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 48ccb4a133f..3695c732e50 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.cloud.ZkStateReader;
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 @@ public class TestCloudJSONFacetSKG extends
SolrCloudTestCase {
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 1228ff0c8bd..d0da76dc419 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 class TestUtils extends SolrTestCaseJ4 {
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 dccae8bb319..43dad63e2a1 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.Map;
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 @@ public final class LTRThreadModule implements
NamedListInitializedPlugin {
// 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 f5a250fb2e8..cb499afca8b 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
@@ -683,7 +683,11 @@ public abstract class CloudSolrClient extends SolrClient {
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 6ce10ef381b..5da32870e04 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.CollectionUtil;
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,9 @@ public class JavaBinUpdateRequestCodec {
public void marshal(UpdateRequest updateRequest, OutputStream os) throws
IOException {
NamedList<Object> nl = new NamedList<>();
- NamedList<Object> params = updateRequest.getParams().toNamedList();
+ // Must be SimpleOrderedMap, not a plain NamedList: JavaBinCodec picks the
wire tag
+ // (ORDERED_MAP vs NAMED_LST) from the runtime type, and receivers expect
ORDERED_MAP here.
+ NamedList<Object> 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 7f34859f0a3..e2ca9d835f1 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.AnalyzerDefinition;
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 class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- schemaName = SchemaResponse.getSchemaName(response.asShallowMap());
+ schemaName = SchemaResponse.getSchemaName(new
SimpleOrderedMap<>(response));
}
public String getSchemaName() {
@@ -282,7 +283,7 @@ public class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- schemaVersion = SchemaResponse.getSchemaVersion(response.asShallowMap());
+ schemaVersion = SchemaResponse.getSchemaVersion(new
SimpleOrderedMap<>(response));
}
public float getSchemaVersion() {
@@ -314,7 +315,7 @@ public class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- fields = SchemaResponse.getFields(response.asShallowMap());
+ fields = SchemaResponse.getFields(new SimpleOrderedMap<>(response));
}
public List<Map<String, Object>> getFields() {
@@ -361,7 +362,7 @@ public class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- uniqueKey = SchemaResponse.getSchemaUniqueKey(response.asShallowMap());
+ uniqueKey = SchemaResponse.getSchemaUniqueKey(new
SimpleOrderedMap<>(response));
}
public String getUniqueKey() {
@@ -376,7 +377,7 @@ public class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- similarity = SchemaResponse.getSimilarity(response.asShallowMap());
+ similarity = SchemaResponse.getSimilarity(new
SimpleOrderedMap<>(response));
}
public Map<String, Object> getSimilarity() {
@@ -391,7 +392,7 @@ public class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- copyFields = SchemaResponse.getCopyFields(response.asShallowMap());
+ copyFields = SchemaResponse.getCopyFields(new
SimpleOrderedMap<>(response));
}
public List<Map<String, Object>> getCopyFields() {
@@ -423,7 +424,7 @@ public class SchemaResponse extends SolrResponseBase {
public void setResponse(NamedList<Object> response) {
super.setResponse(response);
- fieldTypes =
SchemaResponse.getFieldTypeRepresentations(response.asShallowMap());
+ fieldTypes = SchemaResponse.getFieldTypeRepresentations(new
SimpleOrderedMap<>(response));
}
public List<FieldTypeRepresentation> 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 9243350766e..7116f4a5db9 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 java.util.stream.StreamSupport;
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 abstract class SolrParams
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<Object> toNamedList() {
- final SimpleOrderedMap<Object> result = new SimpleOrderedMap<>();
-
- for (Iterator<String> 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 f13a71f8723..13afd38e428 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
@@ -28,7 +28,6 @@ import java.util.LinkedHashMap;
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;
@@ -250,10 +249,10 @@ public class NamedList<T>
*
* @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. */
@@ -262,31 +261,6 @@ public class NamedList<T>
return val == null ? def : val;
}
- /**
- * Gets the value for the first instance of the specified name found
starting at the specified
- * index.
- *
- * <p>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
*
@@ -342,112 +316,6 @@ public class NamedList<T>
return new NamedList<>(Collections.unmodifiableList(copy.nvPairs));
}
- /**
- * @deprecated Use {@link SimpleOrderedMap} instead.
- */
- @Deprecated
- public Map<String, T> asShallowMap() {
- return asShallowMap(false);
- }
-
- /**
- * @deprecated use {@link SimpleOrderedMap} instead of NamedList when a Map
is required.
- */
- @Deprecated
- public Map<String, T> 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<String> 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<Entry<String, T>> 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 bb8ca9898e4..94ca36f2b15 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
@@ -853,9 +853,11 @@ public class CloudHttp2SolrClientTest extends
SolrCloudTestCase {
COLLECTION + ":" + (coll.getZNodeVersion() - 1)); // an older
version expect error
QueryResponse rsp = solrClient.query(q);
+ final NamedList<Object> 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 cadc88ed890..57bfffdf057 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 @@ package org.apache.solr.common.util;
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 class NamedListTest extends SolrTestCase {
Object enltest4 = enl._get(List.of("key2"), null);
assertNull(enltest4);
}
-
- @Test
- public void testShallowMap() {
- NamedList<String> nl = new NamedList<>();
- nl.add("key1", "Val1");
- Map<String, String> 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 b91f991251c..f50216f4848 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 class SimpleOrderedMapTest extends SolrTestCase {
assertFalse(map.containsKey("two"));
}
+ /** The MapWriter constructor copies rather than returning a live view. */
+ @Test
+ public void testMapWriterConstructorCopiesRatherThanViewing() {
+ final NamedList<Integer> source = new NamedList<>();
+ source.add("one", 1);
+
+ final SimpleOrderedMap<Integer> 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);