0

We have a view that displays indexed nodes and we would like to add the ability to filter by proximity to a user-input zip code.

I know this can be achieved with Solr and the Search API Location module (https://www.drupal.org/project/search_api_location) but we are not using Solr.

Is there some other way to implement a proximity search on indexed nodes?

  • have you a DB server?, "it's the same" as solr server, settings are similar – pinueve Jun 21 '17 at 20:44
  • "Currently only Apache Solr is supported as the service class." - going go out on a limb and assume because of Solr's support for geolocation based field and searching based on that. Search API DB probably won't get the job done. – Kevin Jun 21 '17 at 22:06
  • @pinueve we're using Search API DB – ssteigen Jun 23 '17 at 12:39
0

In order to solve this issue, I combined the results of two views.

  1. The Primary View
    • uses the search index and displays indexed nodes
    • has the correct search facet exposed filters
    • can't perform a proximity search (without Solr & Search API Location)
  2. The Secondary View
    • uses the node content and displays non-indexed nodes
    • can't use search facets for the exposed filters because the nodes are not indexed
    • can perform a proximity search with just Geofield, GeoPHP, and GeoCoder (all of which we already had installed)

To create the secondary view, I followed the steps outlined here.

Then I used hook_search_api_views_query_alter() to alter the query for the primary view. In this hook, I retrieve the secondary view, pass in the zip code from the exposed filter in the primary view, and execute the secondary view to get the results. Then I turn those results into an array of nids.

Finally, I add a condition to the views query to filter out all nodes that do not have an nid from that array.

/**
 * Implements hook_search_api_views_query_alter().
 */
function mymodule_search_api_views_query_alter(view &$view, SearchApiViewsQuery &$query) {
  if ($view->name === 'my_primary_view') {

    // Try to get the zip code from the exposed filter.
    $zip_code = NULL;
    if (isset($view->exposed_input['field_geo_latlon'])) {
      $zip_code = $view->exposed_input['field_geo_latlon'];
      unset($view->exposed_raw_input['field_geo_latlon']);
      unset($view->exposed_input['field_geo_latlon']);
      unset($view->exposed_data['field_geo_latlon']);
    }

    // Get the second view, pass in the zip code, get the results.
    $proximity_view = views_get_view('my_secondary_view');
    if (isset($zip_code)) {
      $proximity_view->exposed_input['field_geofield_distance'] = array(
        'distance' => '100',
        'unit' => '3959',
        'origin' => $zip_code,
      );
    }
    $proximity_view->init_display();
    $proximity_view->pre_execute();
    $proximity_view->execute();

    // Turn the results into an array of nids.
    $proximity_nids = array();
    foreach ($proximity_view->result as $proximity_result) {
      $proximity_nids[] = $proximity_result->nid;
    }

    // Add that condition to the view query.
    $view->query->condition('nid', $proximity_nids, 'IN');
  }
}

Note: Initially, the dysfunctional zip code filter from the primary view was filtering out all results when a zip code was entered by a user. I needed to keep this field so that users could still enter their zip code, but I did not want the filter to actually be applied.

To get around this, I edited the settings on the exposed zip code filter in the primary view so that when a zip code is provided, it returns all nodes where the zip code is not empty.

This ensures that entering a zip code into the primary view won't change the result set (technically it will remove results without zip codes, but that's ok) but will still allow us to retrieve the value from the input and pass it to the secondary view for the "real" proximity filter.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.