/**
  * get the solr document list from a query response This differs from getResponseByParams in such
  * a way that it does only create the fields of the response but never search snippets and there
  * are also no facets generated.
  *
  * @param params
  * @return
  * @throws IOException
  * @throws SolrException
  */
 @Override
 public SolrDocumentList getDocumentListByParams(ModifiableSolrParams params)
     throws IOException, SolrException {
   SolrQueryRequest req = this.request(params);
   SolrQueryResponse response = null;
   String q = params.get(CommonParams.Q);
   String fq = params.get(CommonParams.FQ);
   String sort = params.get(CommonParams.SORT);
   String threadname = Thread.currentThread().getName();
   try {
     if (q != null)
       Thread.currentThread()
           .setName(
               "solr query: q = "
                   + q
                   + (fq == null ? "" : ", fq = " + fq)
                   + (sort == null ? "" : ", sort = " + sort)); // for debugging in Threaddump
     response = this.query(req);
     if (q != null) Thread.currentThread().setName(threadname);
     if (response == null) throw new IOException("response == null");
     return SolrQueryResponse2SolrDocumentList(req, response);
   } finally {
     req.close();
     SolrRequestInfo.clearRequestInfo();
   }
 }
 /**
  * Adds extra parameters to a Solr query for better term searches, including custom options. More
  * specifically, adds parameters for requesting the score to be included in the results, for
  * requesting a spellcheck result, and sets the {@code start} and {@code rows} parameters when
  * missing.
  *
  * @param originalParams the original Solr parameters to enhance
  * @param queryOptions extra options to include in the query; these override the default values,
  *     but don't override values already set in the query
  * @return the enhanced parameters
  */
 public static SolrParams enhanceParams(
     SolrParams originalParams, Map<String, String> queryOptions) {
   if (originalParams == null) {
     return null;
   }
   ModifiableSolrParams newParams = new ModifiableSolrParams();
   newParams.set(CommonParams.START, "0");
   newParams.set(CommonParams.ROWS, "1000");
   newParams.set(CommonParams.FL, "* score");
   if (queryOptions != null) {
     for (Map.Entry<String, String> item : queryOptions.entrySet()) {
       newParams.set(item.getKey(), item.getValue());
     }
   }
   for (Map.Entry<String, Object> item : originalParams.toNamedList()) {
     if (item.getValue() != null && item.getValue() instanceof String[]) {
       newParams.set(item.getKey(), (String[]) item.getValue());
     } else {
       newParams.set(item.getKey(), String.valueOf(item.getValue()));
     }
   }
   if (newParams.get(SPELLCHECK) == null) {
     newParams.set(SPELLCHECK, Boolean.toString(true));
     newParams.set(SpellingParams.SPELLCHECK_COLLATE, Boolean.toString(true));
   }
   return newParams;
 }
  /**
   * Replaces the original query in the Solr parameters with the suggested spellchecked query. It
   * also fixes the boost query, if any.
   *
   * @param originalParams the original Solr parameters to fix
   * @param suggestedQuery the suggested query
   * @return new Solr parameters with the query and boost query fixed
   */
  public static SolrParams applySpellcheckSuggestion(
      SolrParams originalParams, String suggestedQuery) {
    if (originalParams == null) {
      return null;
    }
    if (StringUtils.isBlank(suggestedQuery)) {
      return originalParams;
    }
    ModifiableSolrParams newParams = new ModifiableSolrParams(originalParams);
    String newQuery = suggestedQuery;

    // Since the spelling suggestion might not be that good, also search for the original user input
    if (StringUtils.isNotEmpty(originalParams.get(SpellingParams.SPELLCHECK_Q))) {
      newQuery = originalParams.get(CommonParams.Q) + "^10 " + suggestedQuery;
    }

    // Check if the last term in the query is a word stub search which, in case the request comes
    // from a
    // user-triggered search for terms from the UI, is a prefix search for the last typed word
    Matcher originalStub = WORD_STUB.matcher(newParams.get(CommonParams.Q));
    Matcher newStub = WORD_STUB.matcher(suggestedQuery);
    if (originalStub.find()
        && newStub.find()
        && !StringUtils.equals(originalStub.group(2), newStub.group(2))) {
      // Since word stubs aren't complete words, they may wrongly be "corrected" to a full word that
      // doesn't match
      // what the user started typing; include both the original stub and the "corrected" stub in
      // the query
      newQuery += ' ' + originalStub.group() + "^1.5";
      // Also fix the boost query
      String boostQuery = newParams.get(DisMaxParams.BQ);
      if (StringUtils.isNotEmpty(boostQuery)) {
        newParams.add(DisMaxParams.BQ, boostQuery.replace(originalStub.group(2), newStub.group(2)));
      }
    }
    // Replace the query
    newParams.set(CommonParams.Q, newQuery);

    return newParams;
  }
  private NamedList<Object> directUpdate(AbstractUpdateRequest request, ClusterState clusterState)
      throws SolrServerException {
    UpdateRequest updateRequest = (UpdateRequest) request;
    ModifiableSolrParams params = (ModifiableSolrParams) request.getParams();
    ModifiableSolrParams routableParams = new ModifiableSolrParams();
    ModifiableSolrParams nonRoutableParams = new ModifiableSolrParams();

    if (params != null) {
      nonRoutableParams.add(params);
      routableParams.add(params);
      for (String param : NON_ROUTABLE_PARAMS) {
        routableParams.remove(param);
      }
    }

    String collection = nonRoutableParams.get(UpdateParams.COLLECTION, defaultCollection);
    if (collection == null) {
      throw new SolrServerException(
          "No collection param specified on request and no default collection has been set.");
    }

    // Check to see if the collection is an alias.
    Aliases aliases = zkStateReader.getAliases();
    if (aliases != null) {
      Map<String, String> collectionAliases = aliases.getCollectionAliasMap();
      if (collectionAliases != null && collectionAliases.containsKey(collection)) {
        collection = collectionAliases.get(collection);
      }
    }

    DocCollection col = getDocCollection(clusterState, collection);

    DocRouter router = col.getRouter();

    if (router instanceof ImplicitDocRouter) {
      // short circuit as optimization
      return null;
    }

    // Create the URL map, which is keyed on slice name.
    // The value is a list of URLs for each replica in the slice.
    // The first value in the list is the leader for the slice.
    Map<String, List<String>> urlMap = buildUrlMap(col);
    if (urlMap == null) {
      // we could not find a leader yet - use unoptimized general path
      return null;
    }

    NamedList<Throwable> exceptions = new NamedList<>();
    NamedList<NamedList> shardResponses = new NamedList<>();

    Map<String, LBHttpSolrServer.Req> routes =
        updateRequest.getRoutes(router, col, urlMap, routableParams, this.idField);
    if (routes == null) {
      return null;
    }

    long start = System.nanoTime();

    if (parallelUpdates) {
      final Map<String, Future<NamedList<?>>> responseFutures = new HashMap<>(routes.size());
      for (final Map.Entry<String, LBHttpSolrServer.Req> entry : routes.entrySet()) {
        final String url = entry.getKey();
        final LBHttpSolrServer.Req lbRequest = entry.getValue();
        responseFutures.put(
            url,
            threadPool.submit(
                new Callable<NamedList<?>>() {
                  @Override
                  public NamedList<?> call() throws Exception {
                    return lbServer.request(lbRequest).getResponse();
                  }
                }));
      }

      for (final Map.Entry<String, Future<NamedList<?>>> entry : responseFutures.entrySet()) {
        final String url = entry.getKey();
        final Future<NamedList<?>> responseFuture = entry.getValue();
        try {
          shardResponses.add(url, responseFuture.get());
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
          throw new RuntimeException(e);
        } catch (ExecutionException e) {
          exceptions.add(url, e.getCause());
        }
      }

      if (exceptions.size() > 0) {
        throw new RouteException(ErrorCode.SERVER_ERROR, exceptions, routes);
      }
    } else {
      for (Map.Entry<String, LBHttpSolrServer.Req> entry : routes.entrySet()) {
        String url = entry.getKey();
        LBHttpSolrServer.Req lbRequest = entry.getValue();
        try {
          NamedList<Object> rsp = lbServer.request(lbRequest).getResponse();
          shardResponses.add(url, rsp);
        } catch (Exception e) {
          throw new SolrServerException(e);
        }
      }
    }

    UpdateRequest nonRoutableRequest = null;
    List<String> deleteQuery = updateRequest.getDeleteQuery();
    if (deleteQuery != null && deleteQuery.size() > 0) {
      UpdateRequest deleteQueryRequest = new UpdateRequest();
      deleteQueryRequest.setDeleteQuery(deleteQuery);
      nonRoutableRequest = deleteQueryRequest;
    }

    Set<String> paramNames = nonRoutableParams.getParameterNames();

    Set<String> intersection = new HashSet<>(paramNames);
    intersection.retainAll(NON_ROUTABLE_PARAMS);

    if (nonRoutableRequest != null || intersection.size() > 0) {
      if (nonRoutableRequest == null) {
        nonRoutableRequest = new UpdateRequest();
      }
      nonRoutableRequest.setParams(nonRoutableParams);
      List<String> urlList = new ArrayList<>();
      urlList.addAll(routes.keySet());
      Collections.shuffle(urlList, rand);
      LBHttpSolrServer.Req req = new LBHttpSolrServer.Req(nonRoutableRequest, urlList);
      try {
        LBHttpSolrServer.Rsp rsp = lbServer.request(req);
        shardResponses.add(urlList.get(0), rsp.getResponse());
      } catch (Exception e) {
        throw new SolrException(ErrorCode.SERVER_ERROR, urlList.get(0), e);
      }
    }

    long end = System.nanoTime();

    RouteResponse rr = condenseResponse(shardResponses, (long) ((end - start) / 1000000));
    rr.setRouteResponses(shardResponses);
    rr.setRoutes(routes);
    return rr;
  }