castBulkWrite.js 7.2 KB

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