Hybrid Search — Events

Run your own code when a search goes out, when its results and its answer show, and when the reader rates the answer.

Hybrid Search runs two parts, the search results and the answer, and each part emits its own events. Below we listen to both, list the events each part goes through, and then look at the data event, which tracks the responses themselves.

You need a working Hybrid Search page first. See Hybrid Search — Quick Start.

Listen to an event

Call on with the event name and a function. The search results emit on client.ui.hybridSearch, and the answer emits on client.ui.hybridSearch.answer:

const workflow = client.ui.hybridSearch;

workflow.on("done", () => {
  console.log("Results shown");
});

workflow.answer.on("done", () => {
  console.log("Answer done", workflow.questionId);
});

An event on one part does not reach the other. To know when the answer is done, listen on answer.

on returns a function. Call it to remove the listener:

const off = workflow.on("request", ({ payload }) => {
  console.log("Search", payload.q);
});

off();

Search results events

Each search goes through these events in order. Each one fires at most once for a search:

EventFires when
requestThe SDK sends a request to Miso. payload is the request body.
loadingThe request is out, and the SDK waits for the results.
readyThe results show.
doneThe results show. Hybrid Search emits it right after ready.
interruptA new search replaced this one before its results arrived.
errorThe search failed.
finallyThe search has ended, in any of the three ways above.

A search always ends with one of done, interrupt and error, and then with finally.

A new query starts a new search, and so does a change of facet or sort order. The events above then fire again. When the reader scrolls for more results, the search stays the same, so only request and data fire.

request carries type, which says why the SDK sent it:

typeSent when
queryThe reader searches.
refineThe reader changes a facet or the sort order.
moreThe reader scrolls for more results.

Apart from request, these events carry:

PropertyWhat it holds
sessionThe session of the search. A new search gets a new session.
statusloading, ready or erroneous.

Answer events

The answer starts with each new query. A change of facet or sort order keeps the answer as it is. Each answer goes through these events in order:

EventFires when
requestThe SDK asks Miso for the answer, after the search results arrive.
loadingThe SDK waits for the answer.
readyThe answer starts to show.
blocked

Since 1.13.0

Miso withheld the answer, for example at a metering limit. blockedReason says why.
doneThe full answer is on screen.
interruptA new query replaced this one before its answer finished.
errorThe answer failed.
finallyThe answer has ended, in any of the three ways above.

These events carry session, status and ongoing. ongoing is true while more of the answer is on the way.

ready and done follow what is on screen. done fires when the last word of the answer shows, which is after the last part of the response arrives. To act on the response as it arrives, use the data event.

The answer also emits answer-stage each time its progress label changes, with the label in answer_stage. Use it for display only. The labels change without notice, so do not branch on them.

Feedback event

feedback fires on the answer when the reader clicks Helpful or Not helpful:

const workflow = client.ui.hybridSearch;

workflow.answer.on("feedback", ({ value, unselected }) => {
  if (unselected) {
    return; // the reader cleared their choice
  }
  console.log(workflow.questionId, value);
});

value is helpful or unhelpful. A second click on the same button clears the choice. The event then carries unselected: true and no value.

The SDK sends the feedback to Miso by itself. Listen to this event only to send it somewhere else as well.

The data event

data fires each time the data of a part changes. Each part has its own data event, and each one carries the data object:

PropertyWhat it holds
sessionThe session of the search or the answer.
statusinitial, loading, ready or erroneous.
ongoingOn the answer, true while more of the response is on the way.
requestThe request that the SDK sent, with the body in request.payload.
valueThe response. See below for each part.
errorThe error, when status is erroneous.

The event shows the data after each data processor has run, so a field you changed there arrives changed here. The event cannot change the data. To change what Hybrid Search shows, use useDataProcessor.

Search results

On client.ui.hybridSearch, value is the Hybrid Search API response, with products, total and facet_counts:

client.ui.hybridSearch.on("data", ({ status, value }) => {
  if (status !== "ready") {
    return;
  }
  console.log(value.products.length, "of", value.total);
});

For one search, data fires with status: "initial", then loading, then ready. When the reader scrolls for more, it fires again with loading and then ready. value.products then holds every result loaded so far, the new page included.

Answer

On client.ui.hybridSearch.answer, value is the Answer API response, with answer, sources and the other fields:

client.ui.hybridSearch.answer.on("data", ({ status, ongoing, value }) => {
  if (status !== "ready" || ongoing) {
    return;
  }
  console.log(value.sources);
});

For one answer, data fires in this order:

  1. status: "initial" when the new query starts.
  2. status: "loading" while the search and then the answer are on the way.
  3. status: "ready" and ongoing: true for each part of the response. This repeats as the answer streams in.
  4. status: "ready" and ongoing: false when the response is complete.

data comes before the lifecycle events. The response is complete at ongoing: false, but the answer can still be typing out on screen until done.

If it does not work

SymptomCause
The answer emits nothingThe listener is on client.ui.hybridSearch. Set it on client.ui.hybridSearch.answer.
loading and done do not fire for more resultsScrolling for more stays in the same search. Listen to request or data.
value.answer is undefinedThe listener is on the search results. Set it on answer.
A listener runs many times for one answerIt listens to data, which fires for each part of the response. Use done, or check ongoing.
Nothing fires and an error shows in the consoleThe function throws. The SDK logs the error and goes on.

Next