-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
203 lines (198 loc) · 8.44 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
const log = {
error: console.error,
debug: console.log
}
const mongodb = require("mongodb")
const MongoClient = mongodb.MongoClient
const sleep = async msec => {
return new Promise(resolve => setTimeout(resolve, msec))
}
/**
* Default config contains all the defaults.
* @type {Object}
*/
const defaultConfig = {
//connectionString: "mongodb://localhost:27017/test", //Generated automatically if not specified.
protocol: "mongodb", // if connectionString is provided this options is ignored
host: "localhost", // if connectionString is provided this options is ignored
port: 27017, // if connectionString is provided this options is ignored
database: "test", // default database to return, in 3.6 driver you can change database
maxConnectAttempts: 0, //how many times to try before giving up, 0 = never giveup.
connectRetryDelay: 5000, // how many miliseconds to wait after each failed attempt to connect
reconnect: true, // what to do if connection to database closes. (on "close" event)
log,
mongoClientOptions: {
useNewUrlParser: true,
useUnifiedTopology: true,
},
}
let _client = null
/**
* Promise (resolved) or callback will be called only once it has successfully connected to database.
* @callback mongoCallback
* @param {Error} error - if no error this one will be null.
* @param {Client} MongoClient - on success returns mongoClient, can be ignored.
*/
/** Method that tries to connect to mongo, and retries if it fails.
* callback will be called only once it has successfully connected to database.
* If no callback a promise will be returned instead.
* Possible config options check: const _defaultConfig
* @param {defaultConfig|String} [config] - config object or mongodb connection string
* @param {Function} [callback]
* @return {Promise|null} If no callback specified a promise is returned.
*/
const connectToMongo = async (config, callback) => {
if (typeof config === "function" && !callback) {
callback = config
config = {}
}
if (typeof config === "string") config = {connectionString: config}
if(config) mongo.setConfig(config)
let url = mongo.getConnectionString()
let client = new MongoClient(url, mongo.config.mongoClientOptions)
if (mongo.config.maxConnectAttempts === 0 || mongo.config.maxConnectAttempts > mongo.currentAttemptNr) {
try {
mongo.currentAttemptNr++
log.debug("Trying to connect, attempt nr: " + mongo.currentAttemptNr + "")
//connect to database
await client.connect()
client.once("disconnected", mongo.onDisconnect)
client.once("close", () => _client = null)
_client = client
log.debug("Successfully finishing mongo connect method after " + mongo.currentAttemptNr + " tries.")
mongo.currentAttemptNr = 0
if (callback) callback(null, _client)
else return _client
} catch (err) {
log.error("Cannot connect to MongoDB! Check that mongo is running..", err)
await sleep(mongo.config.connectRetryDelay)
if (callback) return connectToMongo(config, callback)
else return await connectToMongo(config)
}
} else {
log.error("Connection to mongoDB failed and will not try to connect anymore after " + mongo.currentAttemptNr + " retries. Restart needed!")
let err = new Error("Maximum connection attempts reached, giving up.")
if(callback) return callback(err)
else throw err
}
}
/**
* Handy shortcut to insert or update data (unordered) for one or many object(s).
* @param collection {String} name of the collection to instert to.
* @param data {JSON|array} single Json object or array of Json objects.
* @param {Function|Callback<BulkWriteResult>|Callback<InsertOneResult<TSchema>>} [callback] The command result callback
* @return {Promise} - If no callback, promise is returned.
*/
const saveData = (collection, data, callback) => {
//if data that we want to save is an array and has more than one item we itterate and save them in batch job.
if (Array.isArray(data) && data.length > 1) {
let operations = []
for (let i in data) {
if(data.hasOwnProperty(i)) {
let item = data[i]
if (item._id) {
let filter = {_id: item._id}
delete item._id
operations.push({updateOne: {filter, update: {$set: item}, upsert: true}})
} else operations.push({insertOne: {document: item}})
}
}
return mongo.collection(collection).bulkWrite(operations, {ordered:false}, callback)
}
//if it is an array, is must have only one item, lets get this item out of the array
if (Array.isArray(data)) data = data[0]
// save single item to db
//if _id is specified, we try to save it to database by matching _id and using upsert function
if (data._id) return mongo.collection(collection).updateOne({"_id": data._id}, {$set:data}, {"upsert": true}, callback)
else return mongo.collection(collection).insertOne(data, callback) //if it does not have _id we just insert it (insert will generate _id)
}
const mongo = {
connectToMongo,
connect: connectToMongo, //alias
currentAttemptNr: 0,
config: Object.assign({}, defaultConfig),
defaultConfig,
setConfig: config => {
if (config) {
if (config.log) {
if (config.log && config.log.error) {
log.error = config.log.error.bind ? config.log.error.bind(config.log) : config.log.error
}
if (config.log && config.log.debug) {
log.debug = config.log.debug.bind ? config.log.debug.bind(config.log) : config.log.debug
}
delete config.log
}
if (config.connectRetryDelay && typeof config.connectRetryDelay !== "number") {
delete config.connectRetryDelay
log.debug("WARN: config.connectRetryDelay should be a number. Ignoring.")
}
if (config.maxConnectAttempts && typeof config.maxConnectAttempts !== "number") {
delete config.maxConnectAttempts
log.debug("WARN: config.maxConnectAttempts should be a number. Ignoring.")
}
if(config.mongoClientOptions){
mongo.config.mongoClientOptions = Object.assign({}, defaultConfig.mongoClientOptions, mongo.config.mongoClientOptions, config.mongoClientOptions)
delete config.mongoClientOptions
}
mongo.config.connectionString = null
mongo.config = Object.assign({}, defaultConfig, mongo.config, config)
}
else mongo.config = Object.assign({}, defaultConfig, mongo.config)
},
onDisconnect: () => {
log.debug("Lost connection to Database..")
if(mongo.config.reconnect){
try{
mongo.close(true)
}catch (err){}
_client = null
log.debug("Trying to reconnect..")
mongo.connectToMongo()
}
},
/**
*
* @returns {MongoClient}
*/
client: () => _client,
/**
*
* @param database
* @returns {Db|null}
*/
db: database => mongo.client() ? mongo.client().db(database) : null,
/**
*
* @param collection
* @returns {Collection<Document>|null}
*/
collection: collection => mongo.db() ? mongo.db().collection(collection) : null,
close: (force, callback) => {
if(!mongo.client()){ // already disconnected
if(callback) return callback()
else return Promise.resolve()
}
return mongo.client().close(force, callback)
},
/**
* @returns {mongodb} mongodb reference (original)
*/
mongodb: mongodb,
ObjectID: mongodb.ObjectID,
getConnectionString: () => {
if (!mongo.config.connectionString) mongo.config.connectionString = (mongo.config.protocol) + "://" + (mongo.config.host) + ":" + (mongo.config.port) + "/" + (mongo.config.database)
return mongo.config.connectionString
},
resetConfig: () => {
mongo.currentAttemptNr = 0
_client = null
mongo.config = Object.assign({}, defaultConfig)
},
saveData,
/** Handy shortcust to clear data in one collection */
clearData: (collection, callback) => {
return mongo.collection(collection).deleteMany({}, callback)
}
}
module.exports = mongo