Database Overview
MayTravel uses PostgreSQL as its primary database management system, with PostGIS extension for geospatial functionality. The database is designed to support AI-generated travel itineraries with flexible schema that can accommodate dynamic fields.Database Configuration
Connection details (backend/src/databases/postgres-db/maytraveldb.mjs:4-10):
Why PostgreSQL?
PostgreSQL was chosen for several strategic reasons:1. Schema Flexibility
“Se hace uso de PostgresSQL debido a que, en caso de que la IA decida agregar nuevos campos a la table de intinerarios, esta no se rompa”PostgreSQL provides the flexibility needed when AI models generate itineraries with varying structures:
- JSON/JSONB Support: Can store semi-structured AI responses
- Dynamic Columns: Schema can evolve without breaking existing data
- Extensibility: Custom types and functions for domain-specific logic
2. Data Integrity & Relationships
“De igual forma postgresSQL maneja integridad y relacionalidad de datos además de la flexibilidad para las tablas de intinerarios”PostgreSQL ensures data consistency through:
- Foreign Key Constraints: Maintain relationships between entities
- ACID Transactions: Reliable data operations
- Referential Integrity: Cascading updates and deletes
- Complex Queries: Powerful JOIN operations for data aggregation
3. Geospatial Capabilities
With PostGIS extension:- Location Data: Store and query geographic coordinates
- Spatial Indexing: Fast proximity searches
- Geometry Types: POINT, LINESTRING, POLYGON support
- Distance Calculations: Built-in geospatial functions
Data Model
The database follows a hierarchical structure for travel itineraries:Entity Relationship Diagram
Database Schema
Users Table
Stores user accounts and authentication information. Columns:id(SERIAL PRIMARY KEY) - Unique user identifierusername(VARCHAR UNIQUE) - User login name (stored lowercase)password(VARCHAR) - Hashed passwordemail(VARCHAR UNIQUE) - User email address (stored lowercase)role(VARCHAR) - User role (default: ‘user’)created_at(TIMESTAMP) - Account creation timestamp
- Primary key on
id - Unique constraint on
username - Unique constraint on
email
UsersModel.mjs:1-62
Interests Table
Catalog of user interests for personalized recommendations. Columns:id(SERIAL PRIMARY KEY) - Unique interest identifiername(VARCHAR) - Interest name (e.g., “museums”, “hiking”, “food”)
InterestsModel.mjs:1-31
Users_Interests Table
Junction table linking users to their interests (many-to-many relationship). Columns:user_id(INTEGER FK) - Referencesusers.idinterest_id(INTEGER FK) - Referencesinterests.id
user_id, interest_id)
Query Example: UsersModel.mjs:44-47
Trips Table
Core table for travel itineraries. Columns:id(SERIAL PRIMARY KEY) - Unique trip identifieruser_id(INTEGER FK) - Referencesusers.idtitle(VARCHAR) - Trip name/destinationshelter(GEOMETRY POINT) - Accommodation location (PostGIS)arrive_date(DATE) - Trip start dateleave_date(DATE) - Trip end datecreated_at(TIMESTAMP) - Trip creation timestamp
TripsModel.mjs:23-26
The
shelter field uses PostGIS ST_SetSRID(ST_MakePoint(lng, lat), 4326) to store geographic coordinates with SRID 4326 (WGS 84 coordinate system).POI_Catalog Table
Points of Interest (POI) catalog with detailed location and timing information. Columns:id(SERIAL PRIMARY KEY) - Unique POI identifiername(VARCHAR) - POI nameaddress(VARCHAR) - Street addresspoi(GEOMETRY POINT) - Geographic location (PostGIS)category(VARCHAR) - POI type (museum, restaurant, park, etc.)open_time(TIME) - Opening timeclose_time(TIME) - Closing timeaverage_stay_minutes(INTEGER) - Typical visit duration
PoisModel.mjs:9-33
Create POI with Geospatial Data:
Stops Table
Itinerary stops linking trips to POIs with scheduling information. Columns:id(SERIAL PRIMARY KEY) - Unique stop identifiertrip_id(INTEGER FK) - Referencestrips.idpoi_catalog_id(INTEGER FK) - Referencespoi_catalog.idstop_order(INTEGER) - Sequence number in itineraryarrival_time(TIME) - Planned arrival timedeparture_time(TIME) - Planned departure time
StopsModel.mjs:4-16
Data Relationships
Trip Hierarchy
The data model implements a hierarchical structure for travel planning:Key Relationships
-
User → Trips (One-to-Many)
- A user can create multiple trips
- Each trip belongs to one user
- Foreign key:
trips.user_id → users.id
-
Trip → Stops (One-to-Many)
- A trip contains multiple stops (activities/destinations)
- Stops are ordered by
stop_orderfield - Foreign key:
stops.trip_id → trips.id
-
POI_Catalog → Stops (One-to-Many)
- POI catalog entry can be used in many trip itineraries
- Each stop references one POI
- Foreign key:
stops.poi_catalog_id → poi_catalog.id
-
User ↔ Interests (Many-to-Many)
- Users can have multiple interests
- Each interest can belong to multiple users
- Junction table:
users_interests
Complex Queries
Trip Details with Stops
Fetching complete trip information with all stops and POI details (TripsModel.mjs:30-33):
- Multiple LEFT JOINs to gather related data
- Ordering by
stop_orderto maintain itinerary sequence - Aliasing columns for clarity (
spot_name,spot_label)
User Trips Overview
Retrieving all trips for a specific user (TripsModel.mjs:9-15):
User Interests with Details
Fetching user profile with interests (UsersModel.mjs:44-47):
Geospatial Features
PostGIS Integration
MayTravel leverages PostGIS for location-based functionality:Storing Location Data
Function:ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
ST_MakePoint(): Creates a point geometry from coordinatesST_SetSRID(): Assigns spatial reference system (4326 = WGS 84)- Note: PostGIS expects (longitude, latitude) order, not (latitude, longitude)
Example: Storing Trip Shelter Location
Future Geospatial Queries
PostGIS enables powerful location-based features:Data Integrity
Parameterized Queries
All database operations use parameterized queries to prevent SQL injection:Input Sanitization
User inputs are sanitized before storage (UsersModel.mjs:15-18):
Foreign Key Constraints
Database enforces referential integrity through foreign keys:trips.user_idmust reference validusers.idstops.trip_idmust reference validtrips.idstops.poi_catalog_idmust reference validpoi_catalog.id
Cascading Operations
Foreign key constraints should implement cascading rules:- ON DELETE CASCADE: Deleting a trip removes all associated stops
- ON UPDATE CASCADE: Updating IDs propagates to related records
Performance Optimization
Indexing Strategy
Recommended Indexes:Query Performance Tips
- Use EXPLAIN ANALYZE: Understand query execution plans
- Limit Result Sets: Use LIMIT for pagination
- **Avoid SELECT ***: Only fetch needed columns
- Connection Pooling: Upgrade from single client to connection pool
Database Migration Considerations
AI-Generated Field Flexibility
Since AI may generate varying itinerary structures, consider:-
JSONB Columns: Store semi-structured AI responses
-
EAV Pattern: Entity-Attribute-Value for dynamic properties
-
Schema Versioning: Track data model versions
Backup & Maintenance
Recommended Practices
-
Regular Backups:
- Point-in-Time Recovery: Enable WAL archiving
-
Vacuum & Analyze: Maintain table statistics
- Monitor Connection Pool: Prevent connection exhaustion
Related Documentation
- System Architecture Overview - Complete system design
- AI Integration - How AI generates itineraries