Index: solr/core/src/java/org/apache/solr/cloud/CreateAliasCmd.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- solr/core/src/java/org/apache/solr/cloud/CreateAliasCmd.java (revision 68d16c2a65b4acd0ce1ca543ae53a82e2516f1e5) +++ solr/core/src/java/org/apache/solr/cloud/CreateAliasCmd.java (revision ) @@ -21,6 +21,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.stream.Collectors; import org.apache.solr.cloud.OverseerCollectionMessageHandler.Cmd; import org.apache.solr.common.SolrException; @@ -43,13 +44,14 @@ @Override public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception { - String aliasName = message.getStr(NAME); - String collections = message.getStr("collections"); // could be comma delimited list + final String aliasName = message.getStr(NAME); + final List canonicalCollectionList = parseCollectionsParameter(message.get("collections")); + final String canonicalCollectionsString = StrUtils.join(canonicalCollectionList, ','); ZkStateReader zkStateReader = ocmh.zkStateReader; - validateAllCollectionsExistAndNoDups(collections, zkStateReader); + validateAllCollectionsExistAndNoDups(canonicalCollectionList, zkStateReader); - zkStateReader.aliasesHolder.applyModificationAndExportToZk(aliases -> aliases.cloneWithCollectionAlias(aliasName, collections)); + zkStateReader.aliasesHolder.applyModificationAndExportToZk(aliases -> aliases.cloneWithCollectionAlias(aliasName, canonicalCollectionsString)); // Sleep a bit to allow ZooKeeper state propagation. // @@ -66,20 +68,34 @@ Thread.sleep(100); } - private void validateAllCollectionsExistAndNoDups(String collections, ZkStateReader zkStateReader) { - List collectionArr = StrUtils.splitSmart(collections, ",", true); - if (new HashSet<>(collectionArr).size() != collectionArr.size()) { + private void validateAllCollectionsExistAndNoDups(List collectionList, ZkStateReader zkStateReader) { + final String collectionStr = StrUtils.join(collectionList, ','); + + if (new HashSet<>(collectionList).size() != collectionList.size()) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - String.format(Locale.ROOT, "Can't create collection alias for collections='%s', since it contains duplicates", collections)); + String.format(Locale.ROOT, "Can't create collection alias for collections='%s', since it contains duplicates", collectionStr)); } ClusterState clusterState = zkStateReader.getClusterState(); Set aliasNames = zkStateReader.getAliases().getCollectionAliasListMap().keySet(); - for (String collection : collectionArr) { + for (String collection : collectionList) { if (clusterState.getCollectionOrNull(collection) == null && !aliasNames.contains(collection)) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - String.format(Locale.ROOT, "Can't create collection alias for collections='%s', '%s' is not an existing collection or alias", collections, collection)); + String.format(Locale.ROOT, "Can't create collection alias for collections='%s', '%s' is not an existing collection or alias", collectionStr, collection)); } } } + + /** + * The v2 API directs that the 'collections' parameter be provided as a JSON array (e.g. ["a", "b"]). We also + * maintain support for the legacy format, a comma-separated list (e.g. a,b). + */ + @SuppressWarnings("unchecked") + private List parseCollectionsParameter(Object colls) { + if (colls == null) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "missing collections param"); + if (colls instanceof List) return (List) colls; + return StrUtils.splitSmart(colls.toString(), ",", true).stream() + .map(String::trim) + .collect(Collectors.toList()); + } } Index: solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java (revision 68d16c2a65b4acd0ce1ca543ae53a82e2516f1e5) +++ solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java (revision ) @@ -23,12 +23,14 @@ import java.util.function.UnaryOperator; import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.client.solrj.request.V2Request; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.Aliases; @@ -177,7 +179,6 @@ @Test public void test() throws Exception { - CollectionAdminRequest.createCollection("collection1", "conf", 2, 1).process(cluster.getSolrClient()); CollectionAdminRequest.createCollection("collection2", "conf", 1, 1).process(cluster.getSolrClient()); waitForState("Expected collection1 to be created with 2 shards and 1 replica", "collection1", clusterShape(2, 1)); @@ -240,7 +241,11 @@ //searchSeveralWays("testalias4,testalias5", new SolrQuery("*:*"), 3); /////////////// - CollectionAdminRequest.createAlias("testalias6", "collection2,collection1").process(cluster.getSolrClient()); + // use v2 API + new V2Request.Builder("/collections") + .withMethod(SolrRequest.METHOD.POST) + .withPayload("{\"create-alias\": {\"name\": \"testalias6\", collections:[\"collection2\",\"collection1\"]}}") + .build().process(cluster.getSolrClient()); searchSeveralWays("testalias6", new SolrQuery("*:*"), 6); @@ -328,7 +333,6 @@ } public void testErrorChecks() throws Exception { - CollectionAdminRequest.createCollection("testErrorChecks-collection", "conf", 2, 1).process(cluster.getSolrClient()); waitForState("Expected testErrorChecks-collection to be created with 2 shards and 1 replica", "testErrorChecks-collection", clusterShape(2, 1));