Autocomplete: How do I debounce only 1 query, then use the items in multiple Sources?

Hi,

The aim is to have:

  • only 1 debounced (remote) query, and
  • multiple Sources (but each one receiving a subset of the items returned by the 1 query)

How do I debounce only this 1 query, in the below code?

import { autocomplete } from '@algolia/autocomplete-js';

// The generic debounce function
function debouncePromise(fn, time) {
  let timerId = undefined;

  return function debounced(...args) {
    if (timerId) {
      clearTimeout(timerId);
    }

    return new Promise((resolve) => {
      timerId = setTimeout(() => resolve(fn(...args)), time);
    });
  };
}
const debounced = debouncePromise((items) => Promise.resolve(items), 300);


// HOW DO I DEBOUNCE ONLY THIS 1 QUERY, THEN USE THE RETURNED `items` in `getSources()` BELOW ?
const items = fetch(`https://example.org/search/?q=${query}`)
  .then((response) => response.json())
  .then(({ results }) => results)
  .catch(() => []);


autocomplete({
  container: '#autocomplete',
  getSources({ query }) {
    return [
      {
        sourceId: 'items_part_1',
        getItems() {
          return items.filter(({ name }) =>
            name.includes("_part_1")
          )
        },

        sourceId: 'items_part_2',
        getItems() {
          return items.filter(({ name }) =>
            name.includes("_part_2")
          )
        },

        sourceId: 'items_part_3',
        getItems() {
          return items.filter(({ name }) =>
            name.includes("_part_3")
          )
        }
      }
    ];
  }
});

Interesting use case – if I understand it, rather than using the query directly for your autocomplete sources, you want to transform the input into a complex object using a single API call, then run multiple sub-queries using different pieces of that initial response, (rather than the original query), for the various sources within your autocomplete experience.

I feel like this level of disconnect between the initial query being typed and the result set may be disconcerting to your end user.

That said, I bet you could probably get something like this working by storing the response returned from the initial query in some sort of state management system. Then you could grab the parts you need within the other sources using the query as a key into your store to retrieve the various parts for filtering.

Again though, it feels like this could be an odd experience for the end user though, since the results aren’t based directly on what they typed.

you want to transform the input into a complex object using a single API call

Not quite.

The idea is simply to send the input (query string) with a single API call, then get back a “complex” results object of which only a part is used/displayed by each “Source”.

BUT: the query calls must be debounced, as one types (not every key stroke should send an API call).

For now, I have the below attempt, but the query calls are not debounced, they are sent out as one types (only the result list is “debounced”, or: “displayed with a delay”):

autocomplete({
  // …
  getSources() {

    // The debounced() like this does not debounce the queries, but only displays the "items" list with the delay...
    return debounced(fetch('…').then(({ items }) => {

      return [
        {
          sourceId: 'source1',
          getItems() {
            return items.filter(/* … */);
          },
        },
        {
          sourceId: 'source2',
          getItems() {
            return items.filter(/* … */);
          },
        },
      ];
      
    }));

  },
});