castBulkWrite.js 7.5 KB

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