0
votes

I'm very keen to utilize Meteor as the framework for my next project. However, there is a requirement to keep customer data separated into different MongoDB instances for users from different customers.

I have read on this thread that it could be as simple as using this:

var d = new MongoInternals.RemoteCollectionDriver("<mongo url>");
C = new Mongo.Collection("<collection name>", { _driver: d });

However, I was dished this error on my server/server.js. I'm using meteor 0.9.2.2 with meteor-platform 1.1.0.

Exception from sub Ep9DL57K7F2H2hTBz Error: A method named '/documents/insert' is already defined
    at packages/ddp/livedata_server.js:1439
    at Function._.each._.forEach (packages/underscore/underscore.js:113)
    at _.extend.methods (packages/ddp/livedata_server.js:1437)
    at Mongo.Collection._defineMutationMethods (packages/mongo/collection.js:888)
    at new Mongo.Collection (packages/mongo/collection.js:208)
    at Function.Documents.getCollectionByMongoUrl (app/server/models/documents.js:9:30)
    at null._handler (app/server/server.js:12:20)
    at maybeAuditArgumentChecks (packages/ddp/livedata_server.js:1594)
    at _.extend._runHandler (packages/ddp/livedata_server.js:943)
    at packages/ddp/livedata_server.js:737

Can anyone be so kind as to enlighten me whether or not I have made a mistake somewhere?

Thanks.

Br, Ethan

Edit: This is my server.js

Meteor.publish('userDocuments', function () {   
    // Get company data store's mongo URL here. Simulate by matching domain of user's email.
    var user = Meteor.users.findOne({ _id: this.userId });
    if (!user || !user.emails) return;

    var email = user.emails[0].address;
    var mongoUrl = (email.indexOf('@gmail.com') >= 0) ? 
        'mongodb://localhost:3001/company-a-db' :
        'mongodb://localhost:3001/company-b-db';

    // Return documents
    return Documents.getCollectionByMongoUrl(mongoUrl).find();
});

and this is the server side model.js

Documents = function () { };

var documentCollections = { };

Documents.getCollectionByMongoUrl = function (url) {
    if (!(url in documentCollections)) {
        var driver = new MongoInternals.RemoteCollectionDriver(url);
        documentCollections[url] = new Meteor.Collection("documents", { _driver: driver });
    }

    return documentCollections[url];
};

Observation: The first attempt to new a Meteor.Collection works fine. I can continue to use that collection multiple times. But when I log out and login as another user from another company (in this example by using an email that is not from @gmail.com), the error above is thrown.

2

2 Answers

0
votes

I believe Meteor distinguish its collections internally by the name you pass to them as the first argument, so when you create the "documents" collection the second time, it tries to override the structure. Hence the error when trying to create the /documents/insert method the second time.

To work around this, you could apply a suffix to your collection name. So instead of:

new Meteor.Collection('documents', { _driver: driver });

you should try:

new Meteor.Collection('documents_' + userId, { _driver: driver })
1
votes

Downloaded meteor's source codes and peeked into mongo package. There is a way to hack around having to declare different collection names on the mongodb server based on Hubert's suggestion.

In the server side model.js, I've made these adaptation:

Documents.getCollectionByMongoUrl = function (userId, url) {
    if (!(userId in documentCollections)) {
        var driver = new MongoInternals.RemoteCollectionDriver(url);
        documentCollections[userId] = new Meteor.Collection("documents" + userId, { _driver: driver });
        documentCollections[userId]._connection = driver.open("documents", documentCollections[userId]._connection);
    }

    return documentCollections[userId];
};

Super hack job here. Be careful when using this!!!!