All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.client.solrj.request.CollectionAdminRequest Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * 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.client.solrj.request;

import static org.apache.solr.common.params.CollectionAdminParams.ALIAS;
import static org.apache.solr.common.params.CollectionAdminParams.COUNT_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM;
import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_SHUFFLE_PARAM;
import static org.apache.solr.common.params.CollectionAdminParams.ROUTER_PREFIX;
import static org.apache.solr.common.params.CollectionAdminParams.SKIP_NODE_ASSIGNMENT;
import static org.apache.solr.common.params.CoreAdminParams.BACKUP_REPOSITORY;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.solr.client.solrj.RoutedAliasTypes;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
import org.apache.solr.client.solrj.response.RequestStatusState;
import org.apache.solr.client.solrj.util.SolrIdentifierValidator;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ImplicitDocRouter;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CollectionParams.CollectionAction;
import org.apache.solr.common.params.CommonAdminParams;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.ModifiableSolrParams;
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.StrUtils;

/**
 * This class is experimental and subject to change.
 *
 * @since solr 4.5
 */
public abstract class CollectionAdminRequest
    extends SolrRequest implements MapWriter {

  /** The set of modifiable collection properties */
  public static final java.util.List MODIFIABLE_COLLECTION_PROPERTIES =
      Arrays.asList(
          CollectionAdminParams.REPLICATION_FACTOR,
          CollectionAdminParams.COLL_CONF,
          CollectionAdminParams.PER_REPLICA_STATE,
          CollectionAdminParams.READ_ONLY);

  protected final CollectionAction action;

  @Deprecated public static String PROPERTY_PREFIX = CollectionAdminParams.PROPERTY_PREFIX;

  public CollectionAdminRequest(CollectionAction action) {
    this("/admin/collections", action);
  }

  public CollectionAdminRequest(String path, CollectionAction action) {
    super(METHOD.GET, path);
    this.action = checkNotNull(CoreAdminParams.ACTION, action);
  }

  @Override
  public SolrParams getParams() {
    ModifiableSolrParams params = new ModifiableSolrParams();
    params.set(CoreAdminParams.ACTION, action.toString());
    return params;
  }

  protected void addProperties(ModifiableSolrParams params, Properties props) {
    for (String propertyName : props.stringPropertyNames()) {
      params.set(PROPERTY_PREFIX + propertyName, props.getProperty(propertyName));
    }
  }

  @Override
  public void writeMap(EntryWriter ew) throws IOException {
    ew.put("class", this.getClass().getName());
    ew.put("method", getMethod().toString());
    SolrParams params = getParams();
    if (params != null) {
      for (Iterator it = params.getParameterNamesIterator(); it.hasNext(); ) {
        final String name = it.next();
        final String[] values = params.getParams(name);
        for (String value : values) {
          ew.put("params." + name, value);
        }
      }
    }
  }

  @Override
  public String toString() {
    return jsonStr();
  }

  @Override
  public String getRequestType() {
    return SolrRequestType.ADMIN.toString();
  }

  /**
   * Take the request specific basic auth creds on this admin request and propagate them to a
   * related request if does not already have credentials set, such as a
   * CollectionAdminRequest.RequestStatus when doing async requests.
   */
  protected >
      T propagateBasicAuthCreds(T req) {
    String user = getBasicAuthUser();
    String pass = getBasicAuthPassword();
    if (user != null && pass != null && req.getBasicAuthUser() == null) {
      req.setBasicAuthCredentials(user, pass);
    }
    return req;
  }

  /** Base class for asynchronous collection admin requests */
  public abstract static class AsyncCollectionAdminRequest
      extends CollectionAdminRequest {

    protected String asyncId = null;
    protected boolean waitForFinalState = false;

    public AsyncCollectionAdminRequest(CollectionAction action) {
      super(action);
    }

    @Override
    protected CollectionAdminResponse createResponse(SolrClient client) {
      return new CollectionAdminResponse();
    }

    private static String generateAsyncId() {
      return UUID.randomUUID().toString();
    }

    public String getAsyncId() {
      return asyncId;
    }

    public void setWaitForFinalState(boolean waitForFinalState) {
      this.waitForFinalState = waitForFinalState;
    }

    public void setAsyncId(String asyncId) {
      this.asyncId = asyncId;
    }

    /**
     * Process this request asynchronously, generating and returning a request id
     *
     * @param client a Solr client
     * @return the request id
     * @see CollectionAdminRequest.RequestStatus
     */
    public String processAsync(SolrClient client) throws IOException, SolrServerException {
      return processAsync(generateAsyncId(), client);
    }

    /**
     * Process this request asynchronously, using a specified request id
     *
     * @param asyncId the request id
     * @param client a Solr client
     * @return the request id
     */
    public String processAsync(String asyncId, SolrClient client)
        throws IOException, SolrServerException {
      this.asyncId = asyncId;
      NamedList resp = client.request(this);
      if (resp.get("error") != null) {
        throw new SolrServerException((String) resp.get("error"));
      }
      return (String) resp.get("requestid");
    }

    /**
     * Send this request to a Solr server, and wait (up to a timeout) for the request to complete or
     * fail
     *
     * @param client a Solr client
     * @param timeoutSeconds the maximum time to wait
     * @return the status of the request on completion or timeout
     */
    public RequestStatusState processAndWait(SolrClient client, long timeoutSeconds)
        throws SolrServerException, InterruptedException, IOException {
      return processAndWait(generateAsyncId(), client, timeoutSeconds);
    }

    /**
     * Send this request to a Solr server, and wait (up to a timeout) for the request to complete or
     * fail
     *
     * @param asyncId an id for the request
     * @param client a Solr client
     * @param timeoutSeconds the maximum time to wait
     * @return the status of the request on completion or timeout
     */
    public RequestStatusState processAndWait(String asyncId, SolrClient client, long timeoutSeconds)
        throws IOException, SolrServerException, InterruptedException {
      // This is kind of slow, see SOLR-16313
      processAsync(asyncId, client);
      return propagateBasicAuthCreds(requestStatus(asyncId)).waitFor(client, timeoutSeconds);
    }

    @Override
    public SolrParams getParams() {
      ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
      if (asyncId != null) {
        params.set(CommonAdminParams.ASYNC, asyncId);
      }
      if (waitForFinalState) {
        params.set(CommonAdminParams.WAIT_FOR_FINAL_STATE, waitForFinalState);
      }
      return params;
    }
  }

  protected abstract static class AsyncCollectionSpecificAdminRequest
      extends AsyncCollectionAdminRequest {

    protected String collection;
    protected Boolean followAliases;

    public AsyncCollectionSpecificAdminRequest(CollectionAction action, String collection) {
      super(action);
      this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection);
    }

    public String getCollectionName() {
      return collection;
    }

    public void setFollowAliases(Boolean followAliases) {
      this.followAliases = followAliases;
    }

    @Override
    public SolrParams getParams() {
      ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
      params.set(CoreAdminParams.NAME, collection);
      params.setNonNull(CollectionAdminParams.FOLLOW_ALIASES, followAliases);
      return params;
    }
  }

  protected abstract static class AsyncShardSpecificAdminRequest
      extends AsyncCollectionAdminRequest {

    protected String collection;
    protected String shard;

    public AsyncShardSpecificAdminRequest(
        CollectionAction action, String collection, String shard) {
      super(action);
      this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection);
      this.shard = checkNotNull(CoreAdminParams.SHARD, shard);
    }

    @Override
    public SolrParams getParams() {
      ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
      params.set(CoreAdminParams.COLLECTION, collection);
      params.set(CoreAdminParams.SHARD, shard);
      return params;
    }
  }

  protected abstract static class ShardSpecificAdminRequest
      extends CollectionAdminRequest {

    protected String collection;
    protected String shard;

    public ShardSpecificAdminRequest(CollectionAction action, String collection, String shard) {
      super(action);
      this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection);
      this.shard = checkNotNull(CoreAdminParams.SHARD, shard);
    }

    @Override
    public SolrParams getParams() {
      ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
      params.set(CoreAdminParams.COLLECTION, collection);
      params.set(CoreAdminParams.SHARD, shard);
      return params;
    }

    @Override
    protected CollectionAdminResponse createResponse(SolrClient client) {
      return new CollectionAdminResponse();
    }
  }

  // ---------------------------------------------------------------------------------------
  //
  // ---------------------------------------------------------------------------------------

  protected abstract static class CollectionAdminRoleRequest extends AsyncCollectionAdminRequest {

    protected String node;
    protected String role;

    public CollectionAdminRoleRequest(CollectionAction action, String node, String role) {
      super(action);
      this.role = checkNotNull(CollectionAdminParams.ROLE, role);
      this.node = checkNotNull(CoreAdminParams.NODE, node);
    }

    public String getNode() {
      return this.node;
    }

    public String getRole() {
      return this.role;
    }

    @Override
    public SolrParams getParams() {
      ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
      params.set(CollectionAdminParams.ROLE, this.role);
      params.set(CoreAdminParams.NODE, this.node);
      return params;
    }
  }

  /** Specific Collection API call implementations * */

  /**
   * Returns a SolrRequest for creating a collection
   *
   * @param collection the collection name
   * @param config the collection config
   * @param numShards the number of shards in the collection
   * @param numNrtReplicas the number of {@link org.apache.solr.common.cloud.Replica.Type#NRT}
   *     replicas
   * @param numTlogReplicas the number of {@link org.apache.solr.common.cloud.Replica.Type#TLOG}
   *     replicas
   * @param numPullReplicas the number of {@link org.apache.solr.common.cloud.Replica.Type#PULL}
   *     replicas
   */
  public static Create createCollection(
      String collection,
      String config,
      Integer numShards,
      Integer numNrtReplicas,
      Integer numTlogReplicas,
      Integer numPullReplicas) {
    return new Create(
        collection, config, numShards, numNrtReplicas, numTlogReplicas, numPullReplicas);
  }

  /**
   * Returns a SolrRequest for creating a collection
   *
   * @param collection the collection name
   * @param config the collection config
   * @param numShards the number of shards in the collection
   * @param numReplicas the replication factor of the collection (same as numNrtReplicas)
   */
  public static Create createCollection(
      String collection, String config, int numShards, int numReplicas) {
    return new Create(collection, config, numShards, numReplicas, null, null);
  }

  /**
   * Returns a SolrRequest for creating a collection using a default configSet
   *
   * 

This requires that there is either a single configset configured in the cluster, or that * there is a configset with the same name as the collection * * @param collection the collection name * @param numShards the number of shards in the collection * @param numReplicas the replication factor of the collection */ public static Create createCollection(String collection, int numShards, int numReplicas) { return new Create(collection, null, numShards, numReplicas, 0, 0); } /** * Returns a SolrRequest for creating a collection with the implicit router * * @param collection the collection name * @param config the collection config * @param shards a shard definition string * @param numReplicas the replication factor of the collection */ public static Create createCollectionWithImplicitRouter( String collection, String config, String shards, int numReplicas) { return new Create(collection, config, shards, numReplicas); } /** * Returns a SolrRequest for creating a collection with the implicit router and specific types of * replicas * * @param collection the collection name * @param config the collection config * @param shards a shard definition string * @param numNrtReplicas the number of replicas of type {@link * org.apache.solr.common.cloud.Replica.Type#NRT} * @param numTlogReplicas the number of replicas of type {@link * org.apache.solr.common.cloud.Replica.Type#TLOG} * @param numPullReplicas the number of replicas of type {@link * org.apache.solr.common.cloud.Replica.Type#PULL} */ public static Create createCollectionWithImplicitRouter( String collection, String config, String shards, int numNrtReplicas, int numTlogReplicas, int numPullReplicas) { return new Create( collection, config, ImplicitDocRouter.NAME, null, checkNotNull("shards", shards), numNrtReplicas, numTlogReplicas, numPullReplicas); } /** * Returns a SolrRequest for modifying a collection with the given properties * * @param collection the collection name * @param properties a map of key and values with which the collection is to be modified */ public static Modify modifyCollection(String collection, Map properties) { return new Modify(collection, properties); } // CREATE request public static class Create extends AsyncCollectionSpecificAdminRequest { protected String configName = null; protected String createNodeSet = null; protected String routerName; protected String policy; protected String shards; protected String routerField; protected Integer numShards; protected Integer nrtReplicas; protected Integer pullReplicas; protected Integer tlogReplicas; protected Boolean perReplicaState; protected Properties properties; protected String alias; protected String[] rule, snitch; /** Constructor intended for typical use cases */ protected Create( String collection, String config, Integer numShards, Integer numNrtReplicas, Integer numTlogReplicas, Integer numPullReplicas) { // TODO: maybe add other constructors this( collection, config, null, numShards, null, numNrtReplicas, numTlogReplicas, numPullReplicas); } /** * Constructor that assumes {@link ImplicitDocRouter#NAME} and an explicit list of shards * */ protected Create(String collection, String config, String shards, int numNrtReplicas) { this( collection, config, ImplicitDocRouter.NAME, null, checkNotNull("shards", shards), numNrtReplicas, null, null); } private Create( String collection, String config, String routerName, Integer numShards, String shards, Integer numNrtReplicas, Integer numTlogReplicas, Integer numPullReplicas) { super(CollectionAction.CREATE, SolrIdentifierValidator.validateCollectionName(collection)); // NOTE: there's very little we can assert about the args because nothing but "collection" is // required by the server if ((null != shards) && (null != numShards)) { throw new IllegalArgumentException("Can not specify both a numShards and a list of shards"); } this.configName = config; this.routerName = routerName; this.numShards = numShards; this.setShards(shards); this.nrtReplicas = numNrtReplicas; this.tlogReplicas = numTlogReplicas; this.pullReplicas = numPullReplicas; } public Create setCreateNodeSet(String nodeSet) { this.createNodeSet = nodeSet; return this; } public Create setRouterName(String routerName) { this.routerName = routerName; return this; } public Create setRouterField(String routerField) { this.routerField = routerField; return this; } public Create setNrtReplicas(Integer nrtReplicas) { this.nrtReplicas = nrtReplicas; return this; } public Create setTlogReplicas(Integer tlogReplicas) { this.tlogReplicas = tlogReplicas; return this; } public Create setPullReplicas(Integer pullReplicas) { this.pullReplicas = pullReplicas; return this; } public Create setReplicationFactor(Integer repl) { this.nrtReplicas = repl; return this; } public Create setRule(String... s) { this.rule = s; return this; } public Create setSnitch(String... s) { this.snitch = s; return this; } public Create setPerReplicaState(Boolean b) { this.perReplicaState = b; return this; } public Create setAlias(String alias) { this.alias = alias; return this; } public String getConfigName() { return configName; } public String getCreateNodeSet() { return createNodeSet; } public String getRouterName() { return routerName; } public String getShards() { return shards; } public Integer getNumShards() { return numShards; } public Integer getReplicationFactor() { return getNumNrtReplicas(); } public Integer getNumNrtReplicas() { return nrtReplicas; } public Integer getNumTlogReplicas() { return tlogReplicas; } public Integer getNumPullReplicas() { return pullReplicas; } public Boolean getPerReplicaState() { return perReplicaState; } /** * Provide the name of the shards to be created, separated by commas * *

