Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReactiveAggregate returns Mongo types instead of Meteor #50

Open
abuszta opened this issue Oct 16, 2018 · 0 comments
Open

ReactiveAggregate returns Mongo types instead of Meteor #50

abuszta opened this issue Oct 16, 2018 · 0 comments

Comments

@abuszta
Copy link

abuszta commented Oct 16, 2018

Hello,

Because meteor-reactive-aggregate is using rawCollection to call aggregate on collection, result contains plain Mongo types instead of Meteor ones. It is a problem for custom EJSON types returned in aggregate or when ObjectID or Decimal128 is used (see Scale Factor aggregation in https://docs.mongodb.com/manual/tutorial/model-monetary-data/).

This can be fixed by adding same code which is used in meteor/mongo.js for converting types:

diff --git a/aggregate.js b/aggregate.js
index b7e7cc9..2cb5683 100644
--- a/aggregate.js
+++ b/aggregate.js
@@ -1,6 +1,81 @@
 import { Meteor } from 'meteor/meteor';
 import { Mongo } from 'meteor/mongo';
 
+// FIXME: Copied this from mongo.js
+import { NpmModuleMongodb as MongoDB } from 'meteor/npm-mongo';
+import { EJSON } from 'meteor/ejson';
+
+var replaceNames = function (filter, thing) {
+  if (typeof thing === "object" && thing !== null) {
+    if (_.isArray(thing)) {
+      return _.map(thing, _.bind(replaceNames, null, filter));
+    }
+
+    var ret = {};
+
+    _.each(thing, function (value, key) {
+      ret[filter(key)] = replaceNames(filter, value);
+    });
+
+    return ret;
+  }
+
+  return thing;
+};
+
+var unmakeMongoLegal = function (name) {
+  return name.substr(5);
+};
+
+var replaceMongoAtomWithMeteor = function (document) {
+  if (document instanceof MongoDB.Binary) {
+    var buffer = document.value(true);
+    return new Uint8Array(buffer);
+  }
+
+  if (document instanceof MongoDB.ObjectID) {
+    return new Mongo.ObjectID(document.toHexString());
+  }
+
+  if (document instanceof MongoDB.Decimal128) {
+    return Decimal(document.toString());
+  }
+
+  if (document["EJSON$type"] && document["EJSON$value"] && _.size(document) === 2) {
+    return EJSON.fromJSONValue(replaceNames(unmakeMongoLegal, document));
+  }
+
+  if (document instanceof MongoDB.Timestamp) {
+    // For now, the Meteor representation of a Mongo timestamp type (not a date!
+    // this is a weird internal thing used in the oplog!) is the same as the
+    // Mongo representation. We need to do this explicitly or else we would do a
+    // structural clone and lose the prototype.
+    return document;
+  }
+
+  return undefined;
+};
+
+var replaceTypes = function (document, atomTransformer) {
+  if (typeof document !== 'object' || document === null) return document;
+  var replacedTopLevelAtom = atomTransformer(document);
+  if (replacedTopLevelAtom !== undefined) return replacedTopLevelAtom;
+  var ret = document;
+
+  _.each(document, function (val, key) {
+    var valReplaced = replaceTypes(val, atomTransformer);
+
+    if (val !== valReplaced) {
+      // Lazy clone. Shallow copy.
+      if (ret === document) ret = _.clone(document);
+      ret[key] = valReplaced;
+    }
+  });
+
+  return ret;
+};
+//FIXME: END
+
 const defaultOptions = ({
   collection, options
 }) => ({
@@ -48,6 +123,7 @@ export const ReactiveAggregate = function (subscription, collection, pipeline =
       }
       // cursor is not done iterating, add and update documents on the client
       else {
+        doc = replaceTypes(doc, replaceMongoAtomWithMeteor);
         if (!subscription._ids[doc._id]) {
           subscription.added(clientCollection, doc._id, doc);
         } else {

I'm not sure however how to solve this without copy-paste.

Antoni.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant