5 - Add Search Results

Adding Search Results with Raffle’s API.

The final step is implementing Search Results, which provide users with a detailed list of results based on their query. This feature is essential for delivering comprehensive answers and guiding users to relevant resources.

To perform searches using the Raffle API, you will use the /search endpoint. This endpoint requires three key parameters: uid, which identifies the search instance or tool; session-id, a unique identifier for the user session to group related searches, and query, which represents the search term provided by the user.

Additionally, the endpoint supports optional parameters like preview and device. Setting preview to true ensures the request is excluded from training data and analytics, making it ideal for testing. The device parameter allows you to specify the user’s device type, such as “desktop” or “mobile,” enabling tailored search experiences and ensuring the web app shows proper information about your users.

When implementing this endpoint, it is important to manage session IDs properly to maintain accurate data grouping and insights. During testing, always use the preview parameter to prevent test data from affecting live results. Accurately setting the device parameter can also enhance the search experience by accounting for device-specific behavior.

Search results returned by the /search endpoint include a feedback_data property. This property is specifically designed to be used when users interact with a result. By sending the feedback_data to the Raffle API using the /feedback endpoint, you enable Raffle to improve search relevance and accuracy over time. Additionally, this feedback mechanism provides valuable insights by tracking which results users find most useful, allowing for continuous optimization and better decision-making. You can find more information about the /feedback endpoint in our API Reference

API Call

Here’s the function to fetch search results from the Raffle API:

export const fetchSearchResults = async (query) => {
  const response = await fetch(
    `https://api.raffle.ai/v2/search?uid=$YOUR_RAFFLE_UID&query=${query}&session-id=${sessionId}&preview=${preview}&device=${device}`
  );
  const data = await response.json();
  return data.results;
};

This function sends a GET request to the search endpoint using the user’s query and returns a list of search results.

Integration with React

We’ll use React Query’s useMutation to fetch the search results dynamically when a search is triggered.

import { useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { fetchSearchResults } from "./api/api";

export const Search = () => {
  const [query, setQuery] = useState("");

  const {
    data: results = [],
    isPending: isLoadingResults,
    mutate: handleFetchResults,
  } = useMutation({
    mutationKey: ["search"],
    mutationFn: fetchSearchResults,
  });

  const handleSearch = () => {
    if (query.trim()) {
      handleFetchResults(query.trim());
    }
  };

  return (
    <div>
      <h1>Search API - React Example</h1>
      <div>
        <input
          type="text"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Enter your query..."
        />
        <button onClick={handleSearch}>Search</button>
      </div>

      <div>
        <h2>Search Results</h2>
        {isLoadingResults ? (
          <p>Loading...</p>
        ) : results.length > 0 ? (
          <ul>
            {results.map((result) => (
              <li key={result.url}>
                <a href={result.url} target="_blank" rel="noopener noreferrer">
                  <h3>{result.title}</h3>
                </a>
                <p>{result.content}</p>
              </li>
            ))}
          </ul>
        ) : (
          <p>No results available for this query.</p>
        )}
      </div>
    </div>
  );
};

Explanation

  1. fetchSearchResults: Fetches detailed search results for a given query from the API.
  2. useMutation: Dynamically fetches search results when a search is triggered.
  3. Loading State: Displays a loading indicator while the results are being fetched.
  4. Rendering Results: Displays a list of results with titles, descriptions, and clickable links.

This step completes the search flow by adding the core functionality of displaying comprehensive search results.

Total Code

At this stage, your full code should look like this:

import { useEffect, useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useDebounce } from "@uidotdev/usehooks";
import {
  fetchSearchResults,
  fetchSuggestions,
  fetchSummary,
  fetchTopQuestions,
} from "./api";

export const Search = () => {
  const [query, setQuery] = useState("");
  const debouncedQuery = useDebounce(query, 500); // Debounce with 500ms delay

  const { data: topQuestions = [], isLoading } = useQuery({
    queryKey: ["topQuestions"],
    queryFn: fetchTopQuestions,
  });

  const { data: suggestions = [], mutate: handleFetchSuggestions } =
    useMutation({
      mutationKey: ["suggestions", debouncedQuery],
      mutationFn: fetchSuggestions,
    });

  const {
    data: summary,
    isPending: isLoadingSummary,
    mutate: handleFetchSummary,
  } = useMutation({
    mutationKey: ["summary"],
    mutationFn: fetchSummary,
  });

  const {
    data: results = [],
    isPending: isLoadingResults,
    mutate: handleFetchResults,
  } = useMutation({
    mutationKey: ["search"],
    mutationFn: fetchSearchResults,
  });

  // Trigger suggestions when debouncedQuery is at least 3 characters long
  useEffect(() => {
    if (debouncedQuery.length >= 3) {
      handleFetchSuggestions(debouncedQuery);
    }
  }, [debouncedQuery, handleFetchSuggestions]);

  const handleSearch = () => {
    if (query.trim()) {
      handleFetchSummary(query.trim());
      handleFetchResults(query.trim());
    }
  };

  return (
    <div>
      <h1>Search API - React Example</h1>
      <div>
        <input
          type="text"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Enter your query..."
        />
        <button onClick={handleSearch}>Search</button>
      </div>

      <div>
        <h2>Top Questions</h2>
        {isLoading ? (
          <p>Loading top questions...</p>
        ) : (
          <ul>
            {topQuestions.map(({ question }) => (
              <li key={question} onClick={() => setQuery(question)}>
                {question}
              </li>
            ))}
          </ul>
        )}
      </div>

      <div>
        <h2>Suggestions</h2>
        <ul>
          {suggestions.map(({ suggestion }, index) => (
            <li
              key={index}
              onClick={() => {
                setQuery(suggestion);
                handleSearch();
              }}
            >
              {suggestion}
            </li>
          ))}
        </ul>
      </div>

      <div>
        <h2>Summary</h2>
        {isLoadingSummary ? (
          <p>Loading...</p>
        ) : summary ? (
          <div>
            <div dangerouslySetInnerHTML={{ __html: summary.summary }} />
            {summary.references.length > 0 && (
              <div>
                <h3>References</h3>
                <ul>
                  {summary.references.map((ref) => (
                    <li key={ref.url}>
                      <a
                        href={ref.url}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        {ref.title}
                      </a>
                    </li>
                  ))}
                </ul>
              </div>
            )}
          </div>
        ) : (
          <p>No summary available for this query.</p>
        )}
      </div>

      <div>
        <h2>Search Results</h2>
        {isLoadingResults ? (
          <p>Loading...</p>
        ) : results.length > 0 ? (
          <ul>
            {results.map((result) => (
              <li key={result.url}>
                <a href={result.url} target="_blank" rel="noopener noreferrer">
                  <h3>{result.title}</h3>
                </a>
                <p>{result.content}</p>
              </li>
            ))}
          </ul>
        ) : (
          <p>No results available for this query.</p>
        )}
      </div>
    </div>
  );
};