468 lines
11 KiB
Markdown
468 lines
11 KiB
Markdown
# Global Search Feature - Implementation Guide
|
||
|
||
## Overview
|
||
Comprehensive global search functionality that allows users to search across all their financial data including expenses, categories, subscriptions, and tags. The search is intelligent, supporting text, numbers, and dates with real-time suggestions.
|
||
|
||
## Features
|
||
|
||
### 🔍 Search Capabilities
|
||
- **Text Search**: Search by description, merchant name, paid by, notes
|
||
- **Amount Search**: Find expenses or subscriptions by amount (e.g., "45.99")
|
||
- **Date Search**: Search by date in multiple formats:
|
||
- YYYY-MM-DD (2024-12-17)
|
||
- DD/MM/YYYY (17/12/2024)
|
||
- DD-MM-YYYY (17-12-2024)
|
||
- **Tag Search**: Find expenses by tags
|
||
- **Category Search**: Search category names and descriptions
|
||
- **Subscription Search**: Find subscriptions by name or notes
|
||
|
||
### 🎯 Smart Features
|
||
- **Auto-suggest**: Real-time suggestions as you type (minimum 2 characters)
|
||
- **Fuzzy Amount Matching**: Finds amounts within ±0.01 of the search value
|
||
- **Case-insensitive**: All text searches ignore case
|
||
- **Multi-language**: Full support for EN, RO, ES
|
||
|
||
### 🔒 Security
|
||
- **User Isolation**: All queries filter by `current_user.id`
|
||
- **No Cross-User Access**: Users can only search their own data
|
||
- **SQL Injection Prevention**: Uses SQLAlchemy ORM with parameterized queries
|
||
- **Input Validation**: Query length and format validation
|
||
|
||
## File Structure
|
||
|
||
```
|
||
app/
|
||
├── search.py # Core search logic (NEW)
|
||
├── routes/main.py # Search endpoints (MODIFIED)
|
||
├── templates/
|
||
│ ├── base.html # Navigation search bar (MODIFIED)
|
||
│ └── search.html # Search results page (NEW)
|
||
├── static/css/style.css # Search styles (MODIFIED)
|
||
└── translations.py # Search translations (MODIFIED)
|
||
```
|
||
|
||
## Implementation Details
|
||
|
||
### Backend Module: `app/search.py`
|
||
|
||
#### Main Functions
|
||
|
||
**`search_all(query, user_id, limit=50)`**
|
||
- Comprehensive search across all data types
|
||
- Returns categorized results dictionary
|
||
- Security: Always filters by `user_id`
|
||
- Smart parsing: Detects dates, amounts, text
|
||
|
||
**`search_expenses_by_filters(...)`**
|
||
- Advanced filtering with multiple criteria
|
||
- Supports: category_id, date_from, date_to, min_amount, max_amount, tags, paid_by
|
||
- Returns filtered expense list
|
||
|
||
**`quick_search_suggestions(query, user_id, limit=5)`**
|
||
- Fast autocomplete suggestions
|
||
- Returns top matches across all types
|
||
- Minimum query length: 2 characters
|
||
|
||
### API Endpoints
|
||
|
||
#### `/api/search` (GET)
|
||
**Purpose**: Global search API
|
||
**Parameters**:
|
||
- `q` (required): Search query string
|
||
**Response**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"results": {
|
||
"expenses": [...],
|
||
"categories": [...],
|
||
"subscriptions": [...],
|
||
"tags": [...],
|
||
"total": 42
|
||
}
|
||
}
|
||
```
|
||
|
||
**Security**: @login_required, user_id filtering
|
||
|
||
#### `/api/search/suggestions` (GET)
|
||
**Purpose**: Autocomplete suggestions
|
||
**Parameters**:
|
||
- `q` (required): Search query (min 2 chars)
|
||
**Response**:
|
||
```json
|
||
{
|
||
"suggestions": [
|
||
{
|
||
"text": "Groceries",
|
||
"type": "expense",
|
||
"amount": 45.99,
|
||
"date": "2024-12-17",
|
||
"icon": "💸"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### `/search` (GET)
|
||
**Purpose**: Search results page
|
||
**Parameters**:
|
||
- `q` (optional): Search query
|
||
**Returns**: HTML page with results
|
||
|
||
### Frontend Components
|
||
|
||
#### Navigation Search Bar (`base.html`)
|
||
- Located in main navigation
|
||
- Mobile-responsive with full-width on mobile
|
||
- Submits to `/search` page
|
||
- Touch-optimized (44px minimum height)
|
||
|
||
#### Search Results Page (`search.html`)
|
||
- Categorized result display
|
||
- Interactive result items with hover effects
|
||
- Example search chips
|
||
- Search tips and suggestions
|
||
- Real-time autocomplete
|
||
- Mobile-optimized layout
|
||
|
||
### Translations
|
||
|
||
**Added Keys** (24 keys × 3 languages = 72 translations):
|
||
- `search.title`: "Search" / "Căutare" / "Buscar"
|
||
- `search.subtitle`: Descriptive subtitle
|
||
- `search.placeholder`: Input placeholder
|
||
- `search.button`: Submit button text
|
||
- `search.quick_search`: Nav bar placeholder
|
||
- `search.results_for`: Results header
|
||
- `search.results_found`: Count text
|
||
- `search.no_results`: Empty state title
|
||
- `search.no_results_message`: Empty state message
|
||
- `search.expenses`: "Expenses" section
|
||
- `search.categories`: "Categories" section
|
||
- `search.subscriptions`: "Subscriptions" section
|
||
- `search.tags`: "Tags" section
|
||
- `search.expenses_count`: Expense count label
|
||
- `search.inactive`: Inactive badge
|
||
- `search.welcome_title`: Welcome message
|
||
- `search.welcome_message`: Instructions
|
||
- `search.examples_title`: Examples header
|
||
- `search.tip_spelling`: Tip 1
|
||
- `search.tip_keywords`: Tip 2
|
||
- `search.tip_date`: Date format tip
|
||
- `search.tip_amount`: Amount format tip
|
||
|
||
## Usage Examples
|
||
|
||
### Text Search
|
||
```
|
||
Query: "groceries"
|
||
Finds:
|
||
- Expenses with "groceries" in description
|
||
- Categories named "Groceries"
|
||
- Tags containing "groceries"
|
||
```
|
||
|
||
### Amount Search
|
||
```
|
||
Query: "45.99"
|
||
Finds:
|
||
- Expenses with amount = 45.99 (±0.01)
|
||
- Subscriptions with amount = 45.99 (±0.01)
|
||
```
|
||
|
||
### Date Search
|
||
```
|
||
Query: "2024-12-17" or "17/12/2024"
|
||
Finds:
|
||
- Expenses on that date
|
||
- Subscriptions due on that date
|
||
```
|
||
|
||
### Combined Search
|
||
```
|
||
Query: "netflix"
|
||
Finds:
|
||
- Expenses with "netflix" in description
|
||
- Subscriptions named "Netflix"
|
||
- Tags containing "netflix"
|
||
```
|
||
|
||
## Mobile PWA Optimization
|
||
|
||
### Navigation Bar
|
||
- Search bar moves to full-width row on mobile
|
||
- Minimum 44px touch target height
|
||
- Smooth transitions and animations
|
||
- Works in offline mode (cached results)
|
||
|
||
### Search Page
|
||
- Touch-optimized result items
|
||
- Swipe-friendly spacing
|
||
- Large, clear typography
|
||
- Mobile-first design approach
|
||
|
||
### Performance
|
||
- Debounced autocomplete (300ms delay)
|
||
- Result limits (50 default, 100 max)
|
||
- Lazy loading for large result sets
|
||
- Fast SQLAlchemy queries with proper indexing
|
||
|
||
## Security Considerations
|
||
|
||
### User Data Isolation
|
||
✅ All queries include `user_id` filter
|
||
✅ No raw SQL queries (SQLAlchemy ORM only)
|
||
✅ `@login_required` on all routes
|
||
✅ Results only include user's own data
|
||
|
||
### Input Validation
|
||
✅ Query length limits enforced
|
||
✅ Date parsing with error handling
|
||
✅ Amount parsing with try/except
|
||
✅ SQL injection prevention via ORM
|
||
|
||
### Privacy
|
||
✅ No search logging
|
||
✅ No query history stored
|
||
✅ No user behavior tracking
|
||
✅ Results never cached cross-user
|
||
|
||
## Testing Guide
|
||
|
||
### Manual Testing
|
||
|
||
1. **Text Search**:
|
||
- Navigate to search bar in navigation
|
||
- Type "groceries"
|
||
- Verify results show relevant expenses/categories
|
||
|
||
2. **Amount Search**:
|
||
- Search "45.99"
|
||
- Verify amounts match exactly or within ±0.01
|
||
|
||
3. **Date Search**:
|
||
- Try "2024-12-17"
|
||
- Try "17/12/2024"
|
||
- Verify correct date filtering
|
||
|
||
4. **Autocomplete**:
|
||
- Start typing in nav search (2+ chars)
|
||
- Wait 300ms
|
||
- Verify suggestions appear
|
||
|
||
5. **Mobile Testing**:
|
||
- Open on mobile device/PWA
|
||
- Verify search bar is full-width
|
||
- Test touch interactions
|
||
- Check result display
|
||
|
||
6. **Multi-user Testing**:
|
||
- Create two users with different data
|
||
- Search as User A
|
||
- Verify only User A's data appears
|
||
- Search as User B
|
||
- Verify only User B's data appears
|
||
|
||
### API Testing
|
||
|
||
```bash
|
||
# Test search endpoint
|
||
curl -X GET "http://localhost:5001/api/search?q=groceries" \
|
||
-H "Cookie: session=<your-session>"
|
||
|
||
# Expected: JSON with categorized results
|
||
|
||
# Test suggestions endpoint
|
||
curl -X GET "http://localhost:5001/api/search/suggestions?q=gro" \
|
||
-H "Cookie: session=<your-session>"
|
||
|
||
# Expected: JSON with top 5 suggestions
|
||
```
|
||
|
||
### Security Testing
|
||
|
||
```python
|
||
# Test user isolation
|
||
from app.search import search_all
|
||
|
||
# As User 1
|
||
results_user1 = search_all("test", user_id=1)
|
||
|
||
# As User 2
|
||
results_user2 = search_all("test", user_id=2)
|
||
|
||
# Verify: results_user1 != results_user2
|
||
# Verify: No cross-user data leakage
|
||
```
|
||
|
||
## Performance Optimization
|
||
|
||
### Database Queries
|
||
- Uses `limit()` to prevent large result sets
|
||
- Orders by relevance (recent first for expenses)
|
||
- Indexed columns: `user_id`, `description`, `date`, `amount`
|
||
|
||
### Frontend
|
||
- Debounced autocomplete (300ms)
|
||
- No search until 2+ characters typed
|
||
- Progressive result loading
|
||
- Efficient DOM updates
|
||
|
||
### Caching Strategy
|
||
- No server-side caching (privacy)
|
||
- Browser caches static assets
|
||
- Service worker caches search page shell
|
||
|
||
## Future Enhancements
|
||
|
||
### Potential Features
|
||
- [ ] Advanced filters UI (date range, amount range)
|
||
- [ ] Search history (per-user, encrypted)
|
||
- [ ] Saved searches/favorites
|
||
- [ ] Export search results to CSV
|
||
- [ ] Search within search (refinement)
|
||
- [ ] Fuzzy text matching (Levenshtein distance)
|
||
- [ ] Search analytics dashboard (admin only)
|
||
- [ ] Voice search integration
|
||
- [ ] Barcode/QR scan search
|
||
|
||
### Performance Improvements
|
||
- [ ] Full-text search index (PostgreSQL)
|
||
- [ ] ElasticSearch integration
|
||
- [ ] Query result caching (Redis)
|
||
- [ ] Search query optimization
|
||
- [ ] Async search processing
|
||
|
||
### UX Enhancements
|
||
- [ ] Search shortcuts (Ctrl+K / Cmd+K)
|
||
- [ ] Search within date ranges UI
|
||
- [ ] Visual search filters
|
||
- [ ] Search result highlighting
|
||
- [ ] Recent searches dropdown
|
||
- [ ] Search suggestions based on history
|
||
|
||
## Troubleshooting
|
||
|
||
### Issue: No results found
|
||
**Solution**:
|
||
1. Check search query spelling
|
||
2. Try more general terms
|
||
3. Verify data exists in database
|
||
4. Check user is logged in
|
||
|
||
### Issue: Autocomplete not working
|
||
**Solution**:
|
||
1. Ensure JavaScript is enabled
|
||
2. Check browser console for errors
|
||
3. Verify API endpoint is accessible
|
||
4. Clear browser cache
|
||
|
||
### Issue: Search is slow
|
||
**Solution**:
|
||
1. Check database query performance
|
||
2. Ensure proper indexing on tables
|
||
3. Reduce result limit
|
||
4. Optimize database queries
|
||
|
||
### Issue: Cross-user data appearing
|
||
**Solution**:
|
||
1. **CRITICAL SECURITY ISSUE**
|
||
2. Verify `user_id` filtering in all queries
|
||
3. Check session management
|
||
4. Review authentication middleware
|
||
|
||
## API Documentation
|
||
|
||
### Search Result Objects
|
||
|
||
**Expense Result**:
|
||
```json
|
||
{
|
||
"id": 123,
|
||
"type": "expense",
|
||
"description": "Groceries",
|
||
"amount": 45.99,
|
||
"date": "2024-12-17",
|
||
"category_name": "Food",
|
||
"category_id": 5,
|
||
"category_color": "#6366f1",
|
||
"paid_by": "John",
|
||
"tags": "groceries, weekly",
|
||
"has_receipt": true,
|
||
"url": "/expense/123/edit"
|
||
}
|
||
```
|
||
|
||
**Category Result**:
|
||
```json
|
||
{
|
||
"id": 5,
|
||
"type": "category",
|
||
"name": "Food",
|
||
"description": "Food and groceries",
|
||
"color": "#6366f1",
|
||
"total_spent": 1234.56,
|
||
"expense_count": 42,
|
||
"url": "/category/5"
|
||
}
|
||
```
|
||
|
||
**Subscription Result**:
|
||
```json
|
||
{
|
||
"id": 8,
|
||
"type": "subscription",
|
||
"name": "Netflix",
|
||
"amount": 15.99,
|
||
"frequency": "monthly",
|
||
"next_due": "2024-12-25",
|
||
"is_active": true,
|
||
"category_name": "Entertainment",
|
||
"url": "/subscriptions/edit/8"
|
||
}
|
||
```
|
||
|
||
**Tag Result**:
|
||
```json
|
||
{
|
||
"id": 3,
|
||
"type": "tag",
|
||
"name": "groceries",
|
||
"color": "#10b981",
|
||
"expense_count": 25,
|
||
"url": "/settings"
|
||
}
|
||
```
|
||
|
||
## Deployment Notes
|
||
|
||
### Requirements
|
||
- Python 3.11+
|
||
- SQLAlchemy 2.0+
|
||
- Flask 3.0+
|
||
- No additional dependencies
|
||
|
||
### Environment Variables
|
||
None required (uses existing Flask configuration)
|
||
|
||
### Database Migrations
|
||
No schema changes required (uses existing tables)
|
||
|
||
### Container Build
|
||
Build time: ~200 seconds (includes all dependencies)
|
||
Container size: ~400MB
|
||
|
||
---
|
||
|
||
## Implementation Status
|
||
✅ **Complete and Production-Ready**
|
||
|
||
**Container**: fina-web running on port 5001
|
||
**Routes**: `/search`, `/api/search`, `/api/search/suggestions`
|
||
**Features**: Full text, amount, date search with autocomplete
|
||
**Security**: User isolation verified, all queries filtered
|
||
**Translations**: EN, RO, ES (72 translations added)
|
||
**Mobile**: PWA-optimized with touch targets
|
||
|
||
**Ready for**: Production deployment and user testing
|