2020-04-11 21:16:37 +02:00
const Provider = require ( '../Provider.js' ) ;
2020-07-04 12:23:10 +02:00
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 ) {
2020-04-16 14:37:04 +02:00
super ( manager , {
name : 'mongodb' ,
config
} ) ;
2020-04-08 16:27:34 +02:00
}
async init ( ) {
try {
2020-04-16 14:37:04 +02:00
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. ` ) ;
2020-04-16 14:37:04 +02:00
this . _initialized = true ;
2020-04-08 16:27:34 +02:00
} catch ( err ) {
2020-04-16 14:37:04 +02:00
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
/ * Q u e r y o b j e c t s t r u c t u r e
2020-04-14 10:26:59 +02:00
{
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
2020-05-06 01:40:46 +02:00
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-14 10:26:59 +02:00
}
* /
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 ) ;
}
2020-05-06 01:40:46 +02:00
/ * *
* 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 ) {
2020-05-21 12:47:58 +02:00
this . db . stats ( options , ( err , stats ) => {
2020-05-06 01:40:46 +02:00
if ( err ) return reject ( err ) ;
2020-05-21 12:47:58 +02:00
const { db , collections , objects , avgObjSize , dataSize } = stats ;
return resolve ( { db , collections , objects , averageSize : avgObjSize , dataSize } ) ;
2020-05-06 01:40:46 +02:00
} ) ;
} else {
this . db . collection ( collection ) . stats ( options , ( err , stats ) => {
if ( err ) return reject ( err ) ;
2020-05-21 12:47:58 +02:00
const { ns , size , count , avgObjSize } = stats ;
return resolve ( { index : ns , size , count , averageSize : avgObjSize } ) ;
2020-05-06 01:40:46 +02:00
} ) ;
}
} ) ;
}
/ * *
* 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 ) => {
2020-05-21 12:47:58 +02:00
if ( error ) return reject ( error ) ;
return resolve ( result ) ;
2020-05-06 01:40:46 +02:00
} ) ;
} ) ;
}
/ * *
* 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 ) => {
2020-05-06 01:40:46 +02:00
this . db . collection ( collection ) . deleteOne ( query , ( err , result ) => {
2020-04-17 22:18:08 +02:00
2020-05-06 01:40:46 +02:00
if ( err ) return reject ( err ) ;
return resolve ( result ) ;
2020-04-17 22:18:08 +02:00
} ) ;
} ) ;
2020-04-14 10:26:59 +02:00
2020-04-17 22:18:08 +02:00
}
2020-05-06 01:40:46 +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-14 10:26:59 +02:00
}
2020-04-08 16:27:34 +02:00
/ * *
* Find and return the first match
2020-05-06 01:40:46 +02:00
*
* @ 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
* /
2020-05-06 01:40:46 +02:00
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 ) => {
2020-04-17 17:23:13 +02:00
if ( ! this . _initialized ) reject ( new Error ( 'MongoDB not connected' ) ) ;
2020-04-12 14:37:49 +02:00
2020-05-06 01:40:46 +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
*
2020-05-06 01:40:46 +02:00
* @ 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
2020-05-06 01:40:46 +02:00
* @ 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 ) => {
2020-04-17 17:23:13 +02:00
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 .
*
2020-05-06 01:40:46 +02:00
* @ 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
2020-05-06 01:40:46 +02:00
* @ 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
* /
2020-05-06 01:40:46 +02:00
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
2020-04-17 17:23:13 +02:00
if ( ! this . _initialized ) reject ( new Error ( 'MongoDB not connected' ) ) ;
2020-04-08 16:27:34 +02:00
2020-05-21 12:47:58 +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 ) ;
2020-05-21 12:47:58 +02:00
//return resolve(result)
const { matchedCount , upsertedCount , modifiedCount } = result ;
return resolve ( { matched : matchedCount , upserted : upsertedCount , modified : modifiedCount } ) ;
2020-04-08 16:27:34 +02:00
} ) ;
} ) ;
}
2020-05-06 01:40:46 +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 ) => {
2020-05-21 12:47:58 +02:00
const unset = { } ;
for ( const field of data ) unset [ field ] = '' ;
2020-05-06 01:40:46 +02:00
this . db . collection ( collection ) . updateOne ( query , { $unset : unset } , async ( error , result ) => {
if ( error ) return reject ( error ) ;
2020-05-21 12:47:58 +02:00
2020-05-06 01:40:46 +02:00
2020-05-21 12:47:58 +02:00
const { matchedCount , modifiedCount } = result ;
return resolve ( { matched : matchedCount , modified : modifiedCount } ) ;
2020-05-06 01:40:46 +02:00
} ) ;
} ) ;
}
2020-04-08 16:27:34 +02:00
/ * *
* Push data to an array
*
2020-05-06 01:40:46 +02:00
* @ 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
2020-05-06 01:40:46 +02:00
* @ param { boolean } [ upsert = true ] Create a new entry if no match is found
2020-04-08 16:27:34 +02:00
* @ returns
* @ memberof Database
* /
2020-05-06 01:40:46 +02:00
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 ) => {
2020-04-17 17:23:13 +02:00
if ( ! this . _initialized ) reject ( new Error ( 'MongoDB not connected' ) ) ;
2020-04-12 14:37:49 +02:00
2020-05-21 12:47:58 +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 ) ;
2020-05-21 12:47:58 +02:00
return resolve ( result ) ;
2020-04-08 16:27:34 +02:00
} ) ;
} ) ;
}
/ * *
* Find a random element from a database
*
2020-05-06 01:40:46 +02:00
* @ 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
* /
2020-04-14 10:26:59 +02:00
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 ;
2020-05-21 12:47:58 +02:00
return new Promise ( ( resolve , reject ) => {
2020-04-08 16:27:34 +02:00
2020-04-17 17:23:13 +02:00
if ( ! this . _initialized ) reject ( new Error ( 'MongoDB not connected' ) ) ;
2020-04-12 14:37:49 +02:00
2020-05-21 12:47:58 +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 ) ;
2020-05-21 12:47:58 +02:00
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 ;