Shard names must consist entirely of periods, underscores, hyphens, and alphanumerics. * Other characters are not allowed. * * @throws IllegalArgumentException if any of the shard names contain invalid characters. */ public Create setShards(String shards) { if (null != shards) { for (String shard : shards.split(",")) { SolrIdentifierValidator.validateShardName(shard); } } this.shards = shards; return this; } public Properties getProperties() { return properties; } public Create setProperties(Properties properties) { this.properties = properties; return this; } public Create setProperties(Map properties) { this.properties = new Properties(); this.properties.putAll(properties); return this; } public Create withProperty(String key, String value) { if (this.properties == null) this.properties = new Properties(); this.properties.setProperty(key, value); return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); if (configName != null) params.set("collection.configName", configName); if (createNodeSet != null) params.set(CREATE_NODE_SET_PARAM, createNodeSet); if (numShards != null) { params.set(CollectionAdminParams.NUM_SHARDS, numShards); } if (routerName != null) params.set("router.name", routerName); if (shards != null) params.set("shards", shards); if (routerField != null) { params.set("router.field", routerField); } if (nrtReplicas != null) { params.set(CollectionAdminParams.NRT_REPLICAS, nrtReplicas); } if (properties != null) { addProperties(params, properties); } if (pullReplicas != null) { params.set(CollectionAdminParams.PULL_REPLICAS, pullReplicas); } if (tlogReplicas != null) { params.set(CollectionAdminParams.TLOG_REPLICAS, tlogReplicas); } if (Boolean.TRUE.equals(perReplicaState)) { params.set(CollectionAdminParams.PER_REPLICA_STATE, perReplicaState); } params.setNonNull(ALIAS, alias); return params; } public Create setPolicy(String policy) { this.policy = policy; return this; } } /** Returns a SolrRequest to reload a collection */ public static Reload reloadCollection(String collection) { return new Reload(collection); } // RELOAD request public static class Reload extends AsyncCollectionSpecificAdminRequest { private Reload(String collection) { super(CollectionAction.RELOAD, collection); } } public static Rename renameCollection(String collection, String target) { return new Rename(collection, target); } public static class Rename extends AsyncCollectionSpecificAdminRequest { String target; public Rename(String collection, String target) { super(CollectionAction.RENAME, collection); this.target = target; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CollectionAdminParams.TARGET, target); return params; } } /** Returns a SolrRequest to delete a node. */ public static DeleteNode deleteNode(String node) { return new DeleteNode(node); } public static class DeleteNode extends AsyncCollectionAdminRequest { String node; /** * @param node The node to be deleted */ public DeleteNode(String node) { super(CollectionAction.DELETENODE); this.node = checkNotNull("node", node); } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.NODE, node); return params; } } public static class ReplaceNode extends AsyncCollectionAdminRequest { String sourceNode, targetNode; Boolean parallel; /** * @param source node to be cleaned up * @param target node where the new replicas are to be created */ public ReplaceNode(String source, String target) { super(CollectionAction.REPLACENODE); this.sourceNode = checkNotNull(CollectionParams.SOURCE_NODE, source); this.targetNode = target; } public ReplaceNode setParallel(Boolean flag) { this.parallel = flag; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CollectionParams.SOURCE_NODE, sourceNode); if (StrUtils.isNotNullOrEmpty(targetNode)) { params.set(CollectionParams.TARGET_NODE, targetNode); } if (parallel != null) params.set("parallel", parallel.toString()); return params; } } public static MoveReplica moveReplica(String collection, String replica, String targetNode) { return new MoveReplica(collection, replica, targetNode); } public static class MoveReplica extends AsyncCollectionAdminRequest { protected String collection, replica, targetNode; protected String shard, sourceNode; protected boolean randomlyMoveReplica; protected boolean inPlaceMove = true; protected int timeout = -1; public MoveReplica(String collection, String replica, String targetNode) { super(CollectionAction.MOVEREPLICA); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); this.replica = checkNotNull(CoreAdminParams.REPLICA, replica); this.targetNode = checkNotNull(CollectionParams.TARGET_NODE, targetNode); this.randomlyMoveReplica = false; } public MoveReplica(String collection, String shard, String sourceNode, String targetNode) { super(CollectionAction.MOVEREPLICA); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); this.shard = checkNotNull(CoreAdminParams.SHARD, shard); this.sourceNode = checkNotNull(CollectionParams.SOURCE_NODE, sourceNode); this.targetNode = checkNotNull(CollectionParams.TARGET_NODE, targetNode); this.randomlyMoveReplica = true; } public void setInPlaceMove(boolean inPlaceMove) { this.inPlaceMove = inPlaceMove; } public void setTimeout(int timeout) { this.timeout = timeout; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.COLLECTION, collection); params.set(CollectionParams.TARGET_NODE, targetNode); params.set(CommonAdminParams.IN_PLACE_MOVE, inPlaceMove); if (timeout != -1) { params.set(CommonAdminParams.TIMEOUT, timeout); } if (randomlyMoveReplica) { params.set(CoreAdminParams.SHARD, shard); params.set(CollectionParams.SOURCE_NODE, sourceNode); } else { params.set(CoreAdminParams.REPLICA, replica); } return params; } } /* * Returns a RebalanceLeaders object to rebalance leaders for a collection */ public static RebalanceLeaders rebalanceLeaders(String collection) { return new RebalanceLeaders(collection); } public static class RebalanceLeaders extends AsyncCollectionAdminRequest { protected Integer maxAtOnce; protected Integer maxWaitSeconds; protected String collection; public RebalanceLeaders setMaxAtOnce(Integer maxAtOnce) { this.maxAtOnce = maxAtOnce; return this; } public RebalanceLeaders setMaxWaitSeconds(Integer maxWaitSeconds) { this.maxWaitSeconds = maxWaitSeconds; return this; } public Integer getMaxAtOnce() { return maxAtOnce; } public Integer getMaxWaitSeconds() { return maxWaitSeconds; } public RebalanceLeaders(String collection) { super(CollectionAction.REBALANCELEADERS); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.COLLECTION, collection); if (this.maxWaitSeconds != null) { params.set("maxWaitSeconds", this.maxWaitSeconds); } if (this.maxAtOnce != null) { params.set("maxAtOnce", this.maxAtOnce); } return params; } } /** Returns a SolrRequest to reindex a collection */ public static ReindexCollection reindexCollection(String collection) { return new ReindexCollection(collection); } public static class ReindexCollection extends AsyncCollectionSpecificAdminRequest { // not moving to CollectionAdminParams because there is COLL_CONF there already; confusing private static final String CONFIGNAME_PARAM = "configName"; String target; String query; String fields; String configName; Boolean removeSource; String cmd; Integer batchSize; Map collectionParams = new HashMap<>(); private ReindexCollection(String collection) { super(CollectionAction.REINDEXCOLLECTION, collection); } /** Target collection name (null if the same). */ public ReindexCollection setTarget(String target) { this.target = target; return this; } /** Set optional command (eg. abort, status). */ public ReindexCollection setCommand(String command) { this.cmd = command; return this; } /** Query matching the documents to reindex (default is '*:*'). */ public ReindexCollection setQuery(String query) { this.query = query; return this; } /** Fields to reindex (the same syntax as {@link CommonParams#FL}), default is '*'. */ public ReindexCollection setFields(String fields) { this.fields = fields; return this; } /** Remove source collection after success. Default is false. */ public ReindexCollection setRemoveSource(boolean removeSource) { this.removeSource = removeSource; return this; } /** Copy documents in batches of this size. Default is 100. */ public ReindexCollection setBatchSize(int batchSize) { this.batchSize = batchSize; return this; } /** Config name for the target collection. Default is the same as source. */ public ReindexCollection setConfigName(String configName) { this.configName = configName; return this; } /** Set other supported collection CREATE parameters. */ public ReindexCollection setCollectionParam(String key, Object value) { this.collectionParams.put(key, value); return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.setNonNull("target", target); params.setNonNull("cmd", cmd); params.setNonNull(CONFIGNAME_PARAM, configName); params.setNonNull(CommonParams.Q, query); params.setNonNull(CommonParams.FL, fields); params.setNonNull("removeSource", removeSource); params.setNonNull(CommonParams.ROWS, batchSize); collectionParams.forEach((k, v) -> params.setNonNull(k, v)); return params; } } /** * Return a SolrRequest for low-level detailed status of the specified collection. * * @param collection the collection to get the status of. */ public static ColStatus collectionStatus(String collection) { checkNotNull(CoreAdminParams.COLLECTION, collection); return new ColStatus(collection); } /** Return a SolrRequest for low-level detailed status of all collections on the cluster. */ public static ColStatus collectionStatuses() { return new ColStatus(); } public static class ColStatus extends AsyncCollectionAdminRequest { protected String collection = null; protected Boolean withSegments = null; protected Boolean withFieldInfo = null; protected Boolean withCoreInfo = null; protected Boolean withSizeInfo = null; protected Boolean withRawSizeInfo = null; protected Boolean withRawSizeSummary = null; protected Boolean withRawSizeDetails = null; protected Float rawSizeSamplingPercent = null; private ColStatus(String collection) { super(CollectionAction.COLSTATUS); this.collection = collection; } private ColStatus() { super(CollectionAction.COLSTATUS); } public ColStatus setWithSegments(boolean withSegments) { this.withSegments = withSegments; return this; } public ColStatus setWithFieldInfo(boolean withFieldInfo) { this.withFieldInfo = withFieldInfo; return this; } public ColStatus setWithCoreInfo(boolean withCoreInfo) { this.withCoreInfo = withCoreInfo; return this; } public ColStatus setWithSizeInfo(boolean withSizeInfo) { this.withSizeInfo = withSizeInfo; return this; } public ColStatus setWithRawSizeInfo(boolean withRawSizeInfo) { this.withRawSizeInfo = withRawSizeInfo; return this; } public ColStatus setWithRawSizeSummary(boolean withRawSizeSummary) { this.withRawSizeSummary = withRawSizeSummary; return this; } public ColStatus setWithRawSizeDetails(boolean withRawSizeDetails) { this.withRawSizeDetails = withRawSizeDetails; return this; } public ColStatus setRawSizeSamplingPercent(float rawSizeSamplingPercent) { this.rawSizeSamplingPercent = rawSizeSamplingPercent; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.setNonNull(CoreAdminParams.COLLECTION, collection); params.setNonNull("segments", withSegments); params.setNonNull("fieldInfo", withFieldInfo); params.setNonNull("coreInfo", withCoreInfo); params.setNonNull("sizeInfo", withSizeInfo); params.setNonNull("rawSizeInfo", withRawSizeInfo); params.setNonNull("rawSizeSummary", withRawSizeSummary); params.setNonNull("rawSizeDetails", withRawSizeDetails); params.setNonNull("rawSizeSamplingPercent", rawSizeSamplingPercent); return params; } } /** Returns a SolrRequest to delete a collection */ public static Delete deleteCollection(String collection) { return new Delete(collection); } // DELETE request public static class Delete extends AsyncCollectionSpecificAdminRequest { private Delete(String collection) { super(CollectionAction.DELETE, collection); } } public static Backup backupCollection(String collection, String backupName) { return new Backup(collection, backupName); } // BACKUP request public static class Backup extends AsyncCollectionSpecificAdminRequest { protected final String name; protected Optional repositoryName = Optional.empty(); protected String location; protected Optional commitName = Optional.empty(); protected Optional indexBackupStrategy = Optional.empty(); protected boolean incremental = true; protected Optional maxNumBackupPoints = Optional.empty(); protected boolean backupConfigset = true; protected Properties extraProperties; public Backup(String collection, String name) { super(CollectionAction.BACKUP, collection); this.name = name; this.repositoryName = Optional.empty(); } public String getLocation() { return location; } public Backup setLocation(String location) { this.location = location; return this; } public Optional getRepositoryName() { return repositoryName; } public Backup setRepositoryName(String repositoryName) { this.repositoryName = Optional.ofNullable(repositoryName); return this; } public Optional getCommitName() { return commitName; } public Backup setCommitName(String commitName) { this.commitName = Optional.ofNullable(commitName); return this; } public Optional getIndexBackupStrategy() { return indexBackupStrategy; } public Backup setIndexBackupStrategy(String indexBackupStrategy) { this.indexBackupStrategy = Optional.ofNullable(indexBackupStrategy); return this; } /** * Specifies the backup method to use: the deprecated 'full-snapshot' format, or the current * 'incremental' format. * *

