opusdev/vector-similarity-api
1
1const { MongoClient, ObjectId } = require('mongodb');2require('dotenv').config();3 4// MongoDB connection5const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017';6const DB_NAME = 'social_media_db';7const COLLECTION_NAME = 'posts';8const COMMENTS_COLLECTION = 'comments';9const SAVES_COLLECTION = 'saved_posts';10const SHARES_COLLECTION = 'shares';11 12let client = null;13let db = null;14let collection = null;15let commentsCollection = null;16let savesCollection = null;17let sharesCollection = null;18 19// Initialize MongoDB connection20async function initMongoDB() {21 if (client && client.topology && client.topology.isConnected()) {22 return collection;23 }24 25 try {26 client = new MongoClient(MONGO_URI);27 await client.connect();28 console.log('✓ Connected to MongoDB');29 30 db = client.db(DB_NAME);31 collection = db.collection(COLLECTION_NAME);32 commentsCollection = db.collection(COMMENTS_COLLECTION);33 savesCollection = db.collection(SAVES_COLLECTION);34 sharesCollection = db.collection(SHARES_COLLECTION);35 36 // Create indexes on post_id37 await collection.createIndex({ post_id: 1 }, { unique: true });38 await commentsCollection.createIndex({ post_id: 1 });39 await commentsCollection.createIndex({ user_id: 1 });40 await commentsCollection.createIndex({ created_at: -1 });41 await savesCollection.createIndex({ post_id: 1 });42 await savesCollection.createIndex({ user_id: 1 });43 await savesCollection.createIndex({ user_id: 1, saved_at: -1 });44 await savesCollection.createIndex({ user_id: 1, post_id: 1 }, { unique: true });45 await sharesCollection.createIndex({ post_id: 1 });46 await sharesCollection.createIndex({ shared_by: 1 });47 await sharesCollection.createIndex({ shared_by: 1, shared_at: -1 });48 49 return collection;50 } catch (error) {51 console.error('MongoDB connection error:', error);52 throw error;53 }54}55 56// Post class with static methods57class Post {58 static async getCollection() {59 if (!collection) {60 await initMongoDB();61 }62 return collection;63 }64 65 static async getAllPosts() {66 const coll = await this.getCollection();67 const posts = await coll.find({}).sort({ created_at: -1 }).toArray();68 return posts;69 }70 71 static async getPost(postId) {72 const coll = await this.getCollection();73 const post = await coll.findOne({ post_id: postId });74 return post;75 }76 77 static async createPost({ post_id, name, caption, media_url, media_type = 'image', category = 'unknown' }) {78 const coll = await this.getCollection();79 80 const postDoc = {81 post_id,82 name,83 caption,84 media_url,85 media_type,86 category,87 created_at: new Date(),88 updated_at: new Date()89 };90 91 const result = await coll.insertOne(postDoc);92 return { ...postDoc, _id: result.insertedId };93 }94 95 static async updatePost(postId, updateData) {96 const coll = await this.getCollection();97 98 const result = await coll.updateOne(99 { post_id: postId },100 {101 $set: {102 ...updateData,103 updated_at: new Date()104 }105 }106 );107 108 if (result.matchedCount === 0) {109 return null;110 }111 112 return await this.getPost(postId);113 }114 115 static async deletePost(postId) {116 const coll = await this.getCollection();117 const result = await coll.deleteOne({ post_id: postId });118 return result.deletedCount > 0;119 }120 121 static async searchPosts(query) {122 const coll = await this.getCollection();123 const posts = await coll.find({124 $or: [125 { name: { $regex: query, $options: 'i' } },126 { caption: { $regex: query, $options: 'i' } }127 ]128 }).toArray();129 return posts;130 }131 132 static async getPostCount() {133 const coll = await this.getCollection();134 return await coll.countDocuments();135 }136 137 static async syncMissingCategories(mapping) {138 const coll = await this.getCollection();139 console.log('Syncing categories in MongoDB...');140 for (const [postId, category] of Object.entries(mapping)) {141 await coll.updateOne(142 { post_id: postId, category: { $exists: false } },143 { $set: { category: category } }144 );145 // Also update "unknown" categories146 await coll.updateOne(147 { post_id: postId, category: "unknown" },148 { $set: { category: category } }149 );150 }151 console.log('✓ Category sync complete');152 }153 154 // ===== COMMENTS METHODS =====155 static async addComment(postId, userId, commentText) {156 await this.getCollection(); // Ensure DB is initialized157 const comment = {158 _id: new ObjectId(),159 post_id: postId,160 user_id: userId,161 text: commentText,162 created_at: new Date(),163 likes: 0164 };165 await commentsCollection.insertOne(comment);166 return comment;167 }168 169 static async getComments(postId) {170 await this.getCollection();171 return await commentsCollection.find({ post_id: postId })172 .sort({ created_at: -1 })173 .toArray();174 }175 176 static async deleteComment(commentId) {177 await this.getCollection();178 const result = await commentsCollection.deleteOne({ _id: new ObjectId(commentId) });179 return result.deletedCount > 0;180 }181 182 static async getCommentCount(postId) {183 await this.getCollection();184 return await commentsCollection.countDocuments({ post_id: postId });185 }186 187 // ===== SAVES METHODS =====188 static async savePost(postId, userId) {189 await this.getCollection();190 const savedPost = {191 _id: new ObjectId(),192 post_id: postId,193 user_id: userId,194 saved_at: new Date()195 };196 await savesCollection.insertOne(savedPost);197 return savedPost;198 }199 200 static async removeSavedPost(postId, userId) {201 await this.getCollection();202 const result = await savesCollection.deleteOne({203 post_id: postId,204 user_id: userId205 });206 return result.deletedCount > 0;207 }208 209 static async getSavedPosts(userId) {210 await this.getCollection();211 const saved = await savesCollection.find({ user_id: userId })212 .sort({ saved_at: -1 })213 .toArray();214 215 if (!saved.length) return [];216 217 const postIds = saved.map((item) => item.post_id);218 const posts = await collection219 .find({ post_id: { $in: postIds } })220 .toArray();221 222 const postMap = new Map(posts.map((post) => [post.post_id, post]));223 return saved224 .map((savedItem) => {225 const post = postMap.get(savedItem.post_id);226 return post ? { ...post, saved_at: savedItem.saved_at } : null;227 })228 .filter(Boolean);229 }230 231 static async isPostSaved(postId, userId) {232 await this.getCollection();233 const saved = await savesCollection.findOne({234 post_id: postId,235 user_id: userId236 });237 return !!saved;238 }239 240 static async getSaveCount(postId) {241 await this.getCollection();242 return await savesCollection.countDocuments({ post_id: postId });243 }244 245 // ===== SHARES METHODS =====246 static async sharePost(postId, sharedBy, platform = 'direct') {247 await this.getCollection();248 const share = {249 _id: new ObjectId(),250 post_id: postId,251 shared_by: sharedBy,252 platform: platform,253 shared_at: new Date()254 };255 await sharesCollection.insertOne(share);256 return share;257 }258 259 static async getShares(postId) {260 await this.getCollection();261 return await sharesCollection.find({ post_id: postId })262 .sort({ shared_at: -1 })263 .toArray();264 }265 266 static async getShareCount(postId) {267 await this.getCollection();268 return await sharesCollection.countDocuments({ post_id: postId });269 }270 271 static async getUserShares(userId) {272 await this.getCollection();273 return await sharesCollection.find({ shared_by: userId })274 .sort({ shared_at: -1 })275 .toArray();276 }277}278 279// Graceful shutdown280process.on('SIGINT', async () => {281 if (client) {282 await client.close();283 console.log('MongoDB connection closed');284 }285 process.exit(0);286});287 288module.exports = Post;