sanket3280/code-execution
0
1const mongoose = require('mongoose');2const zlib = require('zlib');3 4/**5 * OPTIMIZED UserCode Schema6 * 7 * One document per (user, problem, language) combination8 * 9 * Benefits:10 * - Small documents (10-20 KB vs 100-200 KB)11 * - Fast atomic updates (no array rewrite)12 * - Easy deletion (deleteOne vs $pull)13 * - Better indexing and query performance14 * - Optional compression for large code15 */16const UserCodeSchema = new mongoose.Schema({17 userId: {18 type: mongoose.Schema.Types.ObjectId,19 ref: 'User',20 required: true,21 index: true22 },23 challengeId: {24 type: mongoose.Schema.Types.ObjectId,25 ref: 'Challenge',26 default: null, // null = practice mode27 index: true28 },29 problemId: {30 type: mongoose.Schema.Types.ObjectId,31 ref: 'Problem',32 required: true,33 index: true34 },35 language: {36 type: String,37 required: true,38 index: true39 },40 code: {41 type: String,42 default: '',43 validate: {44 validator: function(v) {45 return Buffer.byteLength(v, 'utf8') <= 102400; // 100KB limit46 },47 message: 'Code exceeds maximum size of 100KB'48 }49 },50 customInput: {51 type: String,52 default: ''53 },54 lastSavedAt: {55 type: Date,56 default: Date.now,57 index: true58 },59 clientTimestamp: {60 type: Date,61 default: Date.now62 },63 isSubmitted: {64 type: Boolean,65 default: false66 },67 deleted: {68 type: Boolean,69 default: false,70 index: true71 },72 // Optional: track if code is compressed73 isCompressed: {74 type: Boolean,75 default: false76 },77 createdAt: {78 type: Date,79 default: Date.now80 }81}, {82 timestamps: { updatedAt: true, createdAt: false }83});84 85// NEW: Compound unique index with challengeId - ensures one code per (user, challenge, problem, language)86// null challengeId is treated as a distinct value (practice mode)87UserCodeSchema.index(88 { userId: 1, challengeId: 1, problemId: 1, language: 1 },89 { unique: true }90);91 92// Index for cleanup queries93UserCodeSchema.index({ deleted: 1, lastSavedAt: 1 });94 95// Index for challenge deletion cascade96UserCodeSchema.index({ challengeId: 1 });97 98// TTL index - automatically delete documents after 2 days (172800 seconds)99UserCodeSchema.index(100 { lastSavedAt: 1 },101 { expireAfterSeconds: 172800 }102);103 104/**105 * Compress code before saving (optional)106 * Reduces storage by 40-60% for large code107 */108UserCodeSchema.methods.compressCode = function() {109 if (!this.isCompressed && this.code && this.code.length > 1000) {110 try {111 const compressed = zlib.gzipSync(this.code).toString('base64');112 this.code = compressed;113 this.isCompressed = true;114 } catch (err) {115 console.error('Compression failed:', err);116 }117 }118};119 120/**121 * Decompress code after loading122 */123UserCodeSchema.methods.decompressCode = function() {124 if (this.isCompressed && this.code) {125 try {126 const decompressed = zlib.gunzipSync(127 Buffer.from(this.code, 'base64')128 ).toString();129 this.code = decompressed;130 this.isCompressed = false;131 } catch (err) {132 console.error('Decompression failed:', err);133 }134 }135};136 137/**138 * Static method to get code with auto-decompression139 */140UserCodeSchema.statics.findAndDecompress = async function(query) {141 const doc = await this.findOne(query);142 if (doc && doc.isCompressed) {143 doc.decompressCode();144 }145 return doc;146};147 148// Export UserCode model149module.exports = mongoose.model('UserCode', UserCodeSchema);