DEV Community

Ameh Mathias Ejeh
Ameh Mathias Ejeh

Posted on

Please go through my schema

MerchantSchema

import mongoose from "mongoose";

const merchantSchema = new mongoose.Schema(
  {
    userId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "user",
      required: true,
      index: true
    },
    storeProfile: {
      storeName: {
        type: String,
        required: true,
        trim: true,
        maxLength: 100
      },
      category: {
        type: String,
        required: true,
        enum: [
          "Groceries",
          "African Store",
          "Electronics",
          "Fashion",
          "Home & Garden",
          "Pharmacy & Beauty",
          "Sports",
          "Automotive",
          "Books",
          "Other",
        ],
        default: "Other",
        index: true
      },
    },
    physicalAddress: {
      region: {
        type: String,
        required: true,
        enum: [
          "United Kingdom",
          "USA",
          "Canada",
          "UK",
          "Australia",
          "Germany",
          "France",
          "India",
          "China",
          "Japan",
          "Other",
        ],
        default: "United Kingdom",
      },
      streetAddress: {
        type: String,
        required: true,
        trim: true,
      },
      city: {
        type: String,
        required: true,
        trim: true,
      },
      postcode: {
        type: String,
        required: true,
        trim: true,
        uppercase: true,
      },
    },
    handler: {
      firstName: {
        type: String,
        required: true,
        trim: true,
      },
      lastName: {
        type: String,
        required: true,
        trim: true,
      },
      gender: {
        type: String,
        required: true,
        enum: ["Male", "Female"],
        default: "Male",
      },
      postalCode: {
        type: String,
        required: true,
        trim: true,
        uppercase: true,
      },
    },
    contactDetails: {
      email: {
        type: String,
        required: true,
        trim: true,
        lowercase: true,
        match: [
          /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
          "Please fill a valid email address",
        ],
      },
      phone: {
        countryCode: {
            type: String,
            required: true,
            default: '+44'
        },
        number: {
            type: String,
            required: true,
            trim: true
        },
        isPublic: {
            type: Boolean,
            default: false
        },
        isVerified: {
            type: Boolean,
            default: false
        },
        verifiedAt: Date
      }
    },
    phoneVerification: {
      code: {
      type: String,
      select: false
    },
    codeExpiresAt: {
      type: Date,
      select: false
    },
    attempts: {
      type: Number,
      default: 0,
      select: false
    }
    },
    operatingHours: [
      {
        day: {
          type: String,
          enum: [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ],
          required: true
        },
        isOpen: {
          type: Boolean,
          default: false
        },
        openTime: {
          type: String,
          match: /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/
        },
        closeTime: {
          type: String,
          match: /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/
      },
    }
    ],  
     media: {
      shopPhotos: [{
        url: {
          type: String,
          required: true
        },
        publicId: String, // For cloud storage reference (e.g., Cloudinary)
        type: {
          type: String,
          enum: ['image', 'video'],
          default: 'image'
        },
        isPrimary: {
          type: Boolean,
          default: false
        },
        uploadedAt: {
          type: Date,
          default: Date.now
        }
      }],
      maxPhotos: {
        type: Number,
        default: 10
      }
    },
     advertisingCredits: {
      amount: {
        type: Number,
        default: 500, // £500
        min: 0
      },
      currency: {
        type: String,
        default: 'GBP'
      },
      validUntil: Date,
      isClaimed: {
        type: Boolean,
        default: false
      },
      claimedAt: Date,
      usedAmount: {
        type: Number,
        default: 0,
        min: 0
      }
    },


    onboardingStatus: {
      currentStep: {
        type: Number,
        default: 1,
        min: 1,
        max: 12
      },
      isComplete: {
        type: Boolean,
        default: false
      },
      completedAt: Date,
      stepsCompleted: {
        type: Map,
        of: Boolean,
        default: new Map()
      }
    },
    verification: {
      status: {
        type: String,
        enum: ['pending', 'verified', 'rejected'],
        default: 'pending'
      },
      verifiedAt: Date,
      rejectionReason: String
    },
    isActive: {
      type: Boolean,
      default: true,
      index: true
    },
    defaultPassword: {
      type: String,
      select: false
    }
  },
  {
    timestamps: true,
  }
);

