@@ -105,6 +105,13 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
105105
106106 s . safe = s . safe !== false ;
107107 s . w = s . w || 1 ;
108+ s . writeConcern = s . writeConcern || {
109+ w : s . w ,
110+ wtimeout : s . wtimeout || null ,
111+ j : s . j || null ,
112+ journal : s . journal || null ,
113+ fsync : s . fsync || null ,
114+ } ;
108115 s . url = s . url || generateMongoDBURL ( s ) ;
109116 s . useNewUrlParser = s . useNewUrlParser !== false ;
110117 s . useUnifiedTopology = s . useUnifiedTopology !== false ;
@@ -251,9 +258,6 @@ MongoDB.prototype.connect = function(callback) {
251258 'acceptableLatencyMS' ,
252259 'connectWithNoPrimary' ,
253260 'authSource' ,
254- 'w' ,
255- 'wtimeout' ,
256- 'j' ,
257261 'forceServerObjectId' ,
258262 'serializeFunctions' ,
259263 'ignoreUndefined' ,
@@ -278,13 +282,13 @@ MongoDB.prototype.connect = function(callback) {
278282 'password' ,
279283 'authMechanism' ,
280284 'compression' ,
281- 'fsync' ,
282285 'readPreferenceTags' ,
283286 'numberOfRetries' ,
284287 'auto_reconnect' ,
285288 'minSize' ,
286289 'useNewUrlParser' ,
287290 'useUnifiedTopology' ,
291+ 'writeConcern' ,
288292 // Ignored options
289293 'native_parser' ,
290294 // Legacy options
@@ -293,6 +297,11 @@ MongoDB.prototype.connect = function(callback) {
293297 'replSet' ,
294298 'mongos' ,
295299 'db' ,
300+ 'w' ,
301+ 'wtimeout' ,
302+ 'j' ,
303+ 'journal' ,
304+ 'fsync' ,
296305 ] ;
297306
298307 const lbOptions = Object . keys ( self . settings ) ;
@@ -683,7 +692,7 @@ MongoDB.prototype.exists = function(modelName, id, options, callback) {
683692 debug ( 'exists' , modelName , id ) ;
684693 }
685694 id = self . coerceId ( modelName , id , options ) ;
686- this . execute ( modelName , 'findOne' , { _id : id } , function ( err , data ) {
695+ this . execute ( modelName , 'findOne' , { _id : id } , buildOptions ( { } , options ) , function ( err , data ) {
687696 if ( self . debug ) {
688697 debug ( 'exists.callback' , modelName , id , err , data ) ;
689698 }
@@ -704,7 +713,7 @@ MongoDB.prototype.find = function find(modelName, id, options, callback) {
704713 }
705714 const idName = self . idName ( modelName ) ;
706715 const oid = self . coerceId ( modelName , id , options ) ;
707- this . execute ( modelName , 'findOne' , { _id : oid } , function ( err , data ) {
716+ this . execute ( modelName , 'findOne' , { _id : oid } , buildOptions ( { } , options ) , function ( err , data ) {
708717 if ( self . debug ) {
709718 debug ( 'find.callback' , modelName , id , err , data ) ;
710719 }
@@ -893,7 +902,7 @@ MongoDB.prototype.destroy = function destroy(modelName, id, options, callback) {
893902 debug ( 'delete' , modelName , id ) ;
894903 }
895904 id = self . coerceId ( modelName , id , options ) ;
896- this . execute ( modelName , 'deleteOne' , { _id : id } , function ( err , result ) {
905+ this . execute ( modelName , 'deleteOne' , { _id : id } , buildOptions ( { } , options ) , function ( err , result ) {
897906 if ( self . debug ) {
898907 debug ( 'delete.callback' , modelName , id , err , result ) ;
899908 }
@@ -1034,6 +1043,16 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) {
10341043
10351044 query [ k ] = { $regex : cond } ;
10361045 } else {
1046+ if ( isObjectIDProperty ( modelCtor , propDef , cond , options ) ) {
1047+ if ( Array . isArray ( cond ) ) {
1048+ cond = cond . map ( function ( c ) {
1049+ return ObjectID ( c ) ;
1050+ } ) ;
1051+ } else {
1052+ cond = ObjectID ( cond ) ;
1053+ }
1054+ }
1055+
10371056 query [ k ] = { } ;
10381057 query [ k ] [ '$' + spec ] = cond ;
10391058 }
@@ -1044,8 +1063,15 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) {
10441063 query [ k ] = { $type : 10 } ;
10451064 } else {
10461065 if ( isObjectIDProperty ( modelCtor , propDef , cond , options ) ) {
1047- cond = ObjectID ( cond ) ;
1066+ if ( Array . isArray ( cond ) ) {
1067+ cond = cond . map ( function ( c ) {
1068+ return ObjectID ( c ) ;
1069+ } ) ;
1070+ } else {
1071+ cond = ObjectID ( cond ) ;
1072+ }
10481073 }
1074+
10491075 query [ k ] = cond ;
10501076 }
10511077 }
@@ -1312,8 +1338,17 @@ MongoDB.prototype.convertColumnNames = function(model, data, direction) {
13121338 }
13131339
13141340 if ( direction === 'database' ) {
1315- data [ columnName ] = data [ propName ] ;
1316- delete data [ propName ] ;
1341+ // Handle data is Array object - in case of fields filter
1342+ if ( Array . isArray ( data ) ) {
1343+ const idx = data . indexOf ( propName ) ;
1344+ if ( idx !== - 1 ) {
1345+ data . push ( columnName ) ;
1346+ delete data [ idx ] ;
1347+ }
1348+ } else { // Handle data as Object - in case to create / update
1349+ data [ columnName ] = data [ propName ] ;
1350+ delete data [ propName ] ;
1351+ }
13171352 }
13181353
13191354 if ( direction === 'property' ) {
@@ -1351,17 +1386,23 @@ MongoDB.prototype.all = function all(modelName, filter, options, callback) {
13511386 if ( filter . where ) {
13521387 query = self . buildWhere ( modelName , filter . where , options ) ;
13531388 }
1354- let fields = filter . fields ;
1389+ // Use Object.assign to avoid change filter.fields
1390+ // which will cause error when create model from data
1391+ let fields = undefined ;
1392+ if ( typeof filter . fields !== 'undefined' ) {
1393+ fields = [ ] ;
1394+ Object . assign ( fields , filter . fields ) ;
1395+ }
13551396
13561397 // Convert custom column names
13571398 fields = self . fromPropertyToDatabaseNames ( modelName , fields ) ;
13581399
1400+ options = buildOptions ( { } , options ) ;
1401+
13591402 if ( fields ) {
1360- const findOpts = { projection : fieldsArrayToObj ( fields ) } ;
1361- this . execute ( modelName , 'find' , query , findOpts , processResponse ) ;
1362- } else {
1363- this . execute ( modelName , 'find' , query , processResponse ) ;
1403+ options . projection = fieldsArrayToObj ( fields ) ;
13641404 }
1405+ this . execute ( modelName , 'find' , query , options , processResponse ) ;
13651406
13661407 function processResponse ( err , cursor ) {
13671408 if ( err ) {
@@ -1461,7 +1502,7 @@ MongoDB.prototype.destroyAll = function destroyAll(
14611502 where = self . buildWhere ( modelName , where , options ) ;
14621503 if ( debug . enabled ) debug ( 'destroyAll where %s' , util . inspect ( where ) ) ;
14631504
1464- this . execute ( modelName , 'deleteMany' , where || { } , function ( err , info ) {
1505+ this . execute ( modelName , 'deleteMany' , where || { } , buildOptions ( { } , options ) , function ( err , info ) {
14651506 if ( err ) return callback && callback ( err ) ;
14661507
14671508 if ( self . debug ) debug ( 'destroyAll.callback' , modelName , where , err , info ) ;
@@ -1488,15 +1529,26 @@ MongoDB.prototype.count = function count(modelName, where, options, callback) {
14881529 debug ( 'count' , modelName , where ) ;
14891530 }
14901531 where = self . buildWhere ( modelName , where , options ) || { } ;
1491- const method = Object . keys ( where ) . length === 0 ? 'estimatedDocumentCount' : 'countDocuments' ;
1492- this . execute ( modelName , method , where , function ( err , count ) {
1493- if ( self . debug ) {
1494- debug ( 'count.callback' , modelName , err , count ) ;
1495- }
1496- if ( callback ) {
1497- callback ( err , count ) ;
1498- }
1499- } ) ;
1532+ options = buildOptions ( { } , options ) ;
1533+ if ( Object . keys ( where ) . length === 0 && ! options . session ) {
1534+ this . execute ( modelName , 'estimatedDocumentCount' , function ( err , count ) {
1535+ if ( self . debug ) {
1536+ debug ( 'count.callback' , modelName , err , count ) ;
1537+ }
1538+ if ( callback ) {
1539+ callback ( err , count ) ;
1540+ }
1541+ } ) ;
1542+ } else {
1543+ this . execute ( modelName , 'countDocuments' , where , options , function ( err , count ) {
1544+ if ( self . debug ) {
1545+ debug ( 'count.callback' , modelName , err , count ) ;
1546+ }
1547+ if ( callback ) {
1548+ callback ( err , count ) ;
1549+ }
1550+ } ) ;
1551+ }
15001552} ;
15011553
15021554/**
@@ -1538,7 +1590,7 @@ MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb
15381590 const idName = self . idName ( modelName ) ;
15391591 delete data [ idName ] ;
15401592 data = self . toDatabase ( modelName , data ) ;
1541- this . execute ( modelName , 'replaceOne' , { _id : id } , data , options , function (
1593+ this . execute ( modelName , 'replaceOne' , { _id : id } , data , buildOptions ( { } , options ) , function (
15421594 err ,
15431595 info ,
15441596 ) {
@@ -1735,11 +1787,11 @@ MongoDB.prototype.upsertWithWhere = function upsertWithWhere(
17351787 'findOneAndUpdate' ,
17361788 where ,
17371789 updateData ,
1738- {
1790+ buildOptions ( {
17391791 upsert : true ,
17401792 returnOriginal : false ,
17411793 sort : [ [ '_id' , 'asc' ] ] ,
1742- } ,
1794+ } , options ) ,
17431795 function ( err , result ) {
17441796 if ( err ) return cb && cb ( err ) ;
17451797
@@ -2015,6 +2067,48 @@ MongoDB.prototype.ping = function(cb) {
20152067 }
20162068} ;
20172069
2070+ MongoDB . prototype . beginTransaction = function ( isolationLevel , cb ) {
2071+ // TODO: think about how to convert READ_COMMITED, etc. to transactionOptions
2072+ const transactionOptions = {
2073+ readPreference : 'primary' ,
2074+ readConcern : { level : 'local' } ,
2075+ writeConcern : { w : 'majority' } ,
2076+ } ;
2077+ if ( isolationLevel instanceof Object ) {
2078+ Object . assign ( transactionOptions , isolationLevel || { } ) ;
2079+ }
2080+ const session = this . client . startSession ( ) ;
2081+ session . startTransaction ( transactionOptions ) ;
2082+ cb ( null , session ) ;
2083+ } ;
2084+
2085+ MongoDB . prototype . commit = function ( tx , cb ) {
2086+ tx . commitTransaction ( function ( err ) {
2087+ tx . endSession ( null , function ( error ) {
2088+ if ( err ) return cb ( err ) ;
2089+ if ( error ) return cb ( error ) ;
2090+ cb ( ) ;
2091+ } ) ;
2092+ } ) ;
2093+ } ;
2094+
2095+ MongoDB . prototype . rollback = function ( tx , cb ) {
2096+ tx . abortTransaction ( function ( err ) {
2097+ tx . endSession ( null , function ( error ) {
2098+ if ( err ) return cb ( err ) ;
2099+ if ( error ) return cb ( error ) ;
2100+ cb ( ) ;
2101+ } ) ;
2102+ } ) ;
2103+ } ;
2104+
2105+ function isInTransation ( options ) {
2106+ const ops = { } ;
2107+ if ( options && options . transaction && options . transaction . isInTransation )
2108+ ops . session = options . transaction . session ;
2109+ return ops ;
2110+ }
2111+
20182112// Case insensitive check if a string looks like "ObjectID"
20192113function typeIsObjectId ( input ) {
20202114 if ( ! input ) return false ;
@@ -2072,7 +2166,8 @@ function coerceToObjectId(modelCtor, propDef, propValue) {
20722166function isObjectIDProperty ( modelCtor , propDef , value , options ) {
20732167 if ( ! propDef ) return false ;
20742168
2075- if ( typeof value === 'string' && value . match ( ObjectIdValueRegex ) ) {
2169+ if ( ( typeof value === 'string' && value . match ( ObjectIdValueRegex ) ) ||
2170+ ( Array . isArray ( value ) && value . every ( ( v ) => v . match ( ObjectIdValueRegex ) ) ) ) {
20762171 if ( isStoredAsObjectID ( propDef ) ) return true ;
20772172 else return ! isStrictObjectIDCoercionEnabled ( modelCtor , options ) ;
20782173 } else if ( value instanceof mongodb . ObjectID ) {
@@ -2306,5 +2401,9 @@ function hasDataType(dataType, propertyDef) {
23062401* @param {* } connectorOptions User specified Options
23072402*/
23082403function buildOptions ( requiredOptions , connectorOptions ) {
2309- return Object . assign ( { } , connectorOptions , requiredOptions ) ;
2404+ if ( connectorOptions && connectorOptions . transaction && connectorOptions . transaction . isActive ( ) ) {
2405+ return Object . assign ( { session : connectorOptions . transaction . connection } , connectorOptions , requiredOptions ) ;
2406+ } else {
2407+ return Object . assign ( { } , connectorOptions , requiredOptions ) ;
2408+ }
23102409}
0 commit comments