In order to get this module to work properly, we need to extend Search API's query.inc to provide a custom function executeClean() in order to do min/max value searches.

Currently it is beyond my comprehension how to properly extend the class and call the custom function. Who can enlighten me?

Comments

Anonymous’s picture

Priority: Normal » Critical
drunken monkey’s picture

I don't really understand a) the purpose of the method (i.e., why you can't just use the normal execute()), and b) why you need it to be a method.

Regarding a) I tried to look up the code to see what it does, but includes/query.inc only contains a few class properties copied from the normal query class, no methods. Maybe you accidentally removed the new part and kept the wrong one? Or am I just looking in the wrong place?

Regarding b): If you don't want to override an old method, but add a new one that only you would use — why make it a method at all? Since the class has no "real" protected properties (i.e., none without an accessor method), this should be perfectly possible to implement as a normal function in your module.
Also, the use case is far from clear for me. (If you don't really need the method, as stated above, it doesn't matter, though.)

Anonymous’s picture

Hi, sorry I accidentally left that query.inc empty. The point is to be able to do a custom search query without drupal_alter() and without search_api_current_search(). To find the slider's min/max values, I need to get unaltered "pure" results and these results may not be stored in the Search API system.

So I needed to run a $query->executeClean() like this:

  public final function executeClean() {
    $start = microtime(TRUE);

    // Add filter for languages.
    if (isset($this->options['languages'])) {
      $this->addLanguages($this->options['languages']);
    }

    // Add fulltext fields, unless set
    if ($this->fields === NULL) {
      $this->fields = $this->index->getFulltextFields();
    }

    // Call pre-execute hook.
    $this->preExecute();

    // Preprocess query.
    $this->index->preprocessSearchQuery($this);

    $pre_search = microtime(TRUE);

    // Execute query.
    $response = $this->index->server()->search($this);

    $post_search = microtime(TRUE);

    // Call post-execute hook.
    $this->postExecute($response);

    // Postprocess results.
    $this->index->postprocessSearchResults($response, $this);

    $end = microtime(TRUE);
    $response['performance']['complete'] = $end - $start;
    $response['performance']['hooks'] = $response['performance']['complete'] - ($post_search - $pre_search);

    return $response;
  } 

I removed two lines from execute(():

1. drupal_alter('search_api_query', $this);
Because I need to do a "pure" unalterable query to find min/max values. Otherwise I would end up altering my own min/max query with my search_api_ranges_search_api_query_alter(), but this cannot happen. The min/max queries need to be on a separate track, uninfluenced by anything else.

2. search_api_current_search(NULL, $this, $response);
This makes Search API believe there are multiple searches on the same page, which leads to unwanted behavior. The min/max queries should be considered "stand-alone", not influencing Search API.

Anonymous’s picture

You can see what I'm doing in search_api_ranges_minmax_execute(). It looks up the lowest and highest value for a given field, given the current search's keys and filters.

drunken monkey’s picture

OK, I guess that makes sense. However, I'm sure there are other ways to prevent you from altering your own query, and removing the drupal_alter() call might lead to false min/max results for facetted searches.

Anyways: as said, just doing this in a function instead of a method on the search query is probably the easiest way to do this. If it's absolutely essential that it's a method, then just replace the search_api_query() call with new SearchApiRangesQuery($index, $options).

Anonymous’s picture

Aha, of course I should just add the function to search_api_ranges.module. I didn't think of that :) Good point about the drupal_alter actually. I'll find another way, like adding a $option['skip_ranges_alter'] = 1 or something like that.

Anonymous’s picture

The custom function requires quite a re-write of the original execute() function. I'm trying this now:

function search_api_ranges_minmax_execute(SearchApiQuery $query) {
    $start = microtime(TRUE);

    // Add filter for languages.
    $languages = $query->getOption('languages');
    if (isset($languages)) {
      $query->addLanguages($languages);
    }

    // Add fulltext fields, unless set
    if ($query->getFields() === NULL) {
      $query->fields($query->getIndex()->getFulltextFields());
    }

    // Call pre-execute hook.
    $query->preExecute();

    // Preprocess query.
    $query->getIndex()->preprocessSearchQuery($query);
    
    // Let modules alter the query.
    drupal_alter('search_api_query', $query);

    $pre_search = microtime(TRUE);

    // Execute query.
    $response = $query->getIndex()->server()->search($query);

    $post_search = microtime(TRUE);

    // Call post-execute hook.
    $query->postExecute($response);

    // Postprocess results.
    $query->getIndex()->postprocessSearchResults($response, $query);

    $end = microtime(TRUE);
    $response['performance']['complete'] = $end - $start;
    $response['performance']['hooks'] = $response['performance']['complete'] - ($post_search - $pre_search);

    return $response;
} 

But no luck:
Fatal error: Call to protected method SearchApiQuery::preExecute() from context ''

What would the right approach be?

drunken monkey’s picture

Ah, yes, the preExecute() and postExecute() methods are protected, didn't think of that. However, as long as you aren't dealing with an overridden query class, those methods will be empty anyways. It should therefore be relatively harmless, in most cases, to just omit these calls.

Anonymous’s picture

Status: Active » Fixed

Thanks, we'll try that as long as it works. Just committed this.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.