5 - Add Search Results
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
fetchSearchResults
: Fetches detailed search results for a given query from the API.useMutation
: Dynamically fetches search results when a search is triggered.- Loading State: Displays a loading indicator while the results are being fetched.
- 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>
);
};