/**
   * Executes a solr query for statistics
   *
   * @param searchParameters StatsParameters
   * @return SolrStatsResult
   */
  public SolrStatsResult executeStatsQuery(final StatsParameters searchParameters) {
    if (repositoryState.isBootstrapping()) {
      throw new AlfrescoRuntimeException(
          "SOLR stats queries can not be executed while the repository is bootstrapping");
    }

    try {
      StoreRef store = extractStoreRef(searchParameters);
      SolrStoreMappingWrapper mapping = extractMapping(store);
      Locale locale = extractLocale(searchParameters);

      Pair<HttpClient, String> httpClientAndBaseUrl = mapping.getHttpClientAndBaseUrl();
      HttpClient httpClient = httpClientAndBaseUrl.getFirst();
      String url = buildStatsUrl(searchParameters, httpClientAndBaseUrl.getSecond(), locale);
      JSONObject body =
          buildStatsBody(searchParameters, tenantService.getCurrentUserDomain(), locale);

      if (httpClient == null) {
        throw new AlfrescoRuntimeException("No http client for store " + store.toString());
      }

      return (SolrStatsResult)
          postSolrQuery(
              httpClient,
              url,
              body,
              new SolrJsonProcessor<SolrStatsResult>() {

                @Override
                public SolrStatsResult getResult(JSONObject json) {
                  return new SolrStatsResult(json, searchParameters.isDateSearch());
                }
              });

    } catch (UnsupportedEncodingException e) {
      throw new LuceneQueryParserException("stats", e);
    } catch (HttpException e) {
      throw new LuceneQueryParserException("stats", e);
    } catch (IOException e) {
      throw new LuceneQueryParserException("stats", e);
    } catch (JSONException e) {
      throw new LuceneQueryParserException("stats", e);
    }
  }
  public ResultSet executeQuery(final SearchParameters searchParameters, String language) {
    if (repositoryState.isBootstrapping()) {
      throw new AlfrescoRuntimeException(
          "SOLR queries can not be executed while the repository is bootstrapping");
    }

    try {
      StoreRef store = extractStoreRef(searchParameters);
      SolrStoreMappingWrapper mapping = extractMapping(store);
      Locale locale = extractLocale(searchParameters);

      URLCodec encoder = new URLCodec();
      StringBuilder url = new StringBuilder();

      Pair<HttpClient, String> httpClientAndBaseUrl = mapping.getHttpClientAndBaseUrl();
      HttpClient httpClient = httpClientAndBaseUrl.getFirst();

      url.append(httpClientAndBaseUrl.getSecond());

      String languageUrlFragment = extractLanguageFragment(language);
      url.append("/").append(languageUrlFragment);

      // Send the query in JSON only
      // url.append("?q=");
      // url.append(encoder.encode(searchParameters.getQuery(), "UTF-8"));
      url.append("?wt=").append(encoder.encode("json", "UTF-8"));
      url.append("&fl=").append(encoder.encode("DBID,score", "UTF-8"));

      if ((searchParameters.getStores().size() > 1) || (mapping.isSharded())) {
        boolean requiresSeparator = false;
        url.append("&shards=");
        for (StoreRef storeRef : searchParameters.getStores()) {
          SolrStoreMappingWrapper storeMapping = extractMapping(storeRef);

          if (requiresSeparator) {
            url.append(',');
          } else {
            requiresSeparator = true;
          }

          url.append(storeMapping.getShards());
        }
      }

      // Emulate old limiting behaviour and metadata
      final LimitBy limitBy;
      int maxResults = -1;
      if (searchParameters.getMaxItems() >= 0) {
        maxResults = searchParameters.getMaxItems();
        limitBy = LimitBy.FINAL_SIZE;
      } else if (searchParameters.getLimitBy() == LimitBy.FINAL_SIZE
          && searchParameters.getLimit() >= 0) {
        maxResults = searchParameters.getLimit();
        limitBy = LimitBy.FINAL_SIZE;
      } else {
        maxResults = searchParameters.getMaxPermissionChecks();
        if (maxResults < 0) {
          maxResults = maximumResultsFromUnlimitedQuery;
        }
        limitBy = LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS;
      }
      url.append("&rows=").append(String.valueOf(maxResults));

      url.append("&df=").append(encoder.encode(searchParameters.getDefaultFieldName(), "UTF-8"));
      url.append("&start=").append(encoder.encode("" + searchParameters.getSkipCount(), "UTF-8"));

      url.append("&locale=");
      url.append(encoder.encode(locale.toString(), "UTF-8"));
      url.append("&")
          .append(SearchParameters.ALTERNATIVE_DICTIONARY)
          .append("=")
          .append(alternativeDictionary);
      for (String paramName : searchParameters.getExtraParameters().keySet()) {
        url.append("&")
            .append(paramName)
            .append("=")
            .append(searchParameters.getExtraParameters().get(paramName));
      }
      StringBuffer sortBuffer = buildSortParameters(searchParameters, encoder);
      url.append(sortBuffer);

      if (searchParameters.getPermissionEvaluation() != PermissionEvaluationMode.NONE) {
        url.append("&fq=").append(encoder.encode("{!afts}AUTHORITY_FILTER_FROM_JSON", "UTF-8"));
      }

      if (searchParameters.getExcludeTenantFilter() == false) {
        url.append("&fq=").append(encoder.encode("{!afts}TENANT_FILTER_FROM_JSON", "UTF-8"));
      }

      if (searchParameters.getFieldFacets().size() > 0) {
        url.append("&facet=").append(encoder.encode("true", "UTF-8"));
        for (FieldFacet facet : searchParameters.getFieldFacets()) {
          url.append("&facet.field=").append(encoder.encode(facet.getField(), "UTF-8"));
          if (facet.getEnumMethodCacheMinDF() != 0) {
            url.append("&")
                .append(
                    encoder.encode("f." + facet.getField() + ".facet.enum.cache.minDf", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getEnumMethodCacheMinDF(), "UTF-8"));
          }
          url.append("&")
              .append(encoder.encode("f." + facet.getField() + ".facet.limit", "UTF-8"))
              .append("=")
              .append(encoder.encode("" + facet.getLimit(), "UTF-8"));
          if (facet.getMethod() != null) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.method", "UTF-8"))
                .append("=")
                .append(
                    encoder.encode(
                        facet.getMethod() == FieldFacetMethod.ENUM ? "enum" : "fc", "UTF-8"));
          }
          if (facet.getMinCount() != 0) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.mincount", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getMinCount(), "UTF-8"));
          }
          if (facet.getOffset() != 0) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.offset", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getOffset(), "UTF-8"));
          }
          if (facet.getPrefix() != null) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.prefix", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getPrefix(), "UTF-8"));
          }
          if (facet.getSort() != null) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.sort", "UTF-8"))
                .append("=")
                .append(
                    encoder.encode(
                        facet.getSort() == FieldFacetSort.COUNT ? "count" : "index", "UTF-8"));
          }
        }
        for (String facetQuery : searchParameters.getFacetQueries()) {
          url.append("&facet.query=").append(encoder.encode("{!afts}" + facetQuery, "UTF-8"));
        }
      }
      // end of field facets

      final String searchTerm = searchParameters.getSearchTerm();
      String spellCheckQueryStr = null;
      if (searchTerm != null && searchParameters.isSpellCheck()) {
        StringBuilder builder = new StringBuilder();
        builder.append("&spellcheck.q=").append(encoder.encode(searchTerm, "UTF-8"));
        builder.append("&spellcheck=").append(encoder.encode("true", "UTF-8"));
        spellCheckQueryStr = builder.toString();
        url.append(spellCheckQueryStr);
      }

      JSONObject body = new JSONObject();
      body.put("query", searchParameters.getQuery());

      // Authorities go over as is - and tenant mangling and query building takes place on the SOLR
      // side

      Set<String> allAuthorisations = permissionService.getAuthorisations();
      boolean includeGroups =
          includeGroupsForRoleAdmin
              ? true
              : !allAuthorisations.contains(PermissionService.ADMINISTRATOR_AUTHORITY);

      JSONArray authorities = new JSONArray();
      for (String authority : allAuthorisations) {
        if (includeGroups) {
          authorities.put(authority);
        } else {
          if (AuthorityType.getAuthorityType(authority) != AuthorityType.GROUP) {
            authorities.put(authority);
          }
        }
      }
      body.put("authorities", authorities);
      body.put("anyDenyDenies", anyDenyDenies);

      JSONArray tenants = new JSONArray();
      tenants.put(tenantService.getCurrentUserDomain());
      body.put("tenants", tenants);

      JSONArray locales = new JSONArray();
      for (Locale currentLocale : searchParameters.getLocales()) {
        locales.put(DefaultTypeConverter.INSTANCE.convert(String.class, currentLocale));
      }
      if (locales.length() == 0) {
        locales.put(I18NUtil.getLocale());
      }
      body.put("locales", locales);

      JSONArray templates = new JSONArray();
      for (String templateName : searchParameters.getQueryTemplates().keySet()) {
        JSONObject template = new JSONObject();
        template.put("name", templateName);
        template.put("template", searchParameters.getQueryTemplates().get(templateName));
        templates.put(template);
      }
      body.put("templates", templates);

      JSONArray allAttributes = new JSONArray();
      for (String attribute : searchParameters.getAllAttributes()) {
        allAttributes.put(attribute);
      }
      body.put("allAttributes", allAttributes);

      body.put("defaultFTSOperator", searchParameters.getDefaultFTSOperator());
      body.put("defaultFTSFieldOperator", searchParameters.getDefaultFTSFieldOperator());
      body.put("queryConsistency", searchParameters.getQueryConsistency());
      if (searchParameters.getMlAnalaysisMode() != null) {
        body.put("mlAnalaysisMode", searchParameters.getMlAnalaysisMode().toString());
      }
      body.put("defaultNamespace", searchParameters.getNamespace());

      JSONArray textAttributes = new JSONArray();
      for (String attribute : searchParameters.getTextAttributes()) {
        textAttributes.put(attribute);
      }
      body.put("textAttributes", textAttributes);

      final int maximumResults = maxResults; // just needed for the final parameter

      return (ResultSet)
          postSolrQuery(
              httpClient,
              url.toString(),
              body,
              new SolrJsonProcessor<SolrJSONResultSet>() {

                @Override
                public SolrJSONResultSet getResult(JSONObject json) {
                  return new SolrJSONResultSet(
                      json, searchParameters, nodeService, nodeDAO, limitBy, maximumResults);
                }
              },
              spellCheckQueryStr);
    } catch (UnsupportedEncodingException e) {
      throw new LuceneQueryParserException("", e);
    } catch (HttpException e) {
      throw new LuceneQueryParserException("", e);
    } catch (IOException e) {
      throw new LuceneQueryParserException("", e);
    } catch (JSONException e) {
      throw new LuceneQueryParserException("", e);
    }
  }