galactic-bot/storage/providers/Mongodb.js

341 lines
11 KiB
JavaScript
Raw Normal View History

2020-04-11 21:16:37 +02:00
const Provider = require('../Provider.js');
const { MongoClient, ObjectId } = require('mongodb');
2020-04-08 16:27:34 +02:00
2020-04-08 18:08:46 +02:00
class MongoDBProvider extends Provider {
2020-04-08 16:27:34 +02:00
constructor(manager, config) {
super(manager, {
name: 'mongodb',
config
});
2020-04-08 16:27:34 +02:00
}
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);
2020-04-08 16:27:34 +02:00
this.db = this.manager.db;
2020-04-14 17:05:27 +02:00
this.manager.logger.write('info', `Provider ${this.name} connected.`);
this._initialized = true;
2020-04-08 16:27:34 +02:00
} catch(err) {
this.manager.logger.write('error', `Provider ${this.name} failed to connect: ${err}`);
2020-04-08 16:27:34 +02:00
}
}
2020-04-17 22:18:08 +02:00
_query(request) {
2020-04-14 17:05:27 +02:00
/* Query object structure
{
2020-04-14 17:05:27 +02:00
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
upsert: bool, -- If true and no matching documents are found, insert a new one
options: { },
projection: { } -- The fields that should be returned
}
*/
2020-04-17 22:18:08 +02:00
if (!this._initialized) return { error: true, message: 'MongoDB not connected' };
if (!this[request.type]) return { error: true, message: `Invalid request type, got '${request.type}'` };
2020-05-21 10:51:44 +02:00
if (!request.collection && request.type !== 'stats') return { error: true, message: 'You must specify a collection to query!' };
2020-07-23 22:41:52 +02:00
if (request.query?._id) {
if (typeof request.query?._id === 'string') request.query._id = ObjectId(request.query._id);
else if (request.query?._id instanceof Array) request.query._id = { $in: request.query._id.map(id => ObjectId(id)) };
}
2020-04-17 22:18:08 +02:00
return this[request.type](request);
}
/**
* Fetches basic statistics about the database or collection
*
* @param {String} collection The collection to query, optional
* @param {Object} options Optional options for the stats method
* @returns
* @memberof MongoDBProvider
*/
stats({ collection, options = { } }) {
return new Promise((resolve, reject) => {
if (!collection || collection.length) {
this.db.stats(options, (err, stats) => {
if (err) return reject(err);
const { db, collections, objects, avgObjSize, dataSize } = stats;
return resolve({ db, collections, objects, averageSize: avgObjSize, dataSize });
});
} else {
this.db.collection(collection).stats(options, (err, stats) => {
if (err) return reject(err);
const { ns, size, count, avgObjSize } = stats;
return resolve({ index: ns, size, count, averageSize: avgObjSize });
});
}
});
}
/**
* Count the objects in
*
* @param {String} collection The collection to query
* @param {Object} query Only documents matching this will be counted, optional
* @param {Object} options Optional options, see mongo docs for these
* @returns
* @memberof MongoDBProvider
*/
count({ collection, query, options }) {
return new Promise((resolve, reject) => {
this.db.collection(collection).countDocuments(query, options, (error, result) => {
if (error) return reject(error);
return resolve(result);
});
});
}
/**
* Remove a document from a collection
*
* @param {String} collection The collection to remove from
* @param {Object} query Any documents matching this will be removed
* @returns
* @memberof MongoDBProvider
*/
2020-04-17 22:18:08 +02:00
remove({ collection, query }) {
return new Promise((resolve, reject) => {
this.db.collection(collection).deleteOne(query, (err, result) => {
2020-04-17 22:18:08 +02:00
if (err) return reject(err);
return resolve(result);
2020-04-17 22:18:08 +02:00
});
});
2020-04-17 22:18:08 +02:00
}
/**
* Insert one document to the collection
*
* @param {String} collection The collection to insert into
* @param {Object} data The document to insert
* @returns
* @memberof MongoDBProvider
*/
2020-04-17 22:18:08 +02:00
insertOne({ collection, data }) {
return new Promise((resolve, reject) => {
this.db.collection(collection).insertOne(data, (err, result) => {
if(err) reject(err);
resolve(result);
});
});
}
2020-04-08 16:27:34 +02:00
/**
* Find and return the first match
*
* @param {String} collection The collection to find from
* @param {Object} query Documents matching this will be returned
* @param {Object} projection Defines which fields to return, { 'key': 1, 'key2': 0 } -- key will return, key2 won't, optional
2020-04-08 16:27:34 +02:00
* @memberof Database
*/
find({ collection, query, projection }) {
2020-04-08 16:27:34 +02:00
//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'));
2020-04-12 14:37:49 +02:00
this.db.collection(collection).find(query, { projection }, async (error, cursor) => {
2020-04-08 16:27:34 +02:00
if(error) return reject(error);
return resolve(await cursor.toArray());
});
});
}
/**
* Find and return the first match
*
* @param {String} collection The collection in which the data is to be updated
2020-04-08 16:27:34 +02:00
* @param {Object} query The filter that is used to find the data
* @param {Object} projection Defines which fields to return, { 'key': 1, 'key2': 0 } -- key will return, key2 won't, optional
2020-04-08 16:27:34 +02:00
* @returns {Object} An object containing the queried data
* @memberof Database
*/
2020-06-16 00:15:13 +02:00
findOne({ collection, query, projection, sort }) {
2020-04-08 16:27:34 +02:00
//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'));
2020-04-12 14:37:49 +02:00
2020-06-16 00:15:13 +02:00
this.db.collection(collection).findOne(query, { projection, sort }, async (error, item) => {
2020-04-08 16:27:34 +02:00
if(error) return reject(error);
return resolve(item);
});
});
}
/**
* Update the first filter match.
*
* @param {String} collection The collection in which the data is to be updated
* @param {Object} query The filter that is used to find the data
2020-04-08 16:27:34 +02:00
* @param {Object} data The updated data
* @param {Boolean} [upsert=true] Create a new entry if no match is found
2020-04-08 16:27:34 +02:00
* @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified
* @memberof Database
*/
updateOne({ collection, query, data, upsert = true }) {
2020-04-08 16:27:34 +02:00
2020-04-08 18:08:46 +02:00
//if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
2020-04-08 16:27:34 +02:00
return new Promise((resolve, reject) => {
2020-04-12 14:37:49 +02:00
if(!this._initialized) reject(new Error('MongoDB not connected'));
2020-04-08 16:27:34 +02:00
this.db.collection(collection).updateOne(query, { $set: data }, { upsert }, async (error, result) => {
2020-04-08 16:27:34 +02:00
if(error) return reject(error);
//return resolve(result)
const { matchedCount, upsertedCount, modifiedCount } = result;
return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount });
2020-04-08 16:27:34 +02:00
});
});
}
/**
* Remove a document property
*
* @param {String} collection The collection to query
* @param {Object} query The filter that is used to find the data
* @param {Array} data Array of fields to remove
* @returns
* @memberof MongoDBProvider
*/
removeProperty({ collection, query, data }) {
return new Promise((resolve, reject) => {
const unset = {};
for (const field of data) unset[field] = '';
this.db.collection(collection).updateOne(query, { $unset: unset }, async (error, result) => {
if(error) return reject(error);
const { matchedCount, modifiedCount } = result;
return resolve({ matched: matchedCount, modified: modifiedCount });
});
});
}
2020-04-08 16:27:34 +02:00
/**
* Push data to an array
*
* @param {string} collection The collection to query
* @param {object} query The filter to find the document to update
2020-04-08 16:27:34 +02:00
* @param {object} data The data to be pushed
* @param {boolean} [upsert=true] Create a new entry if no match is found
2020-04-08 16:27:34 +02:00
* @returns
* @memberof Database
*/
push({ collection, query, data, upsert = true }) {
2020-04-08 16:27:34 +02:00
//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'));
2020-04-12 14:37:49 +02:00
this.db.collection(collection).updateOne(query, { $push: data }, { upsert }, async (error, result) => {
2020-04-08 16:27:34 +02:00
if(error) return reject(error);
return resolve(result);
2020-04-08 16:27:34 +02:00
});
});
}
/**
* Find a random element from a database
*
* @param {string} collection The collection to query
* @param {object} [query={}] The filtering object to narrow down the sample pool
2020-04-08 16:27:34 +02:00
* @param {number} [amount=1] Amount of items to return
* @returns {object}
* @memberof Database
*/
random({ collection, query = {}, amount = 1 }) {
2020-04-08 16:27:34 +02:00
//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) => {
2020-04-08 16:27:34 +02:00
if(!this._initialized) reject(new Error('MongoDB not connected'));
2020-04-12 14:37:49 +02:00
this.db.collection(collection).aggregate([{ $match: query }, { $sample: { size: amount } }], (err, item) => {
2020-04-08 16:27:34 +02:00
if(err) return reject(err);
return resolve(item);
2020-04-08 16:27:34 +02:00
});
});
2020-06-04 19:59:09 +02:00
}
aggregate({ collection, query }) {
return new Promise((resolve, reject) => {
if(!this._initialized) reject(new Error('MongoDB not connected'));
this.db.collection(collection).aggregate(query, (err, item) => {
if(err) return reject(err);
return resolve(item.toArray());
});
});
}
2020-04-08 16:27:34 +02:00
}
2020-04-08 18:08:46 +02:00
module.exports = MongoDBProvider;