@@ -105,6 +105,13 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
105
105
106
106
s . safe = s . safe !== false ;
107
107
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
+ } ;
108
115
s . url = s . url || generateMongoDBURL ( s ) ;
109
116
s . useNewUrlParser = s . useNewUrlParser !== false ;
110
117
s . useUnifiedTopology = s . useUnifiedTopology !== false ;
@@ -251,9 +258,6 @@ MongoDB.prototype.connect = function(callback) {
251
258
'acceptableLatencyMS' ,
252
259
'connectWithNoPrimary' ,
253
260
'authSource' ,
254
- 'w' ,
255
- 'wtimeout' ,
256
- 'j' ,
257
261
'forceServerObjectId' ,
258
262
'serializeFunctions' ,
259
263
'ignoreUndefined' ,
@@ -278,13 +282,13 @@ MongoDB.prototype.connect = function(callback) {
278
282
'password' ,
279
283
'authMechanism' ,
280
284
'compression' ,
281
- 'fsync' ,
282
285
'readPreferenceTags' ,
283
286
'numberOfRetries' ,
284
287
'auto_reconnect' ,
285
288
'minSize' ,
286
289
'useNewUrlParser' ,
287
290
'useUnifiedTopology' ,
291
+ 'writeConcern' ,
288
292
// Ignored options
289
293
'native_parser' ,
290
294
// Legacy options
@@ -293,6 +297,11 @@ MongoDB.prototype.connect = function(callback) {
293
297
'replSet' ,
294
298
'mongos' ,
295
299
'db' ,
300
+ 'w' ,
301
+ 'wtimeout' ,
302
+ 'j' ,
303
+ 'journal' ,
304
+ 'fsync' ,
296
305
] ;
297
306
298
307
const lbOptions = Object . keys ( self . settings ) ;
@@ -683,7 +692,7 @@ MongoDB.prototype.exists = function(modelName, id, options, callback) {
683
692
debug ( 'exists' , modelName , id ) ;
684
693
}
685
694
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 ) {
687
696
if ( self . debug ) {
688
697
debug ( 'exists.callback' , modelName , id , err , data ) ;
689
698
}
@@ -704,7 +713,7 @@ MongoDB.prototype.find = function find(modelName, id, options, callback) {
704
713
}
705
714
const idName = self . idName ( modelName ) ;
706
715
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 ) {
708
717
if ( self . debug ) {
709
718
debug ( 'find.callback' , modelName , id , err , data ) ;
710
719
}
@@ -893,7 +902,7 @@ MongoDB.prototype.destroy = function destroy(modelName, id, options, callback) {
893
902
debug ( 'delete' , modelName , id ) ;
894
903
}
895
904
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 ) {
897
906
if ( self . debug ) {
898
907
debug ( 'delete.callback' , modelName , id , err , result ) ;
899
908
}
@@ -1034,6 +1043,16 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) {
1034
1043
1035
1044
query [ k ] = { $regex : cond } ;
1036
1045
} 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
+
1037
1056
query [ k ] = { } ;
1038
1057
query [ k ] [ '$' + spec ] = cond ;
1039
1058
}
@@ -1044,8 +1063,15 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) {
1044
1063
query [ k ] = { $type : 10 } ;
1045
1064
} else {
1046
1065
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
+ }
1048
1073
}
1074
+
1049
1075
query [ k ] = cond ;
1050
1076
}
1051
1077
}
@@ -1312,8 +1338,17 @@ MongoDB.prototype.convertColumnNames = function(model, data, direction) {
1312
1338
}
1313
1339
1314
1340
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
+ }
1317
1352
}
1318
1353
1319
1354
if ( direction === 'property' ) {
@@ -1351,17 +1386,23 @@ MongoDB.prototype.all = function all(modelName, filter, options, callback) {
1351
1386
if ( filter . where ) {
1352
1387
query = self . buildWhere ( modelName , filter . where , options ) ;
1353
1388
}
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
+ }
1355
1396
1356
1397
// Convert custom column names
1357
1398
fields = self . fromPropertyToDatabaseNames ( modelName , fields ) ;
1358
1399
1400
+ options = buildOptions ( { } , options ) ;
1401
+
1359
1402
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 ) ;
1364
1404
}
1405
+ this . execute ( modelName , 'find' , query , options , processResponse ) ;
1365
1406
1366
1407
function processResponse ( err , cursor ) {
1367
1408
if ( err ) {
@@ -1461,7 +1502,7 @@ MongoDB.prototype.destroyAll = function destroyAll(
1461
1502
where = self . buildWhere ( modelName , where , options ) ;
1462
1503
if ( debug . enabled ) debug ( 'destroyAll where %s' , util . inspect ( where ) ) ;
1463
1504
1464
- this . execute ( modelName , 'deleteMany' , where || { } , function ( err , info ) {
1505
+ this . execute ( modelName , 'deleteMany' , where || { } , buildOptions ( { } , options ) , function ( err , info ) {
1465
1506
if ( err ) return callback && callback ( err ) ;
1466
1507
1467
1508
if ( self . debug ) debug ( 'destroyAll.callback' , modelName , where , err , info ) ;
@@ -1488,15 +1529,26 @@ MongoDB.prototype.count = function count(modelName, where, options, callback) {
1488
1529
debug ( 'count' , modelName , where ) ;
1489
1530
}
1490
1531
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
+ }
1500
1552
} ;
1501
1553
1502
1554
/**
@@ -1538,7 +1590,7 @@ MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb
1538
1590
const idName = self . idName ( modelName ) ;
1539
1591
delete data [ idName ] ;
1540
1592
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 (
1542
1594
err ,
1543
1595
info ,
1544
1596
) {
@@ -1735,11 +1787,11 @@ MongoDB.prototype.upsertWithWhere = function upsertWithWhere(
1735
1787
'findOneAndUpdate' ,
1736
1788
where ,
1737
1789
updateData ,
1738
- {
1790
+ buildOptions ( {
1739
1791
upsert : true ,
1740
1792
returnOriginal : false ,
1741
1793
sort : [ [ '_id' , 'asc' ] ] ,
1742
- } ,
1794
+ } , options ) ,
1743
1795
function ( err , result ) {
1744
1796
if ( err ) return cb && cb ( err ) ;
1745
1797
@@ -2015,6 +2067,48 @@ MongoDB.prototype.ping = function(cb) {
2015
2067
}
2016
2068
} ;
2017
2069
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
+
2018
2112
// Case insensitive check if a string looks like "ObjectID"
2019
2113
function typeIsObjectId ( input ) {
2020
2114
if ( ! input ) return false ;
@@ -2072,7 +2166,8 @@ function coerceToObjectId(modelCtor, propDef, propValue) {
2072
2166
function isObjectIDProperty ( modelCtor , propDef , value , options ) {
2073
2167
if ( ! propDef ) return false ;
2074
2168
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 ) ) ) ) {
2076
2171
if ( isStoredAsObjectID ( propDef ) ) return true ;
2077
2172
else return ! isStrictObjectIDCoercionEnabled ( modelCtor , options ) ;
2078
2173
} else if ( value instanceof mongodb . ObjectID ) {
@@ -2306,5 +2401,9 @@ function hasDataType(dataType, propertyDef) {
2306
2401
* @param {* } connectorOptions User specified Options
2307
2402
*/
2308
2403
function 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
+ }
2310
2409
}
0 commit comments