You are here

public function SolrProxyController::getResultsJson in Search API Federated Solr 8.3

Same name and namespace in other branches
  1. 8.2 src/Controller/SolrProxyController.php \Drupal\search_api_federated_solr\Controller\SolrProxyController::getResultsJson()
  2. 4.x src/Controller/SolrProxyController.php \Drupal\search_api_federated_solr\Controller\SolrProxyController::getResultsJson()

Uses the selected index server's backend connector to execute a select query on the index based on request qs params passed from the app.

Parameters

\Symfony\Component\HttpFoundation\Request $request:

Return value

\Drupal\Core\Cache\CacheableJsonResponse Structure mirrors the solr api response object written with the JSON Response Writer with the addition of a '#cache' key for cache metadata. @see https://lucene.apache.org/solr/guide/7_2/response-writers.html#json-resp...

1 string reference to 'SolrProxyController::getResultsJson'
search_api_federated_solr.routing.yml in ./search_api_federated_solr.routing.yml
search_api_federated_solr.routing.yml

File

src/Controller/SolrProxyController.php, line 24

Class

SolrProxyController

Namespace

Drupal\search_api_federated_solr\Controller

Code

public function getResultsJson(Request $request) {
  $data = [];

  // \Drupal\Core\Controller\ControllerBase::config loads config with overrides
  $config = $this
    ->config('search_api_federated_solr.search_app.settings');

  // Get index id from search app config.
  $index_id = $config
    ->get('index.id');

  // Get the server id from index config.
  $index_config = \Drupal::config('search_api.index.' . $index_id);
  $server_id = $index_config
    ->get('server');

  // Load the server.

  /** @var \Drupal\search_api\ServerInterface $server */
  $server = Server::load($server_id);

  // Get query data from route variables.
  $qs = $request
    ->getQueryString();

  // Parse the querystring, with support for multiple values for a key,
  // not using array[] syntax.
  // Can't use \Drupal\Core\Routing\RouteMatchInterface::getParameters()
  //   because the route doesn't / can't define qs params as parameters.
  // Can't use \Drupal\Component\Utility\UrlHelper::parse() because it uses
  //   str_parse which requires array brackets [] syntax for param keys with
  //   multiple values and that is not the syntax that solr expects.
  // @see: http://php.net/manual/en/function.parse-str.php#76792
  $params = Helpers::parseStrMultiple($qs);
  try {

    /** @var \Drupal\search_api_solr\SolrBackendInterface $backend */
    $backend = $server
      ->getBackend();

    /** @var \Drupal\search_api_solr\SolrConnectorInterface $connector */
    $connector = $backend
      ->getSolrConnector();

    // Create the select query.
    // Note: this proxy will only execute select queries.
    // @see: https://solarium.readthedocs.io/en/stable/queries/select-query/building-a-select-query/building-a-select-query/
    $query = $connector
      ->getSelectQuery();

    // Use supplied query fields if configured in settings.php.
    $query_fields_config = $config
      ->get('index.query_fields');

    // Default to passed in query fields, if there are any.
    $query_fields = is_array($query_fields_config) && !empty($query_fields_config) ? $query_fields_config : [
      'tm_rendered_item',
    ];

    // Determine if we should validate passed in query fields against the schema.
    $is_validate_query_fields = $config
      ->get('index.validate_query_fields');
    if ($is_validate_query_fields && is_array($query_fields_config) && !empty($query_fields_config)) {

      // Load the index.
      $indexes = $server
        ->getIndexes();

      /** @var \Drupal\search_api\IndexInterface $federated_search_index */
      $federated_search_index = $indexes[$index_id];

      // Get index field names mapped to their solr field name counterparts
      $backend_field_names_map = $backend
        ->getSolrFieldNames($federated_search_index);

      // Get all full text fields from the index.
      $full_text_fields = $federated_search_index
        ->getFulltextFields();

      // We can only search full text fields, so validate supplied field names.
      $full_text_query_fields = array_intersect($query_fields_config, $full_text_fields);

      // Filter the field names map by our query fields.
      $query_fields_map = array_intersect_key($backend_field_names_map, array_flip($full_text_query_fields));

      // Get the solr field name for our supplied full text query fields.
      $query_fields = array_values($query_fields_map);
    }

    // If there are any query fields, add them to the query.
    if (!empty($query_fields)) {

      // Get edismax query parser (used by the default request handler).
      $edismax = $query
        ->getEDisMax();

      // Set default query fields, overriding solr config.
      $edismax
        ->setQueryFields(implode(' ', $query_fields));
    }

    // Determine if we should add debug info to the proxy response object.
    $is_debug = $config
      ->get('proxy.debug');
    if ($is_debug) {
      $debug = $query
        ->getDebug();
    }

    // Determine if we have issued a site_name query, and filter it as
    // required by the site list settings. Note that if we set a default
    // site name value, it will be passed to the proxy as an 'fq' value.
    $ignore_default = FALSE;
    if (!empty($params) && is_array($params)) {

      // Account for strings passed by the query.
      if (isset($params['fq'])) {
        if (is_string($params['fq'])) {
          $params['fq'] = [
            $params['fq'],
          ];
        }
      }
      else {
        $params['fq'] = [];
      }
      foreach ($params['fq'] as $key => $value) {
        if (substr_count($value, 'sm_site_name') > 0) {
          $fq = urldecode($value);
          unset($params['fq'][$key]);
          $params['fq'][] = $fq;
          $ignore_default = TRUE;
        }
      }
    }

    // If site search is restricted, enforce it here.
    if (!$ignore_default) {

      // Get the list of allowed sites.
      if ($allowed_sites = $config
        ->get('facet.site_name.allowed_sites')) {
        $site_list = array_keys(array_filter($allowed_sites));
      }
      if (!empty($site_list)) {
        foreach ($site_list as $name) {
          $values[] = '"' . $name . '"';
        }
        $params['fq'][] = 'sm_site_name:(' . implode(' OR ', $values) . ')';
      }
    }

    // Set main query param.
    $q = is_array($params) && array_key_exists('q', $params) ? urldecode($params['q']) : '*';
    $query
      ->setQuery($q);

    // Set query conditions.
    $start = is_array($params) && array_key_exists('start', $params) ? $params['start'] : 0;
    $rows = is_array($params) && array_key_exists('rows', $params) ? $params['rows'] : 20;

    // Set query start + number of results.
    $query
      ->setStart($start)
      ->setRows($rows);

    // Set query sort, default to score (relevance).
    // Note: app only supports 1 sort at a time: date or score, desc
    $sort = is_array($params) && array_key_exists('sort', $params) ? urldecode($params['sort']) : 'score=desc';
    if ($sort_parts = explode("=", $sort)) {
      $query
        ->setSorts([
        $sort_parts[0] => $sort_parts[1],
      ]);
    }

    // Configure highlight component.
    $hl_field = array_key_exists('hl.fl', $params) ? $params['hl.fl'] : 'tm_rendered_item';
    $hl_use_phrase_highlighter = array_key_exists('hl.usePhraseHighlighter', $params) ? $params['hl.usePhraseHighlighter'] : TRUE;
    $hl = $query
      ->getHighlighting();
    $hl
      ->setFields($hl_field);
    $hl
      ->setSimplePrefix('<strong>');
    $hl
      ->setSimplePostfix('</strong>');
    $hl
      ->setUsePhraseHighlighter($hl_use_phrase_highlighter);

    // Configure FacetSet component.
    $facet_set = $query
      ->getFacetSet();

    // Set FacetSet limit + sort.
    $facet_limit = is_array($params) && array_key_exists('facet.limit', $params) ? $params['facet.limit'] : -1;
    $facet_sort = is_array($params) && array_key_exists('facet.sort', $params) ? $params['facet.sort'] : 'index';
    $facet_set
      ->setLimit($facet_limit);
    $facet_set
      ->setSort($facet_sort);

    // Create FacetSet fields.
    if (is_array($params) && array_key_exists('facet.field', $params) && is_array($params['facet.field'])) {
      foreach ($params['facet.field'] as $facet_field) {
        $facet_set
          ->createFacetField($facet_field)
          ->setField($facet_field);
      }
    }

    // Create Filter Queries.
    if (is_array($params) && array_key_exists('fq', $params)) {

      // When there is only 1 filter query, make it an array.
      if (!is_array($params['fq'])) {
        $fq = $params['fq'];
        $params['fq'] = [
          $fq,
        ];
      }

      // Write filter queries.
      foreach ($params['fq'] as $fq) {
        $fq = urldecode($fq);
        $parts = explode(':', $fq);

        // Sets a unique key for filter queries <facet.field>=<value> (required),
        // then sets query value <facet.field>:<value>
        $query
          ->createFilterQuery($parts[0] . '=' . $parts[1])
          ->setQuery($fq);
      }
    }

    // Fetch results.
    $query_response = $connector
      ->execute($query);
    $data = $query_response
      ->getData();
  } catch (SearchApiException $e) {
    watchdog_exception('search_api_federated_solr', $e, '%type while executed query on @server: @message in %function (line %line of %file).', array(
      '@server' => $server
        ->label(),
    ));
  }

  // Create json response with 200 response code.
  $response = new JsonResponse($data, 200);
  return $response;
}