3

I have a question about how to just get a certain element of an array using MongoDB and MeteorJS. I have the following schema for the user document:

    bankList:[
       {
          id: "34567890987654345678",
          name: "xfgchjbkn",
          type: "credit"
       },
       {
          id: "09876543456789098767"
          name: "65789876t8",
          type: "debit"
       }
    ]

I first subscribe to only part of the fields in the array, specifically I gather a list of all the ids. Then I have an edit screen that should subscribe to all of the fields for a specific element in the array with a matching id. I do not want to expose the rest of the array just the single element. Currently, I use the following to first gather a list of just the ids:

   Meteor.users.find({_id: this.userId},
                        {fields:{'bankList.id': 1}});

And the following publication-subscription method to get just a specific element's information:

Publication:

  Meteor.publish("userBankAdvanced", function(bankId){
      check(bankId,String);
      if(this.userId){
           return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {'bankList.$': 1});
      }else{
           this.ready();
      }
  });

Subscription:

  this.route('edit_account', {
        path: '/edit/account/',
        waitOn: function(){
              if(Session.get("bankId")){
                    return Meteor.subscribe('userBankAdvanced',Session.get("bankId"));
        }
        return null;
        },
        data: function(){
              if(Session.get("bankId")){
                return Meteor.users.findOne();
        }
        return null;
        },
        onBeforeAction: function(){
              beforeHooks.isRevise(Session.get("bankId"));
        }
  });

The subscription method returns all of the elements of the array with all of the information. I want, for example, just this (not the entire list with all of the information):

       bankList:[
       {
          id: "34567890987654345678",
          name: "xfgchjbkn",
          type: "credit"
       }]
ggobieski
  • 139
  • 13

2 Answers2

4

It looks like you're just missing the "fields" specifier in your "userBankAdvanced" publish function. I wrote a test in meteorpad using your example and it seems to work fine. The bank id is hardcoded for simplicity there.

So instead of

return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {'bankList.$': 1});

try using

return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {fields: {'bankList.$': 1}});
Kako
  • 251
  • 2
  • 6
  • This answer basically saved my life. Is there any chance it works with findAndModify() (itself an add-on :( package). Also - do you know why this syntax is so different to the "expected" mongo docs `coll.find({},{'array':{$elemMatch: {'key':'value'}}})` – lol Nov 18 '15 at 17:40
3

No luck, in meteor the "fields" option works only one level deep. In other words there's no builtin way to include/exclude subdocument fields.

But not all is lost. You can always do it manually

Meteor.publish("userBankAdvanced", function (bankId) {
  var self = this;
  var handle = Meteor.users.find({
    _id: self.userId, "bankList.id": bankId
  }).observeChanges({
    added: function (id, fields) {
      self.added("users", id, filter(fields, bankId));
    },
    changed: function (id, fields) {
      self.changed("users", id, filter(fields, bankId));
    },
    removed: function (id) {
      self.removed("users", id);
    },
  });
  self.ready();
  self.onStop(function () {
    handle.stop();
  });
});

function filter(fields, bankId) {
  if (_.has(fields, 'bankList') {
    fields.bankList = _.filter(fields.bankList, function (bank) {
      return bank.id === bankId;
    });
  }
  return fields;
}

EDIT I updated the above code to match the question requirements. It turns out though that the Carlos answer is correct as well and it's of course much more simple, so I recommend using that one.

Tomasz Lenarcik
  • 4,507
  • 14
  • 25
  • From meteor docs on [field specifiers](http://docs.meteor.com/#fieldspecifiers), "Field operators such as $ and $elemMatch are not available on the client side yet.". This implies that they work on the server side doesn't it? – user728291 Aug 17 '14 at 22:14
  • Tested in app and both the `$` positional operator and `$elemMatch` work on server in `fields` option to find the subdocument. See the [other answer](http://stackoverflow.com/a/25353962/728291) to this question for syntax. – user728291 Aug 18 '14 at 02:27
  • Ok, you're right. This actually does work on server. I didn't know it's already implemented, sorry for misleading you. – Tomasz Lenarcik Aug 18 '14 at 06:38
  • @user728291 I updated the answer because at the beginning I misunderstood what you're trying to achieve. Carlos' answer is a better one though. – Tomasz Lenarcik Aug 18 '14 at 06:49