Extra or Expanded Results

I would like to have my first search to always have at least 30 results showing. If the given query+refinements return less than 30 results, then I want to run an ‘expanded search’ which has less search criteria to add more results.

Example:
Search: query: '', facets: type = 'Furniture', subtype = 'Chairs'
Search Results: [chair1] [chair2] … [chair15]
You may also be interested in: [furniture1] [furniture2] … [furniture15]

Where the expanded search query would be:
query: '', facets: type = 'Furniture'

Here is what I’ve tried to make this work:

// Main Search
const search = instantsearch({
  appId: 'myAppId',
  apiKey: 'myApiKey',
  indexName: 'myIndex',
  searchFunction: function(helper) {
    // triggered once the results are returned
    search.helper.once('result', function() {
      // Expanded results
      if(expandedCondition === true) searchExpanded.helper.search();
    });

    helper.search();
  }
});

// Expanded Search
const searchExpanded = instantsearch({
  appId: 'myAppId',
  apiKey: 'myApiKey',
  indexName: 'myIndex',
  searchFunction: function(helper) {
    // triggered once the results are returned
    searchExpanded.helper.once('result', function() {
      console.log("done");
      // console.log(searchExpanded.helper.lastResults.nbHits); // Line 26
      // expandSearchCriteria(helper);
      // helper.search();
    });

  if(expandedCondition === true) {
    // while(totalResults < 30) {
      expandSearchCriteria(helper); // remove a refinement to increase result count
      helper.search();
    // }
    }
  }
});

So the idea is that if expandedCondition is true, then an expanded search is made to add more results under the “Expanded” header <div>. If that still isn’t enough results, then yet another filter is removed and the expanded search is run again. And so on until we have 30+ total (regular + expanded) results.

The Issue: You’ll notice that in the searchFunction for searchExpanded, I have line 26 commented out. Something strange happening here: if the code is run as is, then the console will display done once. However, if I then uncomment line 26, then it is run two times. Normal:

// triggered once the results are returned
searchExpanded.helper.once('result', function() {
	console.log("done");
});
/* output: 
done

This is fine and makes sense to me.
Uncommented:

// triggered once the results are returned
searchExpanded.helper.once('result', function() {
	console.log("done");
	console.log(searchExpanded.helper.lastResults.nbHits); // Line 26
});
/* output: 
done
14
done
14

Why is it running a second time? This would result in the search criteria being expanded twice, not just once.

The code works for one iteration. It will search, and if there are less than 30 search results then it will do an expanded search to find more. However, if the new total results is still less than 30, then it will not run a second time.

You may also notice that I have a while-loop in the searchFunction. This is where I tried to make it repeat the searches. However, I need each iteration of the loop to be run after the results are returned, since I need to know how many results were returned to determine if the loop should run again or not. This is how I ran into the problem of the code running twice.

I know this is a lot, so please let me know if I should clarify anything.

Hi @kevin2,

I’m glad we could answer you directly. Just reposting the advice here for the community:

It sounds like you want to perform some conditional logic once your receive search results back in the browser, after a query is made. In this case, instead of trying to build this into the a custom search function as you currently are, I’d consider using a custom widget: https://community.algolia.com/instantsearch.js/v2/guides/customization.html

When defining a custom widgets, you can leverage the “render” hook to perform logic when results are returned: https://community.algolia.com/instantsearch.js/v2/guides/custom-widget.html#creating-new-widgets

I think this would be a good way to architect a solution that checks if at least 30 hits were returned for a given query, then respond accordingly.

1 Like

Thanks for the response. I did a bit of both - used the custom search function, as well as creating a widget - and my result seems to be working. Thank you for the help. I am open to suggestions and improvements. :slight_smile:

It isn’t much, but maybe it will be helpful to someone in the future. What I did looks something like this:

const search = instantsearch({ ... as normal ... });

const searchExpanded = instantsearch({
  ... appId, apiKey, indexName ...
  searchFunction: function(helper) {
    if(expandedCondition === true) { // expanded results are required
      // Get the same filters as the main search
      helper.setQueryParameter('query', search.helper.getQueryParameter('query'));
      // add disjunctive and numeric refinements the same way
      helper.search();
    }
  }
});
searchExpanded.addWidget(expandedManager);

And the widget code:

const expandedManager = {
  render: function(renderOptions) {
    // renderOptions contains four keys:
    //   - results: the results from the last request
    //   - helper: to modify the search state and propagate the user interaction
    //   - state: the state at this point
    //   - createURL: if the url sync is active, will make it possible to create new URLs
    const results = renderOptions.results;
    const helper = renderOptions.helper;

    // If not enough results were returned, expand the search and try again
    // `expandedSearchCount` is set in the main code
    if(results.nbHits < expandedSearchCount) {
      this.expandSearchCriteria(helper);
      helper.search();
    }
  },

  //Removes the 'top level' filter(s), broadening the search criteria to allow for an 'expanded search.'
  expandSearchCriteria: function(helper) { ... expanded criteria code here ... }
}
1 Like