Building Search Interfaces
Create intelligent search experiences using Infactory queries
Building Search Interfaces with Infactory
Infactory enables you to build powerful, natural language search interfaces that allow users to find information in your data using ordinary questions. Unlike traditional search solutions, Infactory-powered search interfaces can interpret user intent, extract parameters, and return precise, contextual results.
Why Infactory for Search Interfaces?
Traditional search solutions face several challenges:
Keyword Limitations
Traditional search only matches keywords, missing conceptual matches
Lack of Context
Cannot understand user intent or contextual meaning
Complex Implementation
Requires significant expertise in search technologies
Maintenance Overhead
Difficult to update and maintain as data evolves
Infactory search interfaces offer significant advantages:
- Natural Language Understanding: Users can search using conversational questions
- Precise Results: Returns exact answers, not just document links
- Contextual Awareness: Understands parameters and filters from questions
- Easy Implementation: Build advanced search with minimal code
- Consistency: Delivers reliable, accurate results at database speed
Search Interface Architecture Overview
Define your queries
Create and deploy the queries that will power your search interface.
Design search UI
Create a user-friendly search interface with an input field and results area.
Connect search to Infactory
Send user questions to Infactory’s API and handle the response.
Format results
Format the structured data results into a user-friendly display.
Implement refinement options
Add filters, sorting, or follow-up options to help users refine searches.
Implementation Approaches
Basic Search Interface
The simplest implementation provides a search box that sends questions to Infactory:
import { useState } from 'react';
function SearchInterface() {
const [query, setQuery] = useState('');
const [results, setResults] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
async function handleSearch(e) {
e.preventDefault();
if (!query.trim()) return;
setLoading(true);
setError(null);
try {
// Call your backend API that interfaces with Infactory
const response = await fetch('/api/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ question: query }),
});
if (!response.ok) {
throw new Error('Search failed');
}
const data = await response.json();
if (!data.data || data.data.length === 0) {
setError('No results found. Please try a different search.');
setResults(null);
} else {
setResults(data);
}
} catch (error) {
console.error('Error during search:', error);
setError('Failed to perform search. Please try again.');
setResults(null);
} finally {
setLoading(false);
}
}
// Function to render search results based on data type
function renderResults() {
if (!results || !results.data) return null;
// Check the type of data we received
const data = results.data;
// For tabular data, render a table
if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') {
return (
<div className="table-container">
<table className="results-table">
<thead>
<tr>
{Object.keys(data[0]).map(key => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
{Object.values(row).map((value, i) => (
<td key={i}>{typeof value === 'object' ? JSON.stringify(value) : value}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
// For simple scalar results
if (!Array.isArray(data)) {
return (
<div className="scalar-result">
<p className="result-value">{JSON.stringify(data)}</p>
</div>
);
}
// Fallback for other data types
return (
<div className="json-result">
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
return (
<div className="search-interface">
<h1>Data Search</h1>
<p className="description">
Ask any question about your data
</p>
<form onSubmit={handleSearch} className="search-form">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="e.g., 'What were total sales last month?' or 'How many users signed up yesterday?'"
disabled={loading}
className="search-input"
/>
<button type="submit" disabled={loading} className="search-button">
{loading ? 'Searching...' : 'Search'}
</button>
</form>
<div className="results-container">
{loading && <div className="loading">Searching...</div>}
{error && <div className="error">{error}</div>}
{results && (
<div className="search-results">
<div className="results-header">
<h2>Results</h2>
<div className="query-info">
<span>Query: <code>{results.query_used}</code></span>
<span>Execution time: <code>{results.execution_time_ms}ms</code></span>
</div>
</div>
{renderResults()}
</div>
)}
</div>
</div>
);
}
import { useState } from 'react';
function SearchInterface() {
const [query, setQuery] = useState('');
const [results, setResults] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
async function handleSearch(e) {
e.preventDefault();
if (!query.trim()) return;
setLoading(true);
setError(null);
try {
// Call your backend API that interfaces with Infactory
const response = await fetch('/api/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ question: query }),
});
if (!response.ok) {
throw new Error('Search failed');
}
const data = await response.json();
if (!data.data || data.data.length === 0) {
setError('No results found. Please try a different search.');
setResults(null);
} else {
setResults(data);
}
} catch (error) {
console.error('Error during search:', error);
setError('Failed to perform search. Please try again.');
setResults(null);
} finally {
setLoading(false);
}
}
// Function to render search results based on data type
function renderResults() {
if (!results || !results.data) return null;
// Check the type of data we received
const data = results.data;
// For tabular data, render a table
if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') {
return (
<div className="table-container">
<table className="results-table">
<thead>
<tr>
{Object.keys(data[0]).map(key => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
{Object.values(row).map((value, i) => (
<td key={i}>{typeof value === 'object' ? JSON.stringify(value) : value}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
// For simple scalar results
if (!Array.isArray(data)) {
return (
<div className="scalar-result">
<p className="result-value">{JSON.stringify(data)}</p>
</div>
);
}
// Fallback for other data types
return (
<div className="json-result">
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
return (
<div className="search-interface">
<h1>Data Search</h1>
<p className="description">
Ask any question about your data
</p>
<form onSubmit={handleSearch} className="search-form">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="e.g., 'What were total sales last month?' or 'How many users signed up yesterday?'"
disabled={loading}
className="search-input"
/>
<button type="submit" disabled={loading} className="search-button">
{loading ? 'Searching...' : 'Search'}
</button>
</form>
<div className="results-container">
{loading && <div className="loading">Searching...</div>}
{error && <div className="error">{error}</div>}
{results && (
<div className="search-results">
<div className="results-header">
<h2>Results</h2>
<div className="query-info">
<span>Query: <code>{results.query_used}</code></span>
<span>Execution time: <code>{results.execution_time_ms}ms</code></span>
</div>
</div>
{renderResults()}
</div>
)}
</div>
</div>
);
}
<template>
<div class="search-interface">
<h1>Data Search</h1>
<p class="description">
Ask any question about your data
</p>
<form @submit.prevent="handleSearch" class="search-form">
<input
type="text"
v-model="query"
placeholder="e.g., 'What were total sales last month?' or 'How many users signed up yesterday?'"
:disabled="loading"
class="search-input"
/>
<button type="submit" :disabled="loading" class="search-button">
{{ loading ? 'Searching...' : 'Search' }}
</button>
</form>
<div class="results-container">
<div v-if="loading" class="loading">Searching...</div>
<div v-if="error" class="error">{{ error }}</div>
<div v-if="results" class="search-results">
<div class="results-header">
<h2>Results</h2>
<div class="query-info">
<span>Query: <code>{{ results.query_used }}</code></span>
<span>Execution time: <code>{{ results.execution_time_ms }}ms</code></span>
</div>
</div>
<!-- Table results -->
<div v-if="isTabularData" class="table-container">
<table class="results-table">
<thead>
<tr>
<th v-for="(_, key) in results.data[0]" :key="key">{{ key }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in results.data" :key="index">
<td v-for="(value, key) in row" :key="key">
{{ typeof value === 'object' ? JSON.stringify(value) : value }}
</td>
</tr>
</tbody>
</table>
</div>
<!-- Scalar result -->
<div v-else-if="!Array.isArray(results.data)" class="scalar-result">
<p class="result-value">{{ JSON.stringify(results.data) }}</p>
</div>
<!-- Fallback for other data types -->
<div v-else class="json-result">
<pre>{{ JSON.stringify(results.data, null, 2) }}</pre>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SearchInterface',
data() {
return {
query: '',
results: null,
loading: false,
error: null
};
},
computed: {
isTabularData() {
return this.results &&
Array.isArray(this.results.data) &&
this.results.data.length > 0 &&
typeof this.results.data[0] === 'object';
}
},
methods: {
async handleSearch() {
if (!this.query.trim()) return;
this.loading = true;
this.error = null;
try {
// Call your backend API that interfaces with Infactory
const response = await fetch('/api/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ question: this.query }),
});
if (!response.ok) {
throw new Error('Search failed');
}
const data = await response.json();
if (!data.data || (Array.isArray(data.data) && data.data.length === 0)) {
this.error = 'No results found. Please try a different search.';
this.results = null;
} else {
this.results = data;
}
} catch (error) {
console.error('Error during search:', error);
this.error = 'Failed to perform search. Please try again.';
this.results = null;
} finally {
this.loading = false;
}
}
}
};
</script>
Backend Implementation
Your backend needs to proxy requests to Infactory:
// Node.js/Express backend
const express = require('express');
const axios = require('axios');
const router = express.Router();
// Environment variables
const INFACTORY_API_KEY = process.env.INFACTORY_API_KEY;
const INFACTORY_PROJECT_ID = process.env.INFACTORY_PROJECT_ID;
// Search endpoint
router.post('/api/search', async (req, res) => {
try {
const { question } = req.body;
if (!question) {
return res.status(400).json({ error: 'Question is required' });
}
// Call Infactory's unified query endpoint
const response = await axios.post(
'https://api.infactory.ai/v1/query',
{
query: question,
project_id: INFACTORY_PROJECT_ID
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${INFACTORY_API_KEY}`
}
}
);
res.json(response.data);
} catch (error) {
console.error('Error during search:', error.response?.data || error.message);
// Extract specific error information if available
let errorMessage = 'Failed to process your search';
if (error.response?.data?.error) {
if (error.response.data.error === 'no_matching_query') {
errorMessage = 'No query available to answer this question';
} else if (error.response.data.error === 'parameter_extraction_failed') {
errorMessage = 'Unable to understand all parts of your question';
} else {
errorMessage = error.response.data.error;
}
}
res.status(500).json({
error: errorMessage
});
}
});
module.exports = router;
Enhanced Search Experience
Search Suggestions
Add search suggestions to help users formulate effective questions:
function SearchWithSuggestions() {
const [query, setQuery] = useState('');
const [suggestions, setSuggestions] = useState([
"What were total sales last month?",
"How many customers do we have in California?",
"Show me top 10 products by revenue",
"What's the average order value by customer segment?",
"How many support tickets were opened yesterday?"
]);
// Use a suggested question
const useQuestion = (question) => {
setQuery(question);
// Optionally auto-submit the search
};
return (
<div className="search-interface">
<form onSubmit={handleSearch} className="search-form">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Ask a question about your data..."
className="search-input"
/>
<button type="submit" className="search-button">Search</button>
</form>
{/* Suggestions */}
{!results && !loading && (
<div className="search-suggestions">
<h3>Suggested questions:</h3>
<ul className="suggestions-list">
{suggestions.map((suggestion, index) => (
<li key={index}>
<button
onClick={() => useQuestion(suggestion)}
className="suggestion-btn"
>
{suggestion}
</button>
</li>
))}
</ul>
</div>
)}
{/* Results rendered here */}
</div>
);
}
Search Results Enhancement
Improve the presentation of search results with visualizations and summaries:
function EnhancedResults({ results }) {
if (!results || !results.data) return null;
// Generate a natural language summary of the results
function generateSummary() {
const data = results.data;
if (!Array.isArray(data)) {
return `The result is ${JSON.stringify(data)}.`;
}
if (data.length === 0) {
return "No data was found for your query.";
}
if (data.length === 1) {
return `Found 1 result: ${JSON.stringify(data[0])}.`;
}
return `Found ${data.length} results. Here are the details:`;
}
// Determine if data can be visualized
function canVisualize() {
const data = results.data;
return Array.isArray(data) &&
data.length > 1 &&
data.length <= 20 &&
Object.keys(data[0]).some(key => typeof data[0][key] === 'number');
}
// Simple visualization for numeric data
function renderVisualization() {
if (!canVisualize()) return null;
const data = results.data;
const keys = Object.keys(data[0]);
// Find a likely category field and numeric field
const categoryKey = keys.find(key => typeof data[0][key] === 'string') || keys[0];
const numberKey = keys.find(key => typeof data[0][key] === 'number');
if (!numberKey) return null;
// In a real implementation, you would use a charting library like Chart.js
return (
<div className="visualization">
<h3>Data Visualization</h3>
<div className="chart-placeholder">
{/* In a real app, replace with actual chart component */}
<div className="mock-bar-chart">
{data.map((item, index) => (
<div key={index} className="mock-bar-item">
<div className="mock-bar-label">{item[categoryKey]}</div>
<div
className="mock-bar"
style={{
width: `${Math.min(100, (item[numberKey] / Math.max(...data.map(d => d[numberKey]))) * 100)}%`
}}
></div>
<div className="mock-bar-value">{item[numberKey]}</div>
</div>
))}
</div>
</div>
</div>
);
}
return (
<div className="enhanced-results">
<div className="results-summary">
<h3>Summary</h3>
<p>{generateSummary()}</p>
</div>
{renderVisualization()}
<div className="detailed-results">
<h3>Detailed Results</h3>
{/* Render the full results table or other appropriate format */}
</div>
<div className="results-metadata">
<h4>Query Information</h4>
<ul>
<li><strong>Query Used:</strong> {results.query_used}</li>
<li><strong>Execution Time:</strong> {results.execution_time_ms}ms</li>
<li><strong>Parameters:</strong> {JSON.stringify(results.parameters || {})}</li>
</ul>
</div>
</div>
);
}
Search Filters and Refinement
Add filters and refinement options to help users narrow down results:
function SearchWithFilters() {
const [query, setQuery] = useState('');
const [results, setResults] = useState(null);
const [filters, setFilters] = useState({});
const [availableFilters, setAvailableFilters] = useState([]);
async function handleSearch(e) {
e.preventDefault();
// Perform search as before...
// After getting results, identify possible filters
if (data && Array.isArray(data.data) && data.data.length > 0) {
const possibleFilters = {};
// Identify string columns that could be used as filters
Object.keys(data.data[0]).forEach(key => {
if (typeof data.data[0][key] === 'string') {
// Get unique values for this column
const uniqueValues = [...new Set(data.data.map(item => item[key]))];
// Only use as filter if there are a reasonable number of unique values
if (uniqueValues.length > 1 && uniqueValues.length <= 10) {
possibleFilters[key] = uniqueValues;
}
}
});
setAvailableFilters(possibleFilters);
}
}
// Apply a filter
function applyFilter(key, value) {
setFilters(prev => ({
...prev,
[key]: value
}));
}
// Clear a specific filter
function clearFilter(key) {
setFilters(prev => {
const newFilters = { ...prev };
delete newFilters[key];
return newFilters;
});
}
// Clear all filters
function clearAllFilters() {
setFilters({});
}
// Filter the results based on selected filters
function getFilteredResults() {
if (!results || !Array.isArray(results.data)) return results;
// If no filters are applied, return all results
if (Object.keys(filters).length === 0) return results;
// Apply filters
const filteredData = results.data.filter(item => {
// Item passes if it matches all filters
return Object.entries(filters).every(([key, value]) => {
return item[key] === value;
});
});
return {
...results,
data: filteredData,
filtered: true
};
}
const filteredResults = getFilteredResults();
return (
<div className="search-interface">
{/* Search form */}
<form onSubmit={handleSearch} className="search-form">
{/* ... */}
</form>
{/* Results and filters */}
{results && (
<div className="search-results-container">
{/* Active filters */}
{Object.keys(filters).length > 0 && (
<div className="active-filters">
<div className="active-filters-header">
<h3>Active Filters</h3>
<button onClick={clearAllFilters} className="clear-all-btn">
Clear All
</button>
</div>
<div className="filter-tags">
{Object.entries(filters).map(([key, value]) => (
<div key={key} className="filter-tag">
<span>{key}: {value}</span>
<button onClick={() => clearFilter(key)} className="remove-filter-btn">
×
</button>
</div>
))}
</div>
</div>
)}
<div className="results-and-filters">
{/* Available filters */}
{Object.keys(availableFilters).length > 0 && (
<div className="filter-sidebar">
<h3>Refine Results</h3>
{Object.entries(availableFilters).map(([key, values]) => (
<div key={key} className="filter-group">
<h4>{key}</h4>
<ul className="filter-options">
{values.map(value => (
<li key={value}>
<button
onClick={() => applyFilter(key, value)}
className={filters[key] === value ? 'active' : ''}
>
{value}
</button>
</li>
))}
</ul>
</div>
))}
</div>
)}
{/* Search results */}
<div className="results-area">
{filteredResults.filtered && (
<div className="filter-notice">
Showing {filteredResults.data.length} of {results.data.length} results
</div>
)}
{/* Render results as before */}
</div>
</div>
</div>
)}
</div>
);
}
Advanced Search Features
Search History
Implement search history to allow users to revisit previous searches:
function SearchWithHistory() {
const [query, setQuery] = useState('');
const [searchHistory, setSearchHistory] = useState([]);
// Add search to history
function addToHistory(question, results) {
const historyItem = {
id: Date.now(),
question,
queryUsed: results.query_used,
timestamp: new Date(),
resultCount: Array.isArray(results.data) ? results.data.length : 1
};
setSearchHistory(prev => [historyItem, ...prev].slice(0, 10)); // Keep last 10 searches
// Optionally save to localStorage
try {
const savedHistory = JSON.parse(localStorage.getItem('searchHistory') || '[]');
localStorage.setItem('searchHistory', JSON.stringify([historyItem, ...savedHistory].slice(0, 10)));
} catch (err) {
console.error('Failed to save search history:', err);
}
}
// Load search history from localStorage on component mount
useEffect(() => {
try {
const savedHistory = JSON.parse(localStorage.getItem('searchHistory') || '[]');
setSearchHistory(savedHistory);
} catch (err) {
console.error('Failed to load search history:', err);
}
}, []);
// Clear search history
function clearHistory() {
setSearchHistory([]);
localStorage.removeItem('searchHistory');
}
// Rerun a previous search
function rerunSearch(question) {
setQuery(question);
handleSearch({ preventDefault: () => {} }, question);
}
return (
<div className="search-interface">
{/* Search form */}
<form onSubmit={handleSearch} className="search-form">
{/* ... */}
</form>
{/* Search results */}
{/* ... */}
{/* Search history */}
{searchHistory.length > 0 && (
<div className="search-history">
<div className="history-header">
<h3>Recent Searches</h3>
<button onClick={clearHistory} className="clear-history-btn">
Clear History
</button>
</div>
<ul className="history-list">
{searchHistory.map(item => (
<li key={item.id} className="history-item">
<button onClick={() => rerunSearch(item.question)} className="rerun-search-btn">
{item.question}
</button>
<div className="history-meta">
<span className="history-time">
{new Date(item.timestamp).toLocaleTimeString()}
</span>
<span className="history-results">
{item.resultCount} result{item.resultCount !== 1 ? 's' : ''}
</span>
</div>
</li>
))}
</ul>
</div>
)}
</div>
);
}
Related Searches
Suggest related searches based on current results:
function SearchWithRelatedQuestions({ results }) {
// Generate related questions based on current results
function generateRelatedQuestions() {
if (!results || !results.data) return [];
const relatedQuestions = [];
// If we have query parameters, suggest alternative values
if (results.parameters) {
Object.entries(results.parameters).forEach(([key, value]) => {
if (key === 'timeframe' && value) {
// Suggest different timeframes
if (value === 'last_month') {
relatedQuestions.push(`Same question for last week`);
relatedQuestions.push(`Same question for last quarter`);
} else if (value === 'last_week') {
relatedQuestions.push(`Same question for last month`);
relatedQuestions.push(`Same question for yesterday`);
}
}
if (key === 'limit' && value) {
// Suggest different limits
relatedQuestions.push(`Show me top ${value * 2} results`);
}
if (key === 'category' && value) {
// Suggest comparing with other categories
relatedQuestions.push(`Compare with other categories`);
}
});
}
// Suggest trending or deeper analysis
relatedQuestions.push(`Show trend over time`);
relatedQuestions.push(`What's driving these results?`);
return relatedQuestions;
}
const relatedQuestions = generateRelatedQuestions();
if (relatedQuestions.length === 0) return null;
return (
<div className="related-searches">
<h3>Related Questions</h3>
<ul className="related-questions-list">
{relatedQuestions.map((question, index) => (
<li key={index}>
<button onClick={() => onSelectRelated(question)} className="related-question-btn">
{question}
</button>
</li>
))}
</ul>
</div>
);
}
Saved Searches
Allow users to save and name their searches:
function SavedSearches() {
const [savedSearches, setSavedSearches] = useState([]);
const [showSaveDialog, setShowSaveDialog] = useState(false);
const [searchName, setSearchName] = useState('');
// Load saved searches on component mount
useEffect(() => {
try {
const saved = JSON.parse(localStorage.getItem('savedSearches') || '[]');
setSavedSearches(saved);
} catch (err) {
console.error('Failed to load saved searches:', err);
}
}, []);
// Save current search
function showSaveSearchDialog() {
setSearchName(query);
setShowSaveDialog(true);
}
// Complete saving the search
function saveSearch() {
if (!searchName.trim()) return;
const savedItem = {
id: Date.now(),
name: searchName,
query: query,
createdAt: new Date(),
};
const updatedSaved = [...savedSearches, savedItem];
setSavedSearches(updatedSaved);
// Save to localStorage
try {
localStorage.setItem('savedSearches', JSON.stringify(updatedSaved));
} catch (err) {
console.error('Failed to save searches:', err);
}
setShowSaveDialog(false);
setSearchName('');
}
// Delete a saved search
function deleteSavedSearch(id) {
const updatedSaved = savedSearches.filter(item => item.id !== id);
setSavedSearches(updatedSaved);
// Update localStorage
try {
localStorage.setItem('savedSearches', JSON.stringify(updatedSaved));
} catch (err) {
console.error('Failed to update saved searches:', err);
}
}
return (
<>
{/* Saved searches list */}
{savedSearches.length > 0 && (
<div className="saved-searches">
<h3>Saved Searches</h3>
<ul className="saved-list">
{savedSearches.map(item => (
<li key={item.id} className="saved-item">
<button onClick={() => runSavedSearch(item.query)} className="run-saved-btn">
{item.name}
</button>
<button onClick={() => deleteSavedSearch(item.id)} className="delete-saved-btn">
Delete
</button>
</li>
))}
</ul>
</div>
)}
{/* Save search button - show when results are available */}
{results && (
<button onClick={showSaveSearchDialog} className="save-search-btn">
Save This Search
</button>
)}
{/* Save search dialog */}
{showSaveDialog && (
<div className="save-dialog-overlay">
<div className="save-dialog">
<h3>Save Search</h3>
<input
type="text"
value={searchName}
onChange={(e) => setSearchName(e.target.value)}
placeholder="Name this search"
className="save-name-input"
/>
<div className="save-dialog-actions">
<button onClick={() => setShowSaveDialog(false)} className="cancel-btn">
Cancel
</button>
<button onClick={saveSearch} className="save-btn">
Save
</button>
</div>
</div>
</div>
)}
</>
);
}
Search Interface Best Practices
Clear Search Box
Make the search input prominent with clear placeholder text
Helpful Suggestions
Provide example searches to help users get started
Intelligent Error Handling
When a search fails, suggest alternatives or corrections
Loading States
Show clear loading indicators during search execution
Mobile Optimization
Ensure the search interface works well on all device sizes
Progressive Disclosure
Show a simple result first with options to see more details
Context Preservation
Remember previous searches and allow users to return to them
Clear Feedback
Make it clear which query was used and how parameters were interpreted
Example Search Interface Use Cases
Customer Support Portal
Allow support teams to quickly find customer information
Internal Knowledge Base
Enable employees to search company data and documentation
E-commerce Product Search
Help customers find products using natural language
Business Intelligence Tool
Support executives in finding business metrics quickly
Healthcare Patient Portal
Help patients find relevant information about their care
Financial Research Tool
Allow analysts to find financial data using natural language
Next Steps
After building your search interface, consider:
- Adding user authentication for internal search tools
- Creating specialized queries for specific search types
- Implementing result caching to improve performance
- Exploring dashboard integration to visualize search results
- Building a hybrid search solution that combines Infactory with traditional search engines
Was this page helpful?
- Building Search Interfaces with Infactory
- Why Infactory for Search Interfaces?
- Search Interface Architecture Overview
- Implementation Approaches
- Basic Search Interface
- Backend Implementation
- Enhanced Search Experience
- Search Suggestions
- Search Results Enhancement
- Search Filters and Refinement
- Advanced Search Features
- Search History
- Related Searches
- Saved Searches
- Search Interface Best Practices
- Example Search Interface Use Cases
- Next Steps