trackTransaction.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. 'use strict';
  2. const arrayAtomicsSymbol = require('../helpers/symbols').arrayAtomicsSymbol;
  3. const sessionNewDocuments = require('../helpers/symbols').sessionNewDocuments;
  4. module.exports = function trackTransaction(schema) {
  5. schema.pre('save', function() {
  6. const session = this.$session();
  7. if (session == null) {
  8. return;
  9. }
  10. if (session.transaction == null || session[sessionNewDocuments] == null) {
  11. return;
  12. }
  13. if (!session[sessionNewDocuments].has(this)) {
  14. const initialState = {};
  15. if (this.isNew) {
  16. initialState.isNew = true;
  17. }
  18. if (this.$__schema.options.versionKey) {
  19. initialState.versionKey = this.get(this.$__schema.options.versionKey);
  20. }
  21. initialState.modifiedPaths = new Set(Object.keys(this.$__.activePaths.states.modify));
  22. initialState.atomics = _getAtomics(this);
  23. session[sessionNewDocuments].set(this, initialState);
  24. } else {
  25. const state = session[sessionNewDocuments].get(this);
  26. for (const path of Object.keys(this.$__.activePaths.states.modify)) {
  27. state.modifiedPaths.add(path);
  28. }
  29. state.atomics = _getAtomics(this, state.atomics);
  30. }
  31. });
  32. };
  33. function _getAtomics(doc, previous) {
  34. const pathToAtomics = new Map();
  35. previous = previous || new Map();
  36. const pathsToCheck = Object.keys(doc.$__.activePaths.init).concat(Object.keys(doc.$__.activePaths.modify));
  37. for (const path of pathsToCheck) {
  38. const val = doc.$__getValue(path);
  39. if (val != null &&
  40. val instanceof Array &&
  41. val.isMongooseDocumentArray &&
  42. val.length &&
  43. val[arrayAtomicsSymbol] != null &&
  44. Object.keys(val[arrayAtomicsSymbol]).length > 0) {
  45. const existing = previous.get(path) || {};
  46. pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol]));
  47. }
  48. }
  49. const dirty = doc.$__dirty();
  50. for (const dirt of dirty) {
  51. const path = dirt.path;
  52. const val = dirt.value;
  53. if (val != null && val[arrayAtomicsSymbol] != null && Object.keys(val[arrayAtomicsSymbol]).length > 0) {
  54. const existing = previous.get(path) || {};
  55. pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol]));
  56. }
  57. }
  58. return pathToAtomics;
  59. }
  60. function mergeAtomics(destination, source) {
  61. destination = destination || {};
  62. if (source.$pullAll != null) {
  63. destination.$pullAll = (destination.$pullAll || []).concat(source.$pullAll);
  64. }
  65. if (source.$push != null) {
  66. destination.$push = destination.$push || {};
  67. destination.$push.$each = (destination.$push.$each || []).concat(source.$push.$each);
  68. }
  69. if (source.$addToSet != null) {
  70. destination.$addToSet = (destination.$addToSet || []).concat(source.$addToSet);
  71. }
  72. if (source.$set != null) {
  73. destination.$set = Object.assign(destination.$set || {}, source.$set);
  74. }
  75. return destination;
  76. }