Real-time Search and Auto-Suggestions for Property Listings
This comprehensive guide will walk you through building a real-time search system with auto-suggestions for property listings. I'll provide a complete solution with frontend and backend components.
System Architecture
- Frontend: React.js with TypeScript
- Backend: Node.js with Express
- Database: MongoDB (for property listings)
- Search Engine: Elasticsearch (for fast autocomplete)
Step 1: Set Up the Backend
Install Dependencies
npm init -y
npm install express mongoose body-parser cors elasticsearch
Backend Server (server.js)
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const elasticsearch = require('elasticsearch');
const app = express();
app.use(cors());
app.use(bodyParser.json());
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/propertyDB', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Elasticsearch client
const esClient = new elasticsearch.Client({
host: 'localhost:9200',
log: 'trace'
});
// Property Schema
const PropertySchema = new mongoose.Schema({
title: String,
location: String,
price: Number,
type: String,
area: Number,
bedrooms: Number,
// Add other property fields
});
const Property = mongoose.model('Property', PropertySchema);
// API Endpoints
app.get('/api/properties/search', async (req, res) => {
const { query } = req.query;
try {
const results = await esClient.search({
index: 'properties',
body: {
query: {
multi_match: {
query: query,
fields: ['title', 'location', 'type'],
fuzziness: 'AUTO'
}
}
}
});
res.json(results.hits.hits.map(hit => hit._source));
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/properties/suggest', async (req, res) => {
const { prefix } = req.query;
try {
const suggestions = await esClient.search({
index: 'properties',
body: {
suggest: {
property_suggest: {
prefix: prefix,
completion: {
field: 'suggest',
fuzzy: {
fuzziness: 1
}
}
}
}
}
});
res.json(suggestions.suggest.property_suggest[0].options);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Start server
app.listen(3001, () => {
console.log('Server running on port 3001');
});
Step 2: Set Up Elasticsearch
- Install Elasticsearch (https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html)
- Create an index with mapping:
curl -X PUT "localhost:9200/properties" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"title": { "type": "text" },
"location": { "type": "text" },
"type": { "type": "keyword" },
"suggest": {
"type": "completion",
"analyzer": "simple",
"search_analyzer": "simple"
}
}
}
}
'
Step 3: Frontend Implementation (React)
Install Dependencies
npx create-react-app property-search --template typescript
cd property-search
npm install axios @material-ui/core @material-ui/labs
Search Component (Search.tsx)
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { TextField, List, ListItem, ListItemText, Paper } from '@material-ui/core';
interface Property {
id: string;
title: string;
location: string;
price: number;
type: string;
}
const Search: React.FC = () => {
const [query, setQuery] = useState('');
const [suggestions, setSuggestions] = useState<Property[]>([]);
const [results, setResults] = useState<Property[]>([]);
const [isSearching, setIsSearching] = useState(false);
useEffect(() => {
if (query.length > 2) {
const timer = setTimeout(() => {
fetchSuggestions();
}, 300);
return () => clearTimeout(timer);
} else {
setSuggestions([]);
}
}, [query]);
const fetchSuggestions = async () => {
try {
const response = await axios.get('http://localhost:3001/api/properties/suggest', {
params: { prefix: query }
});
setSuggestions(response.data.map((item: any) => item._source));
} catch (error) {
console.error('Error fetching suggestions:', error);
}
};
const handleSearch = async () => {
if (query.trim() === '') return;
setIsSearching(true);
try {
const response = await axios.get('http://localhost:3001/api/properties/search', {
params: { query }
});
setResults(response.data);
} catch (error) {
console.error('Error searching properties:', error);
} finally {
setIsSearching(false);
}
};
return (
<div style={{ maxWidth: 800, margin: '0 auto' }}>
<TextField
fullWidth
variant="outlined"
label="Search properties..."
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
/>
{suggestions.length > 0 && (
<Paper style={{ position: 'absolute', zIndex: 1, width: '100%' }}>
<List>
{suggestions.map((property) => (
<ListItem
key={property.id}
button
onClick={() => {
setQuery(`${property.title}, ${property.location}`);
setSuggestions([]);
}}
>
<ListItemText
primary={property.title}
secondary={`${property.location} - ${property.type}`}
/>
</ListItem>
))}
</List>
</Paper>
)}
<div style={{ marginTop: 20 }}>
{isSearching ? (
<p>Searching...</p>
) : (
results.map((property) => (
<div key={property.id} style={{ padding: 10, borderBottom: '1px solid #eee' }}>
<h3>{property.title}</h3>
<p>{property.location} - {property.type}</p>
<p>Price: ${property.price}</p>
</div>
))
)}
</div>
</div>
);
};
export default Search;
Step 4: Indexing Properties
Create a script to index existing properties:
// indexProperties.js
const mongoose = require('mongoose');
const elasticsearch = require('elasticsearch');
mongoose.connect('mongodb://localhost:27017/propertyDB', {
useNewUrlParser: true,
useUnifiedTopology: true
});
const Property = require('./models/Property');
const esClient = new elasticsearch.Client({ host: 'localhost:9200' });
async function indexProperties() {
const properties = await Property.find();
for (const property of properties) {
await esClient.index({
index: 'properties',
id: property._id.toString(),
body: {
title: property.title,
location: property.location,
type: property.type,
price: property.price,
suggest: {
input: [
property.title,
property.location,
`${property.title} ${property.location}`,
`${property.type} in ${property.location}`
]
}
}
});
}
console.log('Indexing complete');
process.exit();
}
indexProperties();
Step 5: Deployment Considerations
-
Performance Optimization:
- Implement debouncing for search input (already done in the React component)
- Cache frequent search results
- Use pagination for search results
-
Scalability:
- Consider using Redis for caching
- Implement load balancing for the backend
- Use a managed Elasticsearch service for production
-
Security:
- Add rate limiting to API endpoints
- Implement JWT authentication
- Sanitize all search inputs
Step 6: Testing the Implementation
- Start your backend server:
node server.js
- Start your React app:
cd property-search
npm start
- Test the search functionality:
- Type at least 3 characters to see autocomplete suggestions
- Press Enter or click a suggestion to see full search results
Conclusion
This implementation provides a robust real-time search system with auto-suggestions for property listings. The combination of Elasticsearch for fast autocomplete and fuzzy matching with MongoDB for primary data storage creates a powerful search experience.
Key features:
- Real-time suggestions as users type
- Fuzzy matching for misspellings
- Fast response times
- Scalable architecture
You can extend this system by adding filters (price range, property type), sorting options, or personalized recommendations based on user behavior.
Written by think4buysale.in Software and ompropertydealer.com
Top comments (0)