Search Implementation
This guide covers implementing search functionality in your Twigz application.Overview
Twigz provides comprehensive search capabilities for products, stores, and content discovery.Basic Search Setup
1. Product Search
Copy
// Basic product search
const searchProducts = async (query: string, filters?: SearchFilters) => {
const results = await convex.query(api.shared.search.search, {
query,
type: "products",
filters: {
category: filters?.category,
priceRange: filters?.priceRange,
storeId: filters?.storeId
},
limit: 20
});
return results;
};
2. Store Search
Copy
// Search stores
const searchStores = async (query: string, location?: Location) => {
const results = await convex.query(api.shared.search.search, {
query,
type: "stores",
filters: {
location: location,
category: "all"
},
limit: 10
});
return results;
};
Advanced Search Features
1. Quick Search (Autocomplete)
Copy
// Implement autocomplete
const [suggestions, setSuggestions] = useState([]);
const handleSearchInput = async (query: string) => {
if (query.length < 2) {
setSuggestions([]);
return;
}
const results = await convex.query(api.shared.search.quickSearch, {
query,
limit: 5
});
setSuggestions(results);
};
2. Search History
Copy
// Save search history
const saveSearch = async (query: string) => {
await convex.mutation(api.shared.search.saveSearchHistory, {
query,
userId: currentUser.id
});
};
// Get search history
const getSearchHistory = async () => {
const history = await convex.query(api.shared.search.getSearchHistory, {
userId: currentUser.id,
limit: 10
});
return history;
};
3. Advanced Search
Copy
// Advanced search with multiple filters
const advancedSearch = async (searchParams: AdvancedSearchParams) => {
const results = await convex.query(api.shared.search.advancedSearch, {
query: searchParams.query,
filters: {
category: searchParams.category,
priceRange: searchParams.priceRange,
rating: searchParams.minRating,
location: searchParams.location,
storeId: searchParams.storeId
},
sortBy: searchParams.sortBy,
sortOrder: searchParams.sortOrder,
limit: searchParams.limit || 20
});
return results;
};
Search UI Components
1. Search Bar Component
Copy
const SearchBar = () => {
const [query, setQuery] = useState("");
const [suggestions, setSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const handleInputChange = async (value: string) => {
setQuery(value);
if (value.length >= 2) {
const results = await convex.query(api.shared.search.quickSearch, {
query: value,
limit: 5
});
setSuggestions(results);
setShowSuggestions(true);
} else {
setSuggestions([]);
setShowSuggestions(false);
}
};
return (
<div className="search-container">
<input
type="text"
value={query}
onChange={(e) => handleInputChange(e.target.value)}
placeholder="Search products, stores..."
className="search-input"
/>
{showSuggestions && suggestions.length > 0 && (
<div className="suggestions-dropdown">
{suggestions.map((suggestion, index) => (
<div
key={index}
className="suggestion-item"
onClick={() => {
setQuery(suggestion.text);
setShowSuggestions(false);
performSearch(suggestion.text);
}}
>
{suggestion.text}
</div>
))}
</div>
)}
</div>
);
};
2. Search Filters Component
Copy
const SearchFilters = ({ onFiltersChange }) => {
const [filters, setFilters] = useState({
category: "",
priceRange: { min: 0, max: 1000 },
rating: 0,
sortBy: "relevance"
});
const handleFilterChange = (key, value) => {
const newFilters = { ...filters, [key]: value };
setFilters(newFilters);
onFiltersChange(newFilters);
};
return (
<div className="search-filters">
<select
value={filters.category}
onChange={(e) => handleFilterChange("category", e.target.value)}
>
<option value="">All Categories</option>
<option value="food">Food</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
<input
type="range"
min="0"
max="1000"
value={filters.priceRange.max}
onChange={(e) => handleFilterChange("priceRange", {
...filters.priceRange,
max: parseInt(e.target.value)
})}
/>
<select
value={filters.sortBy}
onChange={(e) => handleFilterChange("sortBy", e.target.value)}
>
<option value="relevance">Relevance</option>
<option value="price">Price</option>
<option value="rating">Rating</option>
<option value="newest">Newest</option>
</select>
</div>
);
};
Search Results
1. Results Display
Copy
const SearchResults = ({ results, loading }) => {
if (loading) {
return <div className="loading">Searching...</div>;
}
if (!results || results.length === 0) {
return <div className="no-results">No results found</div>;
}
return (
<div className="search-results">
{results.map((item) => (
<SearchResultItem key={item.id} item={item} />
))}
</div>
);
};
2. Pagination
Copy
const SearchPagination = ({ currentPage, totalPages, onPageChange }) => {
return (
<div className="pagination">
<button
disabled={currentPage === 1}
onClick={() => onPageChange(currentPage - 1)}
>
Previous
</button>
<span>Page {currentPage} of {totalPages}</span>
<button
disabled={currentPage === totalPages}
onClick={() => onPageChange(currentPage + 1)}
>
Next
</button>
</div>
);
};
Performance Optimization
1. Debounced Search
Copy
import { useDebounce } from 'use-debounce';
const SearchComponent = () => {
const [query, setQuery] = useState("");
const [debouncedQuery] = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) {
performSearch(debouncedQuery);
}
}, [debouncedQuery]);
};
2. Caching Results
Copy
const useSearchCache = () => {
const [cache, setCache] = useState(new Map());
const getCachedResult = (query, filters) => {
const key = `${query}-${JSON.stringify(filters)}`;
return cache.get(key);
};
const setCachedResult = (query, filters, result) => {
const key = `${query}-${JSON.stringify(filters)}`;
setCache(prev => new Map(prev).set(key, result));
};
return { getCachedResult, setCachedResult };
};
Best Practices
- Debounce search input to avoid excessive API calls
- Cache search results for better performance
- Provide clear feedback during search operations
- Handle empty states gracefully
- Implement pagination for large result sets
- Use search history for better UX
- Test search relevance regularly
Search functionality is optimized for fast, relevant results with support for complex filtering and sorting options.