Defaults to 'true' if unspecified. * *

Incremental backups are almost always preferable to the deprecated 'full-snapshot' format, * as incremental backups can take advantage of previously backed-up files and will only upload * those that aren't already stored in the repository - saving lots of time and network * bandwidth. The older 'full-snapshot' format should only be used by experts with a particular * reason to do so. * * @param incremental true to use incremental backups, false otherwise. */ @Deprecated public Backup setIncremental(boolean incremental) { this.incremental = incremental; return this; } /** * Specifies the maximum number of backup points to keep at the backup location. * *

If the current backup causes the number of stored backup points to exceed this value, the * oldest backup points are cleaned up so that only {@code #maxNumBackupPoints} are retained. * *

This parameter is ignored if the request uses a non-incremental backup. * * @param maxNumBackupPoints the number of backup points to retain after the current backup */ public Backup setMaxNumberBackupPoints(int maxNumBackupPoints) { this.maxNumBackupPoints = Optional.of(maxNumBackupPoints); return this; } /** * Indicates weather the Backup operation should also backup the configset files (such as * schema.xml, solrconfig.xml. etc). * *

Note that the configset is needed to restore the collection, so only set this to false if * you know the cluster will have the configset available before restoring. * * @param backupConfigset {@code true} if you want to backup configsets (default). {@code false} * to skip configset files. * @return {@code this} builder */ public Backup setBackupConfigset(boolean backupConfigset) { this.backupConfigset = backupConfigset; return this; } public Backup setExtraProperties(Properties extraProperties) { this.extraProperties = extraProperties; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); if (extraProperties != null) { addProperties(params, extraProperties); } params.set(CoreAdminParams.COLLECTION, collection); params.set(CoreAdminParams.NAME, name); params.set(CoreAdminParams.BACKUP_LOCATION, location); // note: optional if (repositoryName.isPresent()) { params.set(BACKUP_REPOSITORY, repositoryName.get()); } if (commitName.isPresent()) { params.set(CoreAdminParams.COMMIT_NAME, commitName.get()); } if (indexBackupStrategy.isPresent()) { params.set(CollectionAdminParams.INDEX_BACKUP_STRATEGY, indexBackupStrategy.get()); } if (maxNumBackupPoints.isPresent()) { params.set(CoreAdminParams.MAX_NUM_BACKUP_POINTS, maxNumBackupPoints.get()); } params.set(CoreAdminParams.BACKUP_INCREMENTAL, incremental); params.set(CoreAdminParams.BACKUP_CONFIGSET, backupConfigset); return params; } } public static Restore restoreCollection(String collection, String backupName) { return new Restore(collection, backupName); } // RESTORE request public static class Restore extends AsyncCollectionSpecificAdminRequest { protected final String backupName; protected Optional repositoryName = Optional.empty(); protected String location; // in common with collection creation: protected String configName; protected Integer replicationFactor; protected Integer nrtReplicas; protected Integer tlogReplicas; protected Integer pullReplicas; protected Optional createNodeSet = Optional.empty(); protected Optional createNodeSetShuffle = Optional.empty(); protected Properties properties; protected Integer backupId; public Restore(String collection, String backupName) { super(CollectionAction.RESTORE, collection); this.backupName = backupName; } public String getLocation() { return location; } public Restore setLocation(String location) { this.location = location; return this; } public Optional getRepositoryName() { return repositoryName; } public Restore setRepositoryName(String repositoryName) { this.repositoryName = Optional.ofNullable(repositoryName); return this; } public void setCreateNodeSet(String createNodeSet) { this.createNodeSet = Optional.of(createNodeSet); } public Optional getCreateNodeSet() { return createNodeSet; } public Optional getCreateNodeSetShuffle() { return createNodeSetShuffle; } public void setCreateNodeSetShuffle(boolean createNodeSetShuffle) { this.createNodeSetShuffle = Optional.of(createNodeSetShuffle); } // Collection creation params in common: public Restore setConfigName(String config) { this.configName = config; return this; } public String getConfigName() { return configName; } public Integer getReplicationFactor() { return replicationFactor; } public Restore setReplicationFactor(Integer replicationFactor) { this.replicationFactor = replicationFactor; return this; } public Integer getNrtReplicas() { return nrtReplicas; } public Restore setNrtReplicas(Integer nrtReplicas) { this.nrtReplicas = nrtReplicas; return this; } ; public Integer getTlogReplicas() { return tlogReplicas; } public Restore setTlogReplicas(Integer tlogReplicas) { this.tlogReplicas = tlogReplicas; return this; } public Integer getPullReplicas() { return pullReplicas; } public Restore setPullReplicas(Integer pullReplicas) { this.pullReplicas = pullReplicas; return this; } public Properties getProperties() { return properties; } public Restore setProperties(Properties properties) { this.properties = properties; return this; } /** * Specify the ID of the backup-point to restore from. * *

'-1' is used by default to have Solr restore from the most recent backup-point. * *

Solr can store multiple backup points for a given collection - each identified by a unique * backup ID. Users who want to restore a particular backup-point can specify it using this * method. * * @param backupId the ID of the backup-point to restore from */ public Restore setBackupId(int backupId) { this.backupId = backupId; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.COLLECTION, collection); params.set(CoreAdminParams.NAME, backupName); params.set(CoreAdminParams.BACKUP_LOCATION, location); // note: optional params.set("collection.configName", configName); // note: optional if (replicationFactor != null && nrtReplicas != null) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Cannot set both replicationFactor and nrtReplicas as they mean the same thing"); } if (replicationFactor != null) { params.set(CollectionAdminParams.REPLICATION_FACTOR, replicationFactor); } if (nrtReplicas != null) { params.set(CollectionAdminParams.NRT_REPLICAS, nrtReplicas); } if (pullReplicas != null) { params.set(CollectionAdminParams.PULL_REPLICAS, pullReplicas); } if (tlogReplicas != null) { params.set(CollectionAdminParams.TLOG_REPLICAS, tlogReplicas); } if (properties != null) { addProperties(params, properties); } if (repositoryName.isPresent()) { params.set(BACKUP_REPOSITORY, repositoryName.get()); } if (createNodeSet.isPresent()) { params.set(CREATE_NODE_SET_PARAM, createNodeSet.get()); } if (createNodeSetShuffle.isPresent()) { params.set(CREATE_NODE_SET_SHUFFLE_PARAM, createNodeSetShuffle.get()); } if (backupId != null) { params.set(CoreAdminParams.BACKUP_ID, backupId); } return params; } } /** * Install index data to a specific shard of a specific collection * * @param collection the collection to install data to * @param shard the specific shard within to install data to * @param location a URI-string pointing to location of the index data within a particular backup * repository * @param backupRepository the backup repository to lookup and install the index data from */ public static InstallShard installDataToShard( String collection, String shard, String location, String backupRepository) { return new InstallShard(collection, shard, location, backupRepository); } /** * Install index data to a specific shard of a specific collection * *

Will use Solr's "default" backup repository for locating and accessing the index data. * * @param collection the collection to install data to * @param shard the specific shard within to install data to * @param location a URI-string pointing to location of the index data within a particular backup * repository */ public static InstallShard installDataToShard(String collection, String shard, String location) { return new InstallShard(collection, shard, location, null); } public static class InstallShard extends AsyncShardSpecificAdminRequest { protected String repositoryName; protected String location; public InstallShard(String collection, String shard, String location, String backupRepository) { super(CollectionAction.INSTALLSHARDDATA, collection, shard); this.repositoryName = backupRepository; this.location = location; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.setNonNull(CoreAdminParams.BACKUP_REPOSITORY, repositoryName); params.setNonNull(CoreAdminParams.BACKUP_LOCATION, location); return params; } } // Note : This method is added since solrj module does not use Google // guava library. Also changes committed for SOLR-8765 result in wrong // error message when "collection" parameter is specified as Null. // This is because the setCollectionName method is deprecated. static T checkNotNull(String param, T value) { if (value == null) { throw new NullPointerException("Please specify a non-null value for parameter " + param); } return value; } @SuppressWarnings("serial") public static class CreateSnapshot extends AsyncCollectionSpecificAdminRequest { protected final String commitName; public CreateSnapshot(String collection, String commitName) { super(CollectionAction.CREATESNAPSHOT, checkNotNull(CoreAdminParams.COLLECTION, collection)); this.commitName = checkNotNull(CoreAdminParams.COMMIT_NAME, commitName); } @Override public String getCollectionName() { return collection; } public String getCommitName() { return commitName; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.COLLECTION, collection); params.set(CoreAdminParams.COMMIT_NAME, commitName); return params; } } @SuppressWarnings("serial") public static class DeleteSnapshot extends AsyncCollectionSpecificAdminRequest { protected final String commitName; public DeleteSnapshot(String collection, String commitName) { super(CollectionAction.DELETESNAPSHOT, checkNotNull(CoreAdminParams.COLLECTION, collection)); this.commitName = checkNotNull(CoreAdminParams.COMMIT_NAME, commitName); } @Override public String getCollectionName() { return collection; } public String getCommitName() { return commitName; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.COLLECTION, collection); params.set(CoreAdminParams.COMMIT_NAME, commitName); return params; } } @SuppressWarnings("serial") public static class ListSnapshots extends AsyncCollectionSpecificAdminRequest { public ListSnapshots(String collection) { super(CollectionAction.LISTSNAPSHOTS, checkNotNull(CoreAdminParams.COLLECTION, collection)); } @Override public String getCollectionName() { return collection; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.COLLECTION, collection); return params; } } /** Returns a SolrRequest to create a new shard in a collection */ public static CreateShard createShard(String collection, String shard) { return new CreateShard(collection, shard); } // CREATESHARD request public static class CreateShard extends AsyncShardSpecificAdminRequest { protected String nodeSet; protected Properties properties; public CreateShard setNodeSet(String nodeSet) { this.nodeSet = nodeSet; return this; } public String getNodeSet() { return nodeSet; } public Properties getProperties() { return properties; } public CreateShard setProperties(Properties properties) { this.properties = properties; return this; } private CreateShard(String collection, String shard) { super( CollectionAction.CREATESHARD, collection, SolrIdentifierValidator.validateShardName(shard)); } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); if (nodeSet != null) { params.set(CREATE_NODE_SET_PARAM, nodeSet); } if (properties != null) { addProperties(params, properties); } return params; } } /** Returns a SolrRequest to run a mock task. For tests only. */ public static MockCollTask mockCollTask(String collection) { return new MockCollTask(collection); } // MOCK_COLL_TASK request public static class MockCollTask extends AsyncCollectionAdminRequest { protected final String collection; protected String sleep; private MockCollTask(String collection) { super(CollectionAction.MOCK_COLL_TASK); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); } public MockCollTask setSleep(String sleep) { this.sleep = sleep; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.add(CoreAdminParams.COLLECTION, collection); params.setNonNull("sleep", sleep); return params; } } /** Returns a SolrRequest to split a shard in a collection */ public static SplitShard splitShard(String collection) { return new SplitShard(collection); } // SPLITSHARD request public static class SplitShard extends AsyncCollectionAdminRequest { protected String collection; protected String ranges; protected String splitKey; protected String shard; protected String splitMethod; protected Boolean splitByPrefix; protected Integer numSubShards; protected Float splitFuzz; private Properties properties; protected String createNodeSet; private SplitShard(String collection) { super(CollectionAction.SPLITSHARD); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); } public SplitShard setRanges(String ranges) { this.ranges = ranges; return this; } public SplitShard setCreateNodeSet(String nodeset) { this.createNodeSet = nodeset; return this; } public String getRanges() { return ranges; } public Integer getNumSubShards() { return numSubShards; } public SplitShard setNumSubShards(Integer numSubShards) { this.numSubShards = numSubShards; return this; } public SplitShard setSplitMethod(String splitMethod) { this.splitMethod = splitMethod; return this; } public String getSplitMethod() { return splitMethod; } public SplitShard setSplitFuzz(float splitFuzz) { this.splitFuzz = splitFuzz; return this; } public Float getSplitFuzz() { return splitFuzz; } public SplitShard setSplitKey(String splitKey) { this.splitKey = splitKey; return this; } public String getSplitKey() { return this.splitKey; } public Properties getProperties() { return properties; } public SplitShard setProperties(Properties properties) { this.properties = properties; return this; } public SplitShard setShardName(String shard) { this.shard = shard; return this; } public Boolean getSplitByPrefix() { return splitByPrefix; } public SplitShard setSplitByPrefix(Boolean splitByPrefix) { this.splitByPrefix = splitByPrefix; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CollectionAdminParams.COLLECTION, collection); if (this.shard == null && this.splitKey == null) { throw new IllegalArgumentException("You must set shardname OR splitkey for this request."); } params.set(CoreAdminParams.SHARD, shard); params.set("split.key", this.splitKey); params.set(CoreAdminParams.RANGES, ranges); params.set(CommonAdminParams.SPLIT_METHOD, splitMethod); if (numSubShards != null) { params.set("numSubShards", numSubShards); } if (splitFuzz != null) { params.set(CommonAdminParams.SPLIT_FUZZ, String.valueOf(splitFuzz)); } if (splitByPrefix != null) { params.set(CommonAdminParams.SPLIT_BY_PREFIX, splitByPrefix); } if (properties != null) { addProperties(params, properties); } if (createNodeSet != null) { params.set(CREATE_NODE_SET_PARAM, createNodeSet); } return params; } } /** Returns a SolrRequest to delete a shard from a collection */ public static DeleteShard deleteShard(String collection, String shard) { return new DeleteShard(collection, shard); } // DELETESHARD request public static class DeleteShard extends AsyncShardSpecificAdminRequest { private Boolean deleteInstanceDir; private Boolean deleteDataDir; private DeleteShard(String collection, String shard) { super(CollectionAction.DELETESHARD, collection, shard); } public Boolean getDeleteInstanceDir() { return deleteInstanceDir; } public DeleteShard setDeleteInstanceDir(Boolean deleteInstanceDir) { this.deleteInstanceDir = deleteInstanceDir; return this; } public Boolean getDeleteDataDir() { return deleteDataDir; } public DeleteShard setDeleteDataDir(Boolean deleteDataDir) { this.deleteDataDir = deleteDataDir; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); if (deleteInstanceDir != null) { params.set(CoreAdminParams.DELETE_INSTANCE_DIR, deleteInstanceDir); } if (deleteDataDir != null) { params.set(CoreAdminParams.DELETE_DATA_DIR, deleteDataDir); } return params; } } /** * Returns a SolrRequest to force a leader election for a shard in a collection * *

WARNING: This may cause data loss if the new leader does not contain updates acknowledged by * the old leader. Use only if leadership elections are entirely broken. */ public static ForceLeader forceLeaderElection(String collection, String shard) { return new ForceLeader(collection, shard); } // FORCELEADER request public static class ForceLeader extends ShardSpecificAdminRequest { private ForceLeader(String collection, String shard) { super(CollectionAction.FORCELEADER, collection, shard); } } /** A response object for {@link RequestStatus} requests */ public static class RequestStatusResponse extends CollectionAdminResponse { public RequestStatusState getRequestStatus() { NamedList innerResponse = (NamedList) getResponse().get("status"); return RequestStatusState.fromKey((String) innerResponse.get("state")); } } /** * Returns a SolrRequest for checking the status of an asynchronous request * * @see CollectionAdminRequest.AsyncCollectionAdminRequest */ public static RequestStatus requestStatus(String requestId) { return new RequestStatus(requestId); } public static void waitForAsyncRequest(String requestId, SolrClient client, long timeout) throws SolrServerException, InterruptedException, IOException { requestStatus(requestId).waitFor(client, timeout); } // REQUESTSTATUS request public static class RequestStatus extends CollectionAdminRequest { protected String requestId = null; private RequestStatus(String requestId) { super(CollectionAction.REQUESTSTATUS); this.requestId = checkNotNull("requestId", requestId); } public String getRequestId() { return this.requestId; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.REQUESTID, requestId); return params; } @Override protected RequestStatusResponse createResponse(SolrClient client) { return new RequestStatusResponse(); } /** * Wait until the asynchronous request is either completed or failed, up to a timeout * * @param client a SolrClient * @param timeoutSeconds the maximum time to wait in seconds * @return the last seen state of the request */ public RequestStatusState waitFor(SolrClient client, long timeoutSeconds) throws IOException, SolrServerException, InterruptedException { long finishTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds); RequestStatusState state = RequestStatusState.NOT_FOUND; while (System.nanoTime() < finishTime) { state = this.process(client).getRequestStatus(); if (state == RequestStatusState.COMPLETED || state == RequestStatusState.FAILED) { propagateBasicAuthCreds(deleteAsyncId(requestId)).process(client); return state; } // This is kind of slow, see SOLR-16313 TimeUnit.SECONDS.sleep(1); } return state; } } /** Returns a SolrRequest to delete an asynchronous request status */ public static DeleteStatus deleteAsyncId(String requestId) { return new DeleteStatus(checkNotNull("requestId", requestId), null); } /** Returns a SolrRequest to delete a all asynchronous request statuses */ public static DeleteStatus deleteAllAsyncIds() { return new DeleteStatus(null, true); } // DELETESTATUS request public static class DeleteStatus extends CollectionAdminRequest { protected String requestId = null; protected Boolean flush = null; private DeleteStatus(String requestId, Boolean flush) { super(CollectionAction.DELETESTATUS); if (requestId == null && flush == null) throw new IllegalArgumentException( "Either requestid or flush parameter must be specified."); if (requestId != null && flush != null) throw new IllegalArgumentException( "Both requestid and flush parameters can not be specified together."); this.requestId = requestId; this.flush = flush; } public String getRequestId() { return this.requestId; } public Boolean getFlush() { return this.flush; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); if (requestId != null) params.set(CoreAdminParams.REQUESTID, requestId); if (flush != null) params.set(CollectionAdminParams.FLUSH, flush); return params; } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } // ALIASPROP request /** * Returns a SolrRequest to add or remove properties from an alias * * @param aliasName the alias to modify */ public static SetAliasProperty setAliasProperty(String aliasName) { return new SetAliasProperty(aliasName); } public static class SetAliasProperty extends AsyncCollectionAdminRequest { private final String aliasName; private Map properties = new HashMap<>(); public SetAliasProperty(String aliasName) { super(CollectionAction.ALIASPROP); this.aliasName = SolrIdentifierValidator.validateAliasName(aliasName); } public SetAliasProperty addProperty(String key, String value) { if (value == null) { properties.put(key, ""); } else { properties.put(key, value); } return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.NAME, aliasName); properties.forEach((key, value) -> params.set("property." + key, value)); return params; } } /** * Returns a SolrRequest to create a new alias * * @param aliasName the alias name * @param aliasedCollections the collections to alias */ public static CreateAlias createAlias(String aliasName, String aliasedCollections) { return new CreateAlias(aliasName, aliasedCollections); } // CREATEALIAS request public static class CreateAlias extends AsyncCollectionAdminRequest { protected String aliasName; protected String aliasedCollections; private CreateAlias(String aliasName, String aliasedCollections) { super(CollectionAction.CREATEALIAS); this.aliasName = SolrIdentifierValidator.validateAliasName(aliasName); this.aliasedCollections = checkNotNull("aliasedCollections", aliasedCollections); } public String getAliasName() { return aliasName; } public String getAliasedCollections() { return this.aliasedCollections; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.set(CoreAdminParams.NAME, aliasName); params.set("collections", aliasedCollections); return params; } } /** * Returns a SolrRequest to create a time routed alias. For time based routing, the start should * be a standard Solr timestamp string (possibly with "date math"). * * @param aliasName the name of the alias to create. * @param start the start of the routing. A standard Solr date: ISO-8601 or NOW with date math. * @param interval date math representing the time duration of each collection (e.g. {@code * +1DAY}) * @param routerField the document field to contain the timestamp to route on * @param createCollTemplate Holds options to create a collection. The "name" is ignored. */ public static CreateTimeRoutedAlias createTimeRoutedAlias( String aliasName, String start, String interval, String routerField, Create createCollTemplate) { return new CreateTimeRoutedAlias(aliasName, routerField, start, interval, createCollTemplate); } public static class CreateTimeRoutedAlias extends AsyncCollectionAdminRequest implements RoutedAliasAdminRequest { // TODO: This and other commands in this file seem to need to share some sort of constants class // with core to allow this stuff not to be duplicated. (this is pasted from // CreateAliasCmd.java), however I think a comprehensive cleanup of this for all the requests in // this class should be done as a separate ticket. public static final String ROUTER_START = "router.start"; public static final String ROUTER_INTERVAL = "router.interval"; public static final String ROUTER_MAX_FUTURE = "router.maxFutureMs"; public static final String ROUTER_PREEMPTIVE_CREATE_WINDOW = "router.preemptiveCreateMath"; public static final String ROUTER_AUTO_DELETE_AGE = "router.autoDeleteAge"; private final String aliasName; private final String routerField; private final String start; private final String interval; // Optional: private TimeZone tz; private Long maxFutureMs; private String preemptiveCreateMath; private String autoDeleteAge; private final Create createCollTemplate; public CreateTimeRoutedAlias( String aliasName, String routerField, String start, String interval, Create createCollTemplate) { super(CollectionAction.CREATEALIAS); this.aliasName = aliasName; this.start = start; this.interval = interval; this.routerField = routerField; this.createCollTemplate = createCollTemplate; } /** Sets the timezone for interpreting any Solr "date math. */ public CreateTimeRoutedAlias setTimeZone(TimeZone tz) { this.tz = tz; return this; } /** * Sets how long into the future (millis) that we will allow a document to pass. * * @deprecated Please use {@link #setMaxFutureMs(Long)} instead. */ @Deprecated public CreateTimeRoutedAlias setMaxFutureMs(Integer maxFutureMs) { this.maxFutureMs = Long.valueOf(maxFutureMs); return this; } /** Sets how long into the future (millis) that we will allow a document to pass. */ public CreateTimeRoutedAlias setMaxFutureMs(Long maxFutureMs) { this.maxFutureMs = maxFutureMs; return this; } public CreateTimeRoutedAlias setPreemptiveCreateWindow(String preemptiveCreateMath) { this.preemptiveCreateMath = preemptiveCreateMath; return this; } public CreateTimeRoutedAlias setAutoDeleteAge(String autoDeleteAge) { this.autoDeleteAge = autoDeleteAge; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.add(CommonParams.NAME, aliasName); params.add(ROUTER_TYPE_NAME, "time"); params.add(ROUTER_FIELD, routerField); params.add(ROUTER_START, start); params.add(ROUTER_INTERVAL, interval); if (tz != null) { params.add(CommonParams.TZ, tz.getID()); } if (maxFutureMs != null) { params.add(ROUTER_MAX_FUTURE, "" + maxFutureMs); } if (preemptiveCreateMath != null) { params.add(ROUTER_PREEMPTIVE_CREATE_WINDOW, preemptiveCreateMath); } if (autoDeleteAge != null) { params.add(ROUTER_AUTO_DELETE_AGE, autoDeleteAge); } // merge the above with collectionParams. Above takes precedence. ModifiableSolrParams createCollParams = mergeCollParams(createCollTemplate); return SolrParams.wrapDefaults(params, createCollParams); } @Override public RoutedAliasTypes getType() { return RoutedAliasTypes.TIME; } @Override public String getRouterField() { return routerField; } @Override public java.util.List getParamNames() { return java.util.List.of( ROUTER_TYPE_NAME, ROUTER_FIELD, ROUTER_START, ROUTER_INTERVAL, ROUTER_MAX_FUTURE, ROUTER_PREEMPTIVE_CREATE_WINDOW, ROUTER_AUTO_DELETE_AGE, CommonParams.TZ); } @Override public java.util.List getRequiredParamNames() { return java.util.List.of(ROUTER_TYPE_NAME, ROUTER_FIELD, ROUTER_START, ROUTER_INTERVAL); } } /** * Returns a SolrRequest to create a category routed alias. * * @param aliasName the name of the alias to create. * @param routerField the document field to contain the timestamp to route on * @param maxCardinality the maximum number of collections under this CRA * @param createCollTemplate Holds options to create a collection. The "name" is ignored. */ public static CreateCategoryRoutedAlias createCategoryRoutedAlias( String aliasName, String routerField, int maxCardinality, Create createCollTemplate) { return new CreateCategoryRoutedAlias( aliasName, routerField, maxCardinality, createCollTemplate); } public static class CreateCategoryRoutedAlias extends AsyncCollectionAdminRequest implements RoutedAliasAdminRequest { public static final String ROUTER_MAX_CARDINALITY = "router.maxCardinality"; public static final String ROUTER_MUST_MATCH = "router.mustMatch"; private final String aliasName; private final String routerField; private Integer maxCardinality; private String mustMatch; private final Create createCollTemplate; public CreateCategoryRoutedAlias( String aliasName, String routerField, int maxCardinality, Create createCollTemplate) { super(CollectionAction.CREATEALIAS); this.aliasName = aliasName; this.routerField = routerField; this.maxCardinality = maxCardinality; this.createCollTemplate = createCollTemplate; } public CreateCategoryRoutedAlias setMustMatch(String regex) { this.mustMatch = regex; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); params.add(CommonParams.NAME, aliasName); params.add(ROUTER_TYPE_NAME, RoutedAliasTypes.CATEGORY.name()); params.add(ROUTER_FIELD, routerField); params.add(ROUTER_MAX_CARDINALITY, maxCardinality.toString()); if (mustMatch != null) { params.add(ROUTER_MUST_MATCH, mustMatch); } // merge the above with collectionParams. Above takes precedence. ModifiableSolrParams createCollParams = mergeCollParams(createCollTemplate); return SolrParams.wrapDefaults(params, createCollParams); } @Override public RoutedAliasTypes getType() { return RoutedAliasTypes.CATEGORY; } @Override public String getRouterField() { return routerField; } @Override public java.util.List getParamNames() { return java.util.List.of( ROUTER_TYPE_NAME, ROUTER_FIELD, ROUTER_MAX_CARDINALITY, ROUTER_MUST_MATCH); } @Override public java.util.List getRequiredParamNames() { return java.util.List.of(ROUTER_TYPE_NAME, ROUTER_FIELD, ROUTER_MAX_CARDINALITY); } } public interface RoutedAliasAdminRequest { String ROUTER_TYPE_NAME = "router.name"; String ROUTER_FIELD = "router.field"; RoutedAliasTypes getType(); String getRouterField(); java.util.List getParamNames(); java.util.List getRequiredParamNames(); SolrParams getParams(); default ModifiableSolrParams mergeCollParams(Create createCollTemplate) { ModifiableSolrParams createCollParams = new ModifiableSolrParams(); // output target if (createCollTemplate == null) { return createCollParams; } final SolrParams collParams = createCollTemplate.getParams(); final Iterator pIter = collParams.getParameterNamesIterator(); while (pIter.hasNext()) { String key = pIter.next(); if (key.equals(CollectionParams.ACTION) || key.equals("name")) { continue; } createCollParams.set("create-collection." + key, collParams.getParams(key)); } return createCollParams; } } /** * Create a Dimensional Routed alias from two or more routed alias types. * * @param aliasName The name of the alias * @param createCollTemplate a create command that will be used for all collections created * @param dims Routed Alias requests. Note that the aliasName and collection templates inside * dimensions will be ignored and may be safely set to null * @return An object representing a basic DimensionalRoutedAlias creation request. */ public static DimensionalRoutedAlias createDimensionalRoutedAlias( String aliasName, Create createCollTemplate, RoutedAliasAdminRequest... dims) { return new DimensionalRoutedAlias(aliasName, createCollTemplate, dims); } public static class DimensionalRoutedAlias extends AsyncCollectionAdminRequest implements RoutedAliasAdminRequest { private String aliasName; private final Create createCollTemplate; private final RoutedAliasAdminRequest[] dims; public DimensionalRoutedAlias( String aliasName, Create createCollTemplate, RoutedAliasAdminRequest... dims) { super(CollectionAction.CREATEALIAS); this.aliasName = aliasName; this.createCollTemplate = createCollTemplate; this.dims = dims; } public static void addDimensionIndexIfRequired(Set params, int i, String param) { params.add(withDimensionIndexIfRequired(param, i)); } private static String withDimensionIndexIfRequired(String param, int index) { if (param.startsWith(ROUTER_PREFIX)) { return ROUTER_PREFIX + index + "." + param.split("\\.")[1]; } else { return param; } } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); java.util.List types = new ArrayList<>(); java.util.List fields = new ArrayList<>(); for (int i = 0; i < dims.length; i++) { RoutedAliasAdminRequest dim = dims[i]; types.add(dim.getType().name()); fields.add(dim.getRouterField()); for (String param : dim.getParamNames()) { String value = dim.getParams().get(param); if (value != null) { params.add(withDimensionIndexIfRequired(param, i), value); } else { if (dim.getRequiredParamNames().contains(param)) { throw new IllegalArgumentException( "Dimension of type " + dim.getType() + " requires a value for " + param); } } } } params.add(CommonParams.NAME, aliasName); params.add(ROUTER_TYPE_NAME, "Dimensional[" + String.join(",", types) + "]"); params.add(ROUTER_FIELD, String.join(",", fields)); // merge the above with collectionParams. Above takes precedence. ModifiableSolrParams createCollParams = mergeCollParams(createCollTemplate); return SolrParams.wrapDefaults(params, createCollParams); } @Override public RoutedAliasTypes getType() { throw new UnsupportedOperationException( "Dimensions of dimensions are not allowed, the multiverse might collapse!"); } @Override public String getRouterField() { throw new UnsupportedOperationException( "Dimensions of dimensions are not allowed, the multiverse might collapse!"); } @Override public java.util.List getParamNames() { throw new UnsupportedOperationException( "Dimensions of dimensions are not allowed, the multiverse might collapse!"); } @Override public java.util.List getRequiredParamNames() { throw new UnsupportedOperationException( "Dimensions of dimensions are not allowed, the multiverse might collapse!"); } } /** Returns a SolrRequest to delete an alias */ public static DeleteAlias deleteAlias(String aliasName) { return new DeleteAlias(aliasName); } // DELETEALIAS request public static class DeleteAlias extends AsyncCollectionAdminRequest { protected String aliasName; private DeleteAlias(String aliasName) { super(CollectionAction.DELETEALIAS); this.aliasName = checkNotNull("aliasName", aliasName); } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.NAME, aliasName); return params; } } /** * Returns a SolrRequest to add a replica of type {@link * org.apache.solr.common.cloud.Replica.Type#NRT} to a shard in a collection */ public static AddReplica addReplicaToShard(String collection, String shard) { return addReplicaToShard(collection, shard, Replica.Type.NRT); } /** * Returns a SolrRequest to add a replica of the specified type to a shard in a collection. If the * replica type is null, the server default will be used. */ public static AddReplica addReplicaToShard( String collection, String shard, Replica.Type replicaType) { return new AddReplica( collection, checkNotNull(CoreAdminParams.SHARD, shard), null, replicaType); } /** Returns a SolrRequest to add a replica to a collection using a route key */ public static AddReplica addReplicaByRouteKey(String collection, String routeKey) { return new AddReplica(collection, null, checkNotNull("routeKey", routeKey), null); } // ADDREPLICA request public static class AddReplica extends AsyncCollectionAdminRequest { private static final String REPLICA_TYPE_PARAM = "type"; protected String collection; protected String shard; protected String node; protected String coreName; protected String routeKey; protected String instanceDir; protected String dataDir; protected String ulogDir; protected Properties properties; protected Replica.Type type; protected Integer nrtReplicas, tlogReplicas, pullReplicas; protected Boolean skipNodeAssignment; protected String createNodeSet; private AddReplica(String collection, String shard, String routeKey, Replica.Type type) { super(CollectionAction.ADDREPLICA); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); this.shard = shard; this.routeKey = routeKey; this.type = type; } public Properties getProperties() { return properties; } public AddReplica setProperties(Properties properties) { this.properties = properties; return this; } public AddReplica withProperty(String key, String value) { if (this.properties == null) this.properties = new Properties(); this.properties.setProperty(key, value); return this; } public String getNode() { return node; } public AddReplica setNode(String node) { this.node = node; return this; } public AddReplica setSkipNodeAssignment(Boolean skipNodeAssignment) { this.skipNodeAssignment = skipNodeAssignment; return this; } public String getRouteKey() { return routeKey; } public String getInstanceDir() { return instanceDir; } public String getUlogDir() { return ulogDir; } public AddReplica setInstanceDir(String instanceDir) { this.instanceDir = instanceDir; return this; } public String getDataDir() { return dataDir; } public AddReplica setDataDir(String dataDir) { this.dataDir = dataDir; return this; } public AddReplica setType(Replica.Type type) { this.type = type; return this; } public AddReplica setCoreName(String coreName) { this.coreName = coreName; return this; } public AddReplica setUlogDir(String ulogDir) { this.ulogDir = ulogDir; return this; } public String getShard() { return shard; } public Integer getNrtReplicas() { return nrtReplicas; } public AddReplica setNrtReplicas(Integer nrtReplicas) { this.nrtReplicas = nrtReplicas; return this; } public Integer getTlogReplicas() { return tlogReplicas; } public AddReplica setTlogReplicas(Integer tlogReplicas) { this.tlogReplicas = tlogReplicas; return this; } public Integer getPullReplicas() { return pullReplicas; } public AddReplica setPullReplicas(Integer pullReplicas) { this.pullReplicas = pullReplicas; return this; } public String getCreateNodeSet() { return createNodeSet; } public AddReplica setCreateNodeSet(String createNodeSet) { this.createNodeSet = createNodeSet; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.add(CoreAdminParams.COLLECTION, collection); assert ((null == routeKey) ^ (null == shard)); if (null != shard) { params.add(CoreAdminParams.SHARD, shard); } if (null != routeKey) { params.add(ShardParams._ROUTE_, routeKey); } if (node != null) { params.add(CoreAdminParams.NODE, node); } if (skipNodeAssignment != null) { params.add(SKIP_NODE_ASSIGNMENT, String.valueOf(skipNodeAssignment)); } if (instanceDir != null) { params.add(CoreAdminParams.INSTANCE_DIR, instanceDir); } if (dataDir != null) { params.add(CoreAdminParams.DATA_DIR, dataDir); } if (ulogDir != null) { params.add(CoreAdminParams.ULOG_DIR, ulogDir); } if (coreName != null) { params.add(CoreAdminParams.NAME, coreName); } if (type != null) { params.add(REPLICA_TYPE_PARAM, type.name()); } if (properties != null) { addProperties(params, properties); } if (nrtReplicas != null) { params.add(CollectionAdminParams.NRT_REPLICAS, String.valueOf(nrtReplicas)); } if (tlogReplicas != null) { params.add(CollectionAdminParams.TLOG_REPLICAS, String.valueOf(tlogReplicas)); } if (pullReplicas != null) { params.add(CollectionAdminParams.PULL_REPLICAS, String.valueOf(pullReplicas)); } if (createNodeSet != null) { params.add(CREATE_NODE_SET_PARAM, createNodeSet); } return params; } } /** Returns a SolrRequest to delete a replica from a shard in a collection */ public static DeleteReplica deleteReplica(String collection, String shard, String replica) { return new DeleteReplica( collection, checkNotNull(CoreAdminParams.SHARD, shard), checkNotNull(CoreAdminParams.REPLICA, replica)); } public static DeleteReplica deleteReplica(String collection, String shard, int count) { return new DeleteReplica(collection, checkNotNull(CoreAdminParams.SHARD, shard), count); } /** Returns a SolrRequest to remove a number of replicas from a specific shard */ public static DeleteReplica deleteReplicasFromShard(String collection, String shard, int count) { return new DeleteReplica(collection, checkNotNull(CoreAdminParams.SHARD, shard), count); } public static DeleteReplica deleteReplicasFromAllShards(String collection, int count) { return new DeleteReplica(collection, count); } // DELETEREPLICA request public static class DeleteReplica extends AsyncCollectionSpecificAdminRequest { protected String shard; protected String replica; protected Boolean onlyIfDown; private Boolean deleteDataDir; private Boolean deleteInstanceDir; private Boolean deleteIndexDir; private Integer count; private DeleteReplica(String collection, String shard, String replica) { super(CollectionAction.DELETEREPLICA, collection); this.shard = shard; this.replica = replica; } private DeleteReplica(String collection, String shard, int count) { super(CollectionAction.DELETEREPLICA, collection); this.shard = shard; this.count = count; } private DeleteReplica(String collection, int count) { super(CollectionAction.DELETEREPLICA, collection); this.count = count; } public String getReplica() { return this.replica; } public DeleteReplica setOnlyIfDown(boolean onlyIfDown) { this.onlyIfDown = onlyIfDown; return this; } public Boolean getOnlyIfDown() { return this.onlyIfDown; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); // AsyncCollectionSpecificAdminRequest uses 'name' rather than 'collection' // TODO - deal with this inconsistency params.remove(CoreAdminParams.NAME); params.set(CollectionAdminParams.COLLECTION, this.collection); if (this.replica != null) params.set(CollectionAdminParams.REPLICA, this.replica); if (this.shard != null) params.set(CollectionAdminParams.SHARD, this.shard); if (onlyIfDown != null) { params.set("onlyIfDown", onlyIfDown); } if (deleteDataDir != null) { params.set(CoreAdminParams.DELETE_DATA_DIR, deleteDataDir); } if (deleteInstanceDir != null) { params.set(CoreAdminParams.DELETE_INSTANCE_DIR, deleteInstanceDir); } if (deleteIndexDir != null) { params.set(CoreAdminParams.DELETE_INDEX, deleteIndexDir); } if (count != null) { params.set(COUNT_PROP, count); } return params; } public Boolean getDeleteDataDir() { return deleteDataDir; } public DeleteReplica setDeleteDataDir(Boolean deleteDataDir) { this.deleteDataDir = deleteDataDir; return this; } public Boolean getDeleteInstanceDir() { return deleteInstanceDir; } public DeleteReplica setDeleteInstanceDir(Boolean deleteInstanceDir) { this.deleteInstanceDir = deleteInstanceDir; return this; } public Boolean getDeleteIndexDir() { return deleteIndexDir; } public DeleteReplica setDeleteIndexDir(Boolean deleteIndexDir) { this.deleteIndexDir = deleteIndexDir; return this; } } /** Returns a SolrRequest to set (or unset) a cluster property */ public static ClusterProp setClusterProperty(String propertyName, String propertyValue) { return new ClusterProp(propertyName, propertyValue); } // CLUSTERPROP request public static class ClusterProp extends CollectionAdminRequest { private String propertyName; private String propertyValue; private ClusterProp(String propertyName, String propertyValue) { super(CollectionAction.CLUSTERPROP); this.propertyName = checkNotNull("propertyName", propertyName); this.propertyValue = propertyValue; } public String getPropertyName() { return this.propertyName; } public String getPropertyValue() { return this.propertyValue; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.add(CoreAdminParams.NAME, propertyName); params.add("val", propertyValue); return params; } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } public static CollectionProp setCollectionProperty( String collection, String propertyName, String propertyValue) { return new CollectionProp(collection, propertyName, propertyValue); } // COLLECTIONPROP request public static class CollectionProp extends AsyncCollectionSpecificAdminRequest { private String propertyName; private String propertyValue; private CollectionProp(String collection, String propertyName, String propertyValue) { super(CollectionAction.COLLECTIONPROP, collection); this.propertyName = checkNotNull("propertyName", propertyName); this.propertyValue = propertyValue; } public String getPropertyName() { return this.propertyName; } public String getPropertyValue() { return this.propertyValue; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.add(CollectionAdminParams.PROPERTY_NAME, propertyName); params.add(CollectionAdminParams.PROPERTY_VALUE, propertyValue); return params; } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } /** Returns a SolrRequest to migrate data matching a split key to another collection */ public static Migrate migrateData(String collection, String targetCollection, String splitKey) { return new Migrate(collection, targetCollection, splitKey); } // MIGRATE request public static class Migrate extends AsyncCollectionAdminRequest { private String collection; private String targetCollection; private String splitKey; private Integer forwardTimeout; private Properties properties; private Migrate(String collection, String targetCollection, String splitKey) { super(CollectionAction.MIGRATE); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); this.targetCollection = checkNotNull("targetCollection", targetCollection); this.splitKey = checkNotNull("split.key", splitKey); } public String getCollectionName() { return collection; } public String getTargetCollection() { return this.targetCollection; } public String getSplitKey() { return this.splitKey; } public Migrate setForwardTimeout(int forwardTimeout) { this.forwardTimeout = forwardTimeout; return this; } public Integer getForwardTimeout() { return this.forwardTimeout; } public Migrate setProperties(Properties properties) { this.properties = properties; return this; } public Properties getProperties() { return this.properties; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.COLLECTION, collection); params.set("target.collection", targetCollection); params.set("split.key", splitKey); if (forwardTimeout != null) { params.set("forward.timeout", forwardTimeout); } if (properties != null) { addProperties(params, properties); } return params; } } /** Returns a SolrRequest to add a role to a node */ public static AddRole addRole(String node, String role) { return new AddRole(node, role); } // ADDROLE request public static class AddRole extends CollectionAdminRoleRequest { private AddRole(String node, String role) { super(CollectionAction.ADDROLE, node, role); } } /** Returns a SolrRequest to remove a role from a node */ public static RemoveRole removeRole(String node, String role) { return new RemoveRole(node, role); } // REMOVEROLE request public static class RemoveRole extends CollectionAdminRoleRequest { private RemoveRole(String node, String role) { super(CollectionAction.REMOVEROLE, node, role); } } /** Return a SolrRequest to get the Overseer status */ public static OverseerStatus getOverseerStatus() { return new OverseerStatus(); } // OVERSEERSTATUS request public static class OverseerStatus extends AsyncCollectionAdminRequest { public OverseerStatus() { super(CollectionAction.OVERSEERSTATUS); } } // DISTRIBUTEDAPIPROCESSING request for verifying if Collection API execution is distributed public static class RequestApiDistributedProcessing extends CollectionAdminRequest { public RequestApiDistributedProcessing() { super(CollectionAction.DISTRIBUTEDAPIPROCESSING); } @Override protected RequestApiDistributedProcessingResponse createResponse(SolrClient client) { return new RequestApiDistributedProcessingResponse(); } } /** A response object for {@link RequestApiDistributedProcessing} requests */ public static class RequestApiDistributedProcessingResponse extends CollectionAdminResponse { public boolean getIsCollectionApiDistributed() { return (Boolean) getResponse().get("isDistributedApi"); } } /** Return a SolrRequest to get the Cluster status */ public static ClusterStatus getClusterStatus() { return new ClusterStatus(); } // CLUSTERSTATUS request public static class ClusterStatus extends CollectionAdminRequest { protected String shardName = null; protected String collection = null; protected String routeKey = null; public ClusterStatus() { super(CollectionAction.CLUSTERSTATUS); } public ClusterStatus setCollectionName(String collectionName) { this.collection = collectionName; return this; } public String getCollectionName() { return collection; } public ClusterStatus setShardName(String shard) { this.shardName = shard; return this; } public String getShardName() { return this.shardName; } public String getRouteKey() { return routeKey; } public ClusterStatus setRouteKey(String routeKey) { this.routeKey = routeKey; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = (ModifiableSolrParams) super.getParams(); if (collection != null) { params.set(CoreAdminParams.COLLECTION, collection); } if (shardName != null) { params.set(CoreAdminParams.SHARD, shardName); } if (routeKey != null) { params.set(ShardParams._ROUTE_, routeKey); } return params; } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } // LISTALIASES request public static class ListAliases extends CollectionAdminRequest { public ListAliases() { super(CollectionAction.LISTALIASES); } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } /** Returns a SolrRequest to get a list of collections in the cluster */ @SuppressWarnings({"unchecked"}) public static java.util.List listCollections(SolrClient client) throws IOException, SolrServerException { CollectionAdminResponse resp = new List().process(client); return (java.util.List) resp.getResponse().get("collections"); } // LIST request public static class List extends CollectionAdminRequest { public List() { super(CollectionAction.LIST); } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } /** * Configure a {@link SolrRequest} object to delete a single backup-point by it's Backup ID. * *

The created request object is only valid on backup locations that use the new "incremental" * backup file-format introduced in Solr 8.9. It should not be used on locations holding * "non-incremental" backups (those created prior to 8.9, or after 8.9 using the advanced * "incremental=false" flag). * * @param backupName the name of the backup that this request should delete a single backup-point * from. * @param backupId the ID of the backup-point for this request to delete */ public static DeleteBackup deleteBackupById(String backupName, int backupId) { return new DeleteBackup(backupName).setBackupId(backupId); } /** * Create a {@link SolrRequest} object to delete all backup-points after the most recent 'N' * *

The created request object is only valid on backup locations that use the new "incremental" * backup file-format introduced in Solr 8.9. It should not be used on locations holding * "non-incremental" backups (those created prior to 8.9, or after 8.9 using the advanced * "incremental=false" flag). * * @param backupName the name of the backup that this request should delete backup-points from. * @param numRecentBackupPointsToRetain the number of "most-recent" backup-points to retain. */ public static DeleteBackup deleteBackupByRecency( String backupName, int numRecentBackupPointsToRetain) { return new DeleteBackup(backupName).setMaxNumBackupPoints(numRecentBackupPointsToRetain); } /** * Create a {@link SolrRequest} object to delete all unused-files at the backup location. * *

The created request object is only valid on backup locations that use the new "incremental" * backup file-format introduced in Solr 8.9. It should not be used on locations holding * "non-incremental" backups (those created prior to 8.9, or after 8.9 using the advanced * "incremental=false" flag). * * @param backupName the name of the backup that this request should delete unused files from. */ public static DeleteBackup deleteBackupPurgeUnusedFiles(String backupName) { return new DeleteBackup(backupName).setPurgeUnused(); } /** * {@link SolrRequest} class for the "Backup Deletion" API. * *

Currently the API represented by this class only supports deletion of the new "incremental" * backup file-format introduced in Solr 8.9. It should not be used on locations holding * "non-incremental" backups (those created prior to 8.9, or after 8.9 using the advanced * "incremental=false" flag). */ public static class DeleteBackup extends CollectionAdminRequest { private final String name; private String repository; private String location; private Integer backupId; private Integer maxNumBackupPoints; private Boolean purgeUnused; private DeleteBackup(String backupName) { super(CollectionAction.DELETEBACKUP); this.name = backupName; } /** * @param backupRepository the name of the repository implementation to use for accessing backup * information. Defaults to 'LocalFileSystemRepository' if not specified. */ public DeleteBackup setRepositoryName(String backupRepository) { this.repository = backupRepository; return this; } /** * @param backupLocation the location this request will use when accessing backup information. * This parameter is not required - if not specified on the request, Solr will attempt to * read a default location from BackupRepository configuration (solr.xml) and from cluster * properties. If none of these places provide 'location' information an error will be * thrown. */ public DeleteBackup setLocation(String backupLocation) { location = backupLocation; return this; } /** * @param backupId the ID of a single backup-point for this request to delete. Mutually * exclusive with the parameters controlled by {@link #setMaxNumBackupPoints(int)} and * {@link #setPurgeUnused()}. * @see #deleteBackupById(String, int) */ protected DeleteBackup setBackupId(int backupId) { this.backupId = backupId; return this; } /** * @param backupPointsToRetain the number of backup-points to retain, deleting the reset. * Mutually exclusive with the parameters controlled by {@link #setBackupId(int)} and {@link * #setPurgeUnused()}. * @see #deleteBackupByRecency(String, int) */ protected DeleteBackup setMaxNumBackupPoints(int backupPointsToRetain) { this.maxNumBackupPoints = backupPointsToRetain; return this; } /** * Configures the request to delete all unused files. * *

Mutually exclusive with the parameters controlled by {@link #setBackupId(int)} and {@link * #setMaxNumBackupPoints(int)} * * @see #deleteBackupPurgeUnusedFiles(String) */ protected DeleteBackup setPurgeUnused() { this.purgeUnused = true; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.NAME, name); params.setNonNull(CoreAdminParams.BACKUP_LOCATION, location); params.setNonNull(BACKUP_REPOSITORY, repository); params.setNonNull(CoreAdminParams.BACKUP_ID, backupId); params.setNonNull(CoreAdminParams.MAX_NUM_BACKUP_POINTS, maxNumBackupPoints); params.setNonNull(CoreAdminParams.BACKUP_PURGE_UNUSED, purgeUnused); return params; } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } /** * Create a {@link SolrRequest} object to list information about all backup points with the * specified name. * * @param backupName the name of the backup that this request should list information about */ public static ListBackup listBackup(String backupName) { return new ListBackup(backupName); } /** * {@link SolrRequest} class for the "Backup List" API. * *

Currently the API represented by this class only supports listing of the new "incremental" * backup file-format introduced in Solr 8.9. It should not be used on locations holding * "non-incremental" backups (those created prior to 8.9, or after 8.9 using the advanced * "incremental=false" flag). */ public static class ListBackup extends CollectionAdminRequest { private final String backupName; private String location; private String repositoryName; private ListBackup(String backupName) { super(CollectionAction.LISTBACKUP); this.backupName = backupName; } /** * @param backupRepository the name of the repository implementation to use for accessing backup * information. Defaults to 'LocalFileSystemRepository' if not specified. */ public ListBackup setBackupRepository(String backupRepository) { this.repositoryName = backupRepository; return this; } /** * @param backupLocation the location this request will use when accessing backup information. * This parameter is not required - if not specified on the request, Solr will attempt to * read a default location from BackupRepository configuration (solr.xml) and from cluster * properties. If none of these places provide 'location' information an error will be * thrown. */ public ListBackup setBackupLocation(String backupLocation) { this.location = backupLocation; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.NAME, backupName); params.setNonNull(CoreAdminParams.BACKUP_LOCATION, this.location); params.setNonNull(BACKUP_REPOSITORY, this.repositoryName); return params; } @Override protected CollectionAdminResponse createResponse(SolrClient client) { return new CollectionAdminResponse(); } } /** Returns a SolrRequest to add a property to a specific replica */ public static AddReplicaProp addReplicaProperty( String collection, String shard, String replica, String propertyName, String propertyValue) { return new AddReplicaProp(collection, shard, replica, propertyName, propertyValue); } // ADDREPLICAPROP request public static class AddReplicaProp extends AsyncShardSpecificAdminRequest { private String replica; private String propertyName; private String propertyValue; private Boolean shardUnique; private AddReplicaProp( String collection, String shard, String replica, String propertyName, String propertyValue) { super(CollectionAction.ADDREPLICAPROP, collection, shard); this.replica = checkNotNull(CoreAdminParams.REPLICA, replica); this.propertyName = checkNotNull("propertyName", propertyName); this.propertyValue = checkNotNull("propertyValue", propertyValue); } public String getReplica() { return replica; } public String getPropertyName() { return propertyName; } public String getPropertyValue() { return propertyValue; } public Boolean getShardUnique() { return shardUnique; } public AddReplicaProp setShardUnique(Boolean shardUnique) { this.shardUnique = shardUnique; return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.REPLICA, replica); params.set("property", propertyName); params.set("property.value", propertyValue); if (shardUnique != null) { params.set("shardUnique", shardUnique); } return params; } } /** Returns a SolrRequest to delete a property from a specific replica */ public static DeleteReplicaProp deleteReplicaProperty( String collection, String shard, String replica, String propertyName) { return new DeleteReplicaProp(collection, shard, replica, propertyName); } // DELETEREPLICAPROP request public static class DeleteReplicaProp extends AsyncShardSpecificAdminRequest { private final String replica; private final String propertyName; private DeleteReplicaProp( String collection, String shard, String replica, String propertyName) { super(CollectionAction.DELETEREPLICAPROP, collection, shard); this.replica = checkNotNull(CoreAdminParams.REPLICA, replica); this.propertyName = checkNotNull("propertyName", propertyName); } public String getReplica() { return replica; } public String getPropertyName() { return propertyName; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.REPLICA, replica); params.set("property", propertyName); return params; } } /** Returns a SolrRequest to balance a replica property across the shards of a collection */ public static BalanceShardUnique balanceReplicaProperty(String collection, String propertyName) { return new BalanceShardUnique(collection, propertyName); } // BALANCESHARDUNIQUE request public static class BalanceShardUnique extends AsyncCollectionAdminRequest { protected String collection; protected String propertyName; protected Boolean onlyActiveNodes; protected Boolean shardUnique; private BalanceShardUnique(String collection, String propertyName) { super(CollectionAction.BALANCESHARDUNIQUE); this.collection = checkNotNull(CoreAdminParams.COLLECTION, collection); this.propertyName = checkNotNull("propertyName", propertyName); } public String getPropertyName() { return propertyName; } public Boolean getOnlyActiveNodes() { return onlyActiveNodes; } public BalanceShardUnique setOnlyActiveNodes(Boolean onlyActiveNodes) { this.onlyActiveNodes = onlyActiveNodes; return this; } public Boolean getShardUnique() { return shardUnique; } public BalanceShardUnique setShardUnique(Boolean shardUnique) { this.shardUnique = shardUnique; return this; } @Override public String getCollection() { return collection; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.COLLECTION, collection); params.set("property", propertyName); if (onlyActiveNodes != null) params.set("onlyactivenodes", onlyActiveNodes); if (shardUnique != null) params.set("shardUnique", shardUnique); return params; } } /** A Modify Collection request */ public static class Modify extends AsyncCollectionSpecificAdminRequest { protected Map attributes; private Modify(String collection, Map attributes) { super(CollectionAction.MODIFYCOLLECTION, collection); this.attributes = attributes; } /** * Sets the attributes to be modified using the Modify Collection API. Note: this method will * overwrite any previously set attributes * * @param attributes a map of attribute key vs value */ public void setAttributes(Map attributes) { this.attributes = attributes; } /** * Sets the collection attribute to the given value * * @param key a string attribute key, must be one of the entries documented in the Modify * Collection API documentation * @param value the attribute value for the given key */ public Modify setAttribute(String key, Object value) { if (key == null) { throw new IllegalArgumentException( "Attribute key cannot be null for the modify collection API"); } if (!MODIFIABLE_COLLECTION_PROPERTIES.contains(key)) { throw new IllegalArgumentException( "Unknown attribute key: " + key + ". Must be one of: " + MODIFIABLE_COLLECTION_PROPERTIES); } if (value == null) { throw new IllegalArgumentException("Value cannot be null for key: " + key); } if (attributes == null) { attributes = new HashMap<>(); } attributes.put(key, value); return this; } /** * Removes the given key from the collection * * @param key the string attribute key, must be one of the entries documented in the Modify * Collection API documentation */ public Modify unsetAttribute(String key) { if (key == null) { throw new IllegalArgumentException( "Attribute key cannot be null for the modify collection API"); } if (!MODIFIABLE_COLLECTION_PROPERTIES.contains(key)) { throw new IllegalArgumentException( "Unknown attribute key: " + key + ". Must be one of: " + MODIFIABLE_COLLECTION_PROPERTIES); } if (attributes == null) { attributes = new HashMap<>(); } attributes.put(key, ""); return this; } @Override public SolrParams getParams() { ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); params.set(CoreAdminParams.COLLECTION, collection); for (Map.Entry entry : attributes.entrySet()) { params.set(entry.getKey(), String.valueOf(entry.getValue())); } return params; } } }