backdrop-contrib / search_api

Provides a generic API for modules offering search capabilities
GNU General Public License v2.0
0 stars 6 forks source link

current_search is getting the wrong search #56

Open danksearle opened 1 year ago

danksearle commented 1 year ago

I have a page that presents a search interface to the user. There are facets, the results are shown, and since our search is split into 7 main types of search, there are 7 links to go to those searches, one of which will be the search you are viewing right now. We have 4 search indexes. So of those 7 links, the first 4 are for differently filtered searches for the 1st index, and the other 3 are for the other three indexes. Imagine I have indexes A, B, C and D. A has four variants, so the links are:

I hope that makes some sense.

What I want to do is to show the counts of results that you would get for each one, e.g.

So to do that I am calling search_api_query().

For each one I pass the search index of course, A,B, C or D, and I pass a unique "search id".

That works fine.

However, the problem is that it affects the counts that I see on the facets shown in that page. E.g. beneath that list of 7 top items, I have the facets, e.g. a list of checkboxes with counts next to each one.

The facet options counts get it wrong when I view one of the first 4 searches, A.1 - A.4. Because they are all for the same search index. The facet options counts shown are the ones calculated for A.4 - the last search for that search index to be done.

Here's an example of the sort of facets we have, with counts showing: image

In this file: modules/search_api/contrib/search_api_facetapi/plugins/facetapi/adapter.inc

  public function getCurrentSearch() {
    // Even if this fails once, there might be a search query later in the page
    // request. We therefore don't store anything in $this->current_search in
    // case of failure, but just try again if the method is called again.
    if (!isset($this->current_search)) {
      $index_id = $this->info['instance'];
      // There is currently no way to configure the "current search" block to
      // show on a per-searcher basis as we do with the facets. Therefore we
      // cannot match it up to the correct "current search".
      // I suspect that http://drupal.org/node/593658 would help.
      // For now, just taking the first current search for this index. :-/
      foreach (search_api_current_search() as $search) {
        list($query) = $search;
        if ($query->getIndex()->machine_name == $index_id) {
          $this->current_search = $search;
        }
      }
    }
    return $this->current_search;
  }

I think this is where the diffculty is. Each time I call search_api_query() I give a unique 'search id' in the $options array, but the top four all use the same search index, so that is the same for them.

argiepiano commented 1 year ago

Can you clarify what you mean by "4 variants of index A"? How is that looking in the UI at admin/config/search/search_api?

danksearle commented 1 year ago

Sorry, that was rather abstract. Here's a made up list (I don't want to reveal the inner workings of our private intranet) that is not the truth but I think it's close enough to help you understand.

This is what we have working now in DRUPAL 7: image

And this is as close as we got in BACKDROP (you can see the counts are missing): image

The first four in my mockup: Closed projects; Open projects; Projects where help is requested; Inactive projects, are all based on data in a single search index. The other three, Leads; Posts & library and People each have their own search indexes.

In the UI, our users feel that we have a single search page where you see this. That's not true - we have 7 search pages, because each of those project links goes to a different URL, where a different view and display is loaded, each with a different layout - but to the user they all look the same.

In the Admin UI, it shows these indexes (I changed the names):

image

danksearle commented 1 year ago

You should know that our content types have one type for Closed projects, and another that is for Open, with help requested and also Inactive. There are a huge number of fields on these content types, it's not practical currently to bring them into a single type. The fields for Closed have more differences than similarities with the other type.

argiepiano commented 1 year ago

Thanks. Now please tell me how you are producing those list both in D7 and Backdrop. I assume the list you posted through that snapshot is not a view, since you say:

So to do that I am calling search_api_query().

Are you using a custom module to create that output?

danksearle commented 1 year ago

yes, there is much custom code here. Both in D7 and Backdrop.

Those 7 links are all created by custom code, not a view. Part of that process is to calculate the number to show as the count.

The actual query that is sent to SOLR is affected by the user's chocies in the facets, and also by custom code in an implementation of hook_search_api_solr_query_alter.

argiepiano commented 1 year ago

This seems like a complex problem to solve, and given that this is done with custom code, I'm not sure I can help much. Perhaps if you post the essential parts of your custom code? And also, how did you overcome this limitation in D7? Is search_api_facets substantially different in the D7 environment?

From what I understand, the problem probably boils down to this:

  1. Each search index has a unique machine name (they are stored as entities in the database)
  2. You are doing 4 query "variations" for the same search index in the same page request
  3. Search API stores each search information statically as elements in an indexed static array with $query and $results as the two sub-elements in each element of that static array (see search_api_current_search())
  4. That static array is parsed in SearchApiFacetapiAdapter::getCurrentSearch() which is probably used by Facet Api invoking its methods getSearchKeys() and/or getResultCount() , getSearchPath() . The parsing of that static array is done in a pretty limited way, but matching the machine name in the $query variable, to the machine name of the index entity. The matching doesn't account for the possibility that there are more than one statically-stored queries with the same index machine name, and therefore returns the very last one found in the static array
  5. It looks like Facet API is calling those methods to populate its tokens, and to build its "Current search" block.

So, back to how you are displaying the counts - are you relying on a Facet API token to do that?

Without fully understanding the code, I could think of two solutions:

  1. Create separate indexes for each of the 4 "variations". The indexes could be exactly the same, but the fact that you have 4 "index entities" will result in getCurrentSearch returning the correct search. It's inefficient, but it may be the easiest way to do this.
  2. OR, figure out if there is another way to identify the "variations" by looking at the information stored in the $query in the static variable. If so, then perhaps defining a new FAcet API adapter with hook_facetapi_adapters() that uses an extension of SearchApiFacetapiAdapter that overrides getCurrentSearch() and getResultCount() to account for the new way of finding the correct variation. And then (if you are using tokens for the numbers), defining a new chained token that somehow passes the variation identifier to getResultCount().
danksearle commented 1 year ago

yes, I had come to your first suggestion too. It's a lot of work though, that index covers a huge list of fields and facets. I hadn't thought of your second one though, and that seems worth trying. I'm prioritising other things right now, so I won't do this until next week I think. I'll update this with the results. Thanks for the idea!