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.
You will use the /search endpoint to perform searches using the Raffle API. This endpoint requires three key parameters:
/search
uid
session-id
query
Additionally, the endpoint supports optional parameters like preview and device.
preview
device
Note: 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 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 designed to be used when users interact with a result. Therefore, you need to send the feedback_data to the Raffle API using the /feedback endpoint, to help Raffle improve search relevance and accuracy over time. Additionally, this feedback property provides valuable insights by tracking which results users find most useful, allowing for continuous optimization and better decision-making.
feedback_data
/feedback
Note: The search implementation will work without you sending the feedback_data property to the /feedback. However, it is essential to the accurate functionality of the “Search Inquiries”. If feedback_data is not used, then the “Click-Through Rate” will remain 0 as not all the required information is available for calculations.
You can find more information about the /feedback endpoint in our API Reference
Here’s the function to fetch search results from the Raffle API. This function sends a GET request to the search endpoint using the user’s query and returns a list of search results.
GET
search
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; };
Here is the POST request to the feedback endpoint
POST
feedback
export const sendFeedback = async (feedback_data) => { const response = await fetch('https://api.raffle.ai/v2/feedback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'click', feedback_data }), }) const data = response.json(); return data; }
We’ll use React Query’s useMutation to fetch the search results dynamically when a search is triggered.
useMutation
import { useState } from "react"; import { useMutation } from "@tanstack/react-query"; import { fetchSearchResults, sendFeedback } from "./api/api"; export const Search = () => { const [query, setQuery] = useState(""); // Mutation for fetching search results const { data: results = [], isPending: isLoadingResults, mutate: handleFetchResults, } = useMutation({ mutationKey: ["search"], mutationFn: fetchSearchResults, }); // Mutation for sending feedback const { mutate: handleSendFeedback } = useMutation({ mutationKey: ["feedback"], mutationFn: sendFeedback, }); // Function to handle search submission 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" onClick={() => handleSendFeedback(result.feedback_data)}> <h3>{result.title}</h3> </a> <p>{result.content}</p> </li> ))} </ul> ) : ( <p>No results available for this query.</p> )} </div> </div> ); };
fetchSearchResults
This step completes the search flow by adding the core functionality of displaying comprehensive search results.
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, sendFeedback, fetchSuggestions, fetchSummary, fetchTopQuestions, } from "./api"; export const Search = () => { const [query, setQuery] = useState(""); const debouncedQuery = useDebounce(query, 500); // Debounce with 500ms delay // Fetch top questions const { data: topQuestions = [], isLoading } = useQuery({ queryKey: ["topQuestions"], queryFn: fetchTopQuestions, }); // Mutation for fetching suggestions const { data: suggestions = [], mutate: handleFetchSuggestions } = useMutation({ mutationKey: ["suggestions", debouncedQuery], mutationFn: fetchSuggestions, }); // Mutation for fetching summary const { data: summary, isPending: isLoadingSummary, mutate: handleFetchSummary, } = useMutation({ mutationKey: ["summary"], mutationFn: fetchSummary, }); // Mutation for fetching search results const { data: results = [], isPending: isLoadingResults, mutate: handleFetchResults, } = useMutation({ mutationKey: ["search"], mutationFn: fetchSearchResults, }); // Mutation for sending feedback const { mutate: handleSendFeedback } = useMutation({ mutationKey: ["feedback"], mutationFn: sendFeedback, }); // Trigger suggestions when debouncedQuery is at least 3 characters long useEffect(() => { if (debouncedQuery.length >= 3) { handleFetchSuggestions(debouncedQuery); } }, [debouncedQuery, handleFetchSuggestions]); // Handle search action const handleSearch = () => { if (query.trim()) { handleFetchSummary(query.trim()); handleFetchResults(query.trim()); } }; return ( <div> <h1>Search API - React Example</h1> {/* Search Input */} <div> <input type="text" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Enter your query..." /> <button onClick={handleSearch}>Search</button> </div> {/* Top Questions Section */} <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> {/* Suggestions Section */} <div> <h2>Suggestions</h2> <ul> {suggestions.map(({ suggestion }, index) => ( <li key={index} onClick={() => { setQuery(suggestion); handleSearch(); }} > {suggestion} </li> ))} </ul> </div> {/* Summary Section */} <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> {/* Search Results Section */} <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" onClick={() => handleSendFeedback(result.feedback_data)}> <h3>{result.title}</h3> </a> <p>{result.content}</p> </li> ))} </ul> ) : ( <p>No results available for this query.</p> )} </div> </div> ); };
Search Results Customization