//Indexes for efficient querying
merchantSchema.index({ 'storeProfile.category': 1, isActive: 1 });
merchantSchema.index({ 'physicalAddress.city': 1 });
merchantSchema.index({ 'onboardingStatus.isComplete': 1 });
merchantSchema.index({ 'contactDetails.phone.number': 1 });

// Virtual for full name
merchantSchema.virtual('handler.fullName').get(function() {
  return `${this.handler.firstName} ${this.handler.lastName}`;
});

// Ensure virtuals are included in JSON
merchantSchema.set('toJSON', { virtuals: true });
merchantSchema.set('toObject', { virtuals: true });

export default mongoose.model("Merchant", merchantSchema);
Enter fullscreen mode Exit fullscreen mode

ProductSchema

import mongoose from 'mongoose';

const productSchema = new mongoose.Schema(
  {
    merchantId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'merchant',
      required: true,
      index: true
    },

    name: {
      type: String,
      required: true,
      trim: true,
      maxlength: 200
    },

    category: {
      type: String,
      required: true,
      trim: true
    },

    description: {
      type: String,
      trim: true,
      maxlength: 2000
    },

    media: {
      primary: {
        url: String,
        publicId: String,
        type: {
          type: String,
          enum: ['image', 'video']
        }
      },
      gallery: [{
        url: String,
        publicId: String,
        type: {
          type: String,
          enum: ['image', 'video']
        }
      }]
    },

    pricing: {
      price: {
        type: Number,
        min: 0
      },
      currency: {
        type: String,
        default: 'GBP'
      },

    },
      inventory: {
      quantity: {
        type: Number,
        default: 0,
        min: 0
      },
      trackInventory: {
        type: Boolean,
        default: true
      },
      lowStockThreshold: {
        type: Number,
        default: 5
      }
    },

    isActive: {
      type: Boolean,
      default: true
    },

    isFeatured: {
      type: Boolean,
      default: false
    },

    tags: [String],

    views: {
      type: Number,
      default: 0
    }
  },
  {
    timestamps: true,
  }
);

// Indexes
productSchema.index({ merchantId: 1, isActive: 1 });
productSchema.index({ category: 1, isActive: 1 });
productSchema.index({ name: 'text', description: 'text', tags: 'text' });

const Product = mongoose.model('Product', productSchema);

export default Product;
Enter fullscreen mode Exit fullscreen mode

serviceSchema

import mongoose from 'mongoose';

const serviceSchema = new mongoose.Schema(
  {
    merchantId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'merchant',
      required: true,
      index: true
    },

    name: {
      type: String,
      required: true,
      trim: true,
      maxlength: 200
    },

    category: {
      type: String,
      required: true,
      trim: true
    },

    description: {
      type: String,
      trim: true,
      maxlength: 2000
    },

    media: {
      primary: {
        url: String,
        publicId: String,
        type: {
          type: String,
          enum: ['image', 'video']
        }
      },
      gallery: [{
        url: String,
        publicId: String,
        type: {
          type: String,
          enum: ['image', 'video']
        }
      }]
    },

     booking: {
      isBookable: {
        type: Boolean,
        default: false
      },
      duration: Number, // in minutes
      maxBookingsPerDay: Number
    },

    isActive: {
      type: Boolean,
      default: true
    },

    isFeatured: {
      type: Boolean,
      default: false
    },

    tags: [String],

    views: {
      type: Number,
      default: 0
    }
  },
  {
    timestamps: true,
  }
);

// Indexes
serviceSchema.index({ merchantId: 1, isActive: 1 });
serviceSchema.index({ category: 1, isActive: 1 });
serviceSchema.index({ name: 'text', description: 'text', tags: 'text' });

serviceSchema.set('toJSON', { virtuals: true });
serviceSchema.set('toObject', { virtuals: true });

const Service = mongoose.model('Service', serviceSchema);

export default Service;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)