const Provider = require('../Provider.js'); const { MongoClient } = require('mongodb'); class MongoDBProvider extends Provider { constructor(manager, config) { super(manager, { name: 'mongodb', config }); } async init() { try { this.connection = await MongoClient.connect(this.config.host+this.config.database, { useUnifiedTopology: true }); this.manager.db = await this.connection.db(this.config.database); this.db = this.manager.db; this.manager.logger.write('info', `Provider ${this.name} connected.`); this._initialized = true; } catch(err) { this.manager.logger.write('error', `Provider ${this.name} failed to connect: ${err}`); } } _query(request) { /* Query object structure { type: '', -- The function to use from this class, ex. findOne collection: '', -- Which collection to query from query: { }, -- Actual search query data: { } -- If the operation is to update or insert something, this is what to insert } */ if (!this._initialized) return { error: true, message: 'MongoDB not connected' }; if (!this[request.type]) return { error: true, message: `Invalid request type, got '${request.type}'` }; if (!request.collection) return { error: true, message: `You must specify a collection to query!` }; return this[request.type](request); } remove({ collection, query }) { return new Promise((resolve, reject) => { this.db.collection(collection).remove(query, (err, result) => { if (err) reject(err); resolve(result); }); }); } insertOne({ collection, data }) { return new Promise((resolve, reject) => { this.db.collection(collection).insertOne(data, (err, result) => { if(err) reject(err); resolve(result); }); }); } /** * Find and return the first match * @memberof Database */ find({ collection, query }) { //if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`); return new Promise((resolve, reject) => { if(!this._initialized) reject(new Error('MongoDB not connected')); this.db.collection(collection).find(query, async (error, cursor) => { if(error) return reject(error); return resolve(await cursor.toArray()); }); }); } /** * Find and return the first match * * @param {String} db The collection in which the data is to be updated * @param {Object} query The filter that is used to find the data * @returns {Object} An object containing the queried data * @memberof Database */ findOne({ collection, query }) { //if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`); return new Promise((resolve, reject) => { if(!this._initialized) reject(new Error('MongoDB not connected')); this.db.collection(collection).findOne(query, async (error, item) => { if(error) return reject(error); return resolve(item); }); }); } /** * Update the first filter match. * * @param {String} db The collection in which the data is to be updated * @param {Object} filter The filter that is used to find the data * @param {Object} data The updated data * @param {Boolean} [upsert=false] Create a new entry if no match is found * @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified * @memberof Database */ updateOne({ collection, query, data, upsert = false }) { //if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`); return new Promise((resolve, reject) => { if(!this._initialized) reject(new Error('MongoDB not connected')); this.db.collection(collection).updateOne(query, { $set: data }, { upsert: upsert }, async (error, result) => { if(error) return reject(error); else { //return resolve(result) let { matchedCount, upsertedCount, modifiedCount } = result; return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount }); } }); }); } /** * Push data to an array * * @param {string} db The collection to query * @param {object} filter The filter to find the document to update * @param {object} data The data to be pushed * @param {boolean} [upsert=false] Create a new entry if no match is found * @returns * @memberof Database */ push({ collection, query, data, upsert = false }) { //if(this.manager.debug) this.manager.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`); return new Promise((resolve, reject) => { if(!this._initialized) reject(new Error('MongoDB not connected')); this.db.collection(collection).updateOne(query, { $push: data }, { upsert: upsert }, async (error, result) => { if(error) return reject(error); else return resolve(result); }); }); } /** * Find a random element from a database * * @param {string} db The collection to query * @param {object} [filter={}] The filtering object to narrow down the sample pool * @param {number} [amount=1] Amount of items to return * @returns {object} * @memberof Database */ random({ collection, query = {}, amount = 1 }) { //if(this.manager.debug) this.manager.logger.debug(`Incoming random query for ${db} with parameters ${JSON.stringify(filter)} and amount ${amount}`); if(amount > 100) amount = 100; return new Promise((resolve, reject)=>{ if(!this._initialized) reject(new Error('MongoDB not connected')); this.db.collection(collection).aggregate([{ $match: query }, { $sample: {size: amount}}], function(err, item) { if(err) return reject(err); resolve(item); }); }); } } module.exports = MongoDBProvider;