@everytravel/shared
Version:
A comprehensive shared package for Everytravel containing Mongoose models and CRUD operations for hotel booking, user management, and transaction handling. Updated with improved model syntax and enhanced error handling.
101 lines (95 loc) • 4.56 kB
JavaScript
import mongoose from 'mongoose';
import {
isNonEmptyString,
isLat,
isLng,
arrayOfStringsSanitizer,
trimIfString,
} from '../validation/index.js';
const { Schema } = mongoose;
/**
* Mongoose schema for Property.
*
* Represents a property such as a hotel, apartment, guesthouse, villa, or resort.
*
* Fields:
* - name: Name of the property.
* - type: Type of property (Hotel, Apartment, Guesthouse, Villa, Resort).
* - description: Description of the property.
* - location: Object containing address, city, country, zipCode, and coordinates (lat, lng).
* - metaTags: Array of strings for meta tags (e.g., "luxury", "budget").
* - facilities: Array of strings listing facilities (e.g., "wifi", "pool").
* - images: Array of image objects with url and tags.
* - policies: Object containing check-in, check-out, cancellation, children, pets, and extra beds policies.
* - starRating: Number of stars for the property.
* - owner: Reference to the Host (user who owns the property).
* - suites: Array of references to Suite documents.
*
* Timestamps are automatically managed (createdAt, updatedAt).
*/
const PropertySchema = new Schema({
name: {
type: String,
set: trimIfString,
validate: {
validator: (v) => v === undefined || v === null || isNonEmptyString(v),
message: 'name must be a non-empty string',
},
},
type: {
type: String,
enum: ['Hotel', 'Apartment', 'Guesthouse', 'Villa', 'Resort'],
required: true,
},
description: { type: String, set: trimIfString },
location: {
address: { type: String, set: trimIfString },
city: { type: String, set: trimIfString },
country: { type: String, set: trimIfString },
zipCode: { type: String, set: trimIfString },
coordinates: {
lat: { type: Number, validate: { validator: (v) => v === undefined || v === null || isLat(v), message: 'lat must be between -90 and 90' } },
lng: { type: Number, validate: { validator: (v) => v === undefined || v === null || isLng(v), message: 'lng must be between -180 and 180' } },
}
},
metaTags: { type: [String], set: arrayOfStringsSanitizer },// e.g. ["luxury", "budget", "business"]
facilities: { type: [String], set: arrayOfStringsSanitizer }, // e.g. ["wifi","bar" "pool", etc.]
images: [{
url: { type: String, set: trimIfString, validate: { validator: (v) => v === undefined || v === null || isNonEmptyString(v), message: 'images.url must be a non-empty string' } },
tags: { type: [String], set: arrayOfStringsSanitizer },
order: { type: Number, min: 0 },
isCover: { type: Boolean, default: false },
}],
policies: {
checkIn: { type: String, set: trimIfString },
checkOut: { type: String, set: trimIfString },
cancellation: { type: String, set: trimIfString },
children: { type: String, set: trimIfString },
pets: { type: String, set: trimIfString },
extraBeds: { type: String, set: trimIfString },
},
starRating: { type: Number, min: 0, max: 5 },// 1-5 stars programmatically set based on reviews
show: { type: Boolean, default: true },// Whether the property is visible to the public
softDeleted: { type: Boolean, default: false },// Whether the property is deleted (soft delete)
deletedAt: Date,// When the property was deleted (for soft delete)
owner: { type: Schema.Types.ObjectId, ref: 'Host' },
suites: [{ type: Schema.Types.ObjectId, ref: 'Suite' }],
}, { timestamps: true });
// Indexes to optimize common queries
// - Ownership and soft-deletion checks for host-scoped queries
PropertySchema.index({ owner: 1, softDeleted: 1 });
// - Public visibility filters
PropertySchema.index({ show: 1, softDeleted: 1 });
// - Filterable attributes
PropertySchema.index({ type: 1 });
PropertySchema.index({ 'location.city': 1 });
PropertySchema.index({ 'location.country': 1 });
PropertySchema.index({ metaTags: 1 }); // multikey
PropertySchema.index({ facilities: 1 }); // multikey
PropertySchema.index({ starRating: 1 });
// - Recency queries
PropertySchema.index({ createdAt: -1 });
// - Text search (optional; queries currently use regex, but this is useful if $text is adopted)
PropertySchema.index({ name: 'text', description: 'text' });
const Property = mongoose.model('Property', PropertySchema);
export default Property;