castBulkWrite.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. 'use strict';
  2. const getDiscriminatorByValue = require('../../helpers/discriminator/getDiscriminatorByValue');
  3. const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
  4. const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
  5. const cast = require('../../cast');
  6. const castUpdate = require('../query/castUpdate');
  7. const setDefaultsOnInsert = require('../setDefaultsOnInsert');
  8. /*!
  9. * Given a model and a bulkWrite op, return a thunk that handles casting and
  10. * validating the individual op.
  11. */
  12. module.exports = function castBulkWrite(originalModel, op, options) {
  13. const now = originalModel.base.now();
  14. if (op['insertOne']) {
  15. return (callback) => {
  16. const model = decideModelByObject(originalModel, op['insertOne']['document']);
  17. const doc = new model(op['insertOne']['document']);
  18. if (model.schema.options.timestamps) {
  19. doc.initializeTimestamps();
  20. }
  21. if (options.session != null) {
  22. doc.$session(options.session);
  23. }
  24. op['insertOne']['document'] = doc;
  25. if (options.skipValidation || op['insertOne'].skipValidation) {
  26. callback(null);
  27. return;
  28. }
  29. op['insertOne']['document'].$validate({ __noPromise: true }, function(error) {
  30. if (error) {
  31. return callback(error, null);
  32. }
  33. callback(null);
  34. });
  35. };
  36. } else if (op['updateOne']) {
  37. return (callback) => {
  38. try {
  39. if (!op['updateOne']['filter']) {
  40. throw new Error('Must provide a filter object.');
  41. }
  42. if (!op['updateOne']['update']) {
  43. throw new Error('Must provide an update object.');
  44. }
  45. const model = decideModelByObject(originalModel, op['updateOne']['filter']);
  46. const schema = model.schema;
  47. const strict = options.strict != null ? options.strict : model.schema.options.strict;
  48. _addDiscriminatorToObject(schema, op['updateOne']['filter']);
  49. if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) {
  50. const createdAt = model.schema.$timestamps.createdAt;
  51. const updatedAt = model.schema.$timestamps.updatedAt;
  52. applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateOne']['update'], {});
  53. }
  54. applyTimestampsToChildren(now, op['updateOne']['update'], model.schema);
  55. if (op['updateOne'].setDefaultsOnInsert !== false) {
  56. setDefaultsOnInsert(op['updateOne']['filter'], model.schema, op['updateOne']['update'], {
  57. setDefaultsOnInsert: true,
  58. upsert: op['updateOne'].upsert
  59. });
  60. }
  61. op['updateOne']['filter'] = cast(model.schema, op['updateOne']['filter'], {
  62. strict: strict,
  63. upsert: op['updateOne'].upsert
  64. });
  65. op['updateOne']['update'] = castUpdate(model.schema, op['updateOne']['update'], {
  66. strict: strict,
  67. overwrite: false,
  68. upsert: op['updateOne'].upsert
  69. }, model, op['updateOne']['filter']);
  70. } catch (error) {
  71. return callback(error, null);
  72. }
  73. callback(null);
  74. };
  75. } else if (op['updateMany']) {
  76. return (callback) => {
  77. try {
  78. if (!op['updateMany']['filter']) {
  79. throw new Error('Must provide a filter object.');
  80. }
  81. if (!op['updateMany']['update']) {
  82. throw new Error('Must provide an update object.');
  83. }
  84. const model = decideModelByObject(originalModel, op['updateMany']['filter']);
  85. const schema = model.schema;
  86. const strict = options.strict != null ? options.strict : model.schema.options.strict;
  87. if (op['updateMany'].setDefaultsOnInsert !== false) {
  88. setDefaultsOnInsert(op['updateMany']['filter'], model.schema, op['updateMany']['update'], {
  89. setDefaultsOnInsert: true,
  90. upsert: op['updateMany'].upsert
  91. });
  92. }
  93. if (model.schema.$timestamps != null && op['updateMany'].timestamps !== false) {
  94. const createdAt = model.schema.$timestamps.createdAt;
  95. const updatedAt = model.schema.$timestamps.updatedAt;
  96. applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateMany']['update'], {});
  97. }
  98. applyTimestampsToChildren(now, op['updateMany']['update'], model.schema);
  99. _addDiscriminatorToObject(schema, op['updateMany']['filter']);
  100. op['updateMany']['filter'] = cast(model.schema, op['updateMany']['filter'], {
  101. strict: strict,
  102. upsert: op['updateMany'].upsert
  103. });
  104. op['updateMany']['update'] = castUpdate(model.schema, op['updateMany']['update'], {
  105. strict: strict,
  106. overwrite: false,
  107. upsert: op['updateMany'].upsert
  108. }, model, op['updateMany']['filter']);
  109. } catch (error) {
  110. return callback(error, null);
  111. }
  112. callback(null);
  113. };
  114. } else if (op['replaceOne']) {
  115. return (callback) => {
  116. const model = decideModelByObject(originalModel, op['replaceOne']['filter']);
  117. const schema = model.schema;
  118. const strict = options.strict != null ? options.strict : model.schema.options.strict;
  119. _addDiscriminatorToObject(schema, op['replaceOne']['filter']);
  120. try {
  121. op['replaceOne']['filter'] = cast(model.schema, op['replaceOne']['filter'], {
  122. strict: strict,
  123. upsert: op['replaceOne'].upsert
  124. });
  125. } catch (error) {
  126. return callback(error, null);
  127. }
  128. // set `skipId`, otherwise we get "_id field cannot be changed"
  129. const doc = new model(op['replaceOne']['replacement'], strict, true);
  130. if (model.schema.options.timestamps) {
  131. doc.initializeTimestamps();
  132. }
  133. if (options.session != null) {
  134. doc.$session(options.session);
  135. }
  136. op['replaceOne']['replacement'] = doc;
  137. if (options.skipValidation || op['replaceOne'].skipValidation) {
  138. op['replaceOne']['replacement'] = op['replaceOne']['replacement'].toBSON();
  139. callback(null);
  140. return;
  141. }
  142. op['replaceOne']['replacement'].$validate({ __noPromise: true }, function(error) {
  143. if (error) {
  144. return callback(error, null);
  145. }
  146. op['replaceOne']['replacement'] = op['replaceOne']['replacement'].toBSON();
  147. callback(null);
  148. });
  149. };
  150. } else if (op['deleteOne']) {
  151. return (callback) => {
  152. const model = decideModelByObject(originalModel, op['deleteOne']['filter']);
  153. const schema = model.schema;
  154. _addDiscriminatorToObject(schema, op['deleteOne']['filter']);
  155. try {
  156. op['deleteOne']['filter'] = cast(model.schema,
  157. op['deleteOne']['filter']);
  158. } catch (error) {
  159. return callback(error, null);
  160. }
  161. callback(null);
  162. };
  163. } else if (op['deleteMany']) {
  164. return (callback) => {
  165. const model = decideModelByObject(originalModel, op['deleteMany']['filter']);
  166. const schema = model.schema;
  167. _addDiscriminatorToObject(schema, op['deleteMany']['filter']);
  168. try {
  169. op['deleteMany']['filter'] = cast(model.schema,
  170. op['deleteMany']['filter']);
  171. } catch (error) {
  172. return callback(error, null);
  173. }
  174. callback(null);
  175. };
  176. } else {
  177. return (callback) => {
  178. callback(new Error('Invalid op passed to `bulkWrite()`'), null);
  179. };
  180. }
  181. };
  182. function _addDiscriminatorToObject(schema, obj) {
  183. if (schema == null) {
  184. return;
  185. }
  186. if (schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
  187. obj[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
  188. }
  189. }
  190. /*!
  191. * gets discriminator model if discriminator key is present in object
  192. */
  193. function decideModelByObject(model, object) {
  194. const discriminatorKey = model.schema.options.discriminatorKey;
  195. if (object != null && object.hasOwnProperty(discriminatorKey)) {
  196. model = getDiscriminatorByValue(model.discriminators, object[discriminatorKey]) || model;
  197. }
  198. return model;
  199. }