Server can't find any documents in the collection

It seems like the server can’t find any documents in the collection I made, even though there are many documents in it… and I don’t know why it’s behaving like this.

I got a method in /imports/server/index.js

import { Notifications } from '../../api/tasks.js';
[...]
notificationsAreSeen: function(notificationId, userId) {
	if(Notifications.find({_id: notificationId, userId: userId}).count() <= 0) throw new Meteor.Error('Critical Error', 'Invalid user');

	return Notifications.update(
		{_id: notificationId},
			{$set: {seen: true}}
	);
}

I’m importing Notifications from /imports/api/tasks.js

export const Notifications = new Mongo.Collection('notifications');

if(Meteor.isServer) {
    Meteor.publish('notifications', function (userId) {
      return [
        Notifications.find({ userId: userId, seen: false })
      ];
  });
}

So I figured it might be because the server isn’t subscribed (although the client is), but I can’t access Meteor.subscribe() server-side, it says it’s undefined. I have also double-checked all values such as notificationId and userId, they’re correct.

It can run Notifications.update() and change the values successfully (if I comment out the if statement) but otherwise it’s always throwing an exception saying it can’t find anything.

Any help?

Are you sure the userId is right?

Yes I’m certain.

I have also tried running Notifications.find({}).fetch() and it doesn’t return anything of relevance, and Notifications.find({}).fetch()[0].userId (and all other fields) returns undefined.

May I suggest some refactoring to simplify and make some of the coding patterns you’ve used more compliant with the Meteor Guide:

  1. Split the collection export from the publication. So, create a new file /imports/api/Notifications.js which contains:

    import { Mongo } from 'meteor/mongo';
    export const Notifications = new Mongo.Collection('notifications');
    
  2. Move /imports/api/tasks.js into /imports/server/tasks.js and change to:

    import { Notifications } from '/imports/api/Notifications.js';
    Meteor.publish('notifications', function (userId) {
      return Notifications.find({ userId: userId, seen: false });
    });
    
  3. In your /imports/server/index.js:

    import { Notifications } from '/imports/api/Notifications.js';
    import '/imports/server/tasks.js';
    ...
    notificationsAreSeen(notificationId, userId) {
      if(Notifications.find({ _id: notificationId, userId: userId }).count() <= 0) throw new Meteor.Error('Critical Error', 'Invalid user');
      return Notifications.update({ _id: notificationId }, { $set: { seen: true } });
    }
    

    However, you should note that normally, your index.js would contain just a bunch of import statements, one of which would be the method, above, in its own file. See the Meteor Guide here.

  4. Sprinkle some console.log statements in to help you isolate other issues (invalid _ids, etc.). If you use vscode, you can debug client and server code from your desktop.

1, Show us how you’re using the Meteor.call to your method - are you waiting for the response?

Thanks for your helpful tips, I’ve now implemented your recommendations, as well as from the guide.

I’m already logging the variables etc and they’re correct, but it’s still not finding the documents.
As for the meteor call, this is my code (in body.js)

Template.App_body.events({
        'click ._Notifications > ._Overflow > a > li': function(event, template) {
            Meteor.call('notificationsAreSeen', $(event.currentTarget).attr('data-id'), Meteor.userId());
        }
    });

In the blaze template loop (where it gets the _id from)
<li data-id="{{this._id}}">

Hmm. How are documents being inserted?

What does a typical document look like if you display it through the MongoDB shell?

You should not include userId in publication and method as param. You can have that value in the method by calling Meteor.userId() and this.userId in publication.

1 Like

If that is the current user - that wasn’t clear.

He called Meteor.userId() from template, it’s current user. There is a security issue here.

2 Likes

Haha - true :slight_smile:

I am currently inserting everything manually using a third-party program.
It looks as follows:

{
    userId: 'AaJSEZ2YRC76Qypks',
    message: 'test',
    url: 'http://localhost:3000',
    avatar: 'avatar.png',
    icon: false,
    sender: false,
    createdAt: 0,
    seen: false
}

@minhna thanks for your input too, I didn’t realise that

You have no _id field. Minimongo relies on the _id field being present.

2 Likes

Just to add to this, I wasn’t aware that it was possible to have a collection without an _id field. However, it turns out that it can be done, but there are some consequences:

  1. You can’t do this if you have replica sets. For Meteor, that means no oplog-based reactivity.
  2. Meteor’s minimongo uses the _id field throughout - including for pub/sub.
  3. This ability has been deprecated since MongoDB 3.2.

It seems so unlikely that there isn’t an _id field, that I need to double-check:

@pawpey: is that document example actually from querying the database?

@robfallows It does have an _id field, I didn’t add it to my reply because I thought it was obvious it existed.

To answer your other question, here’s the output from the console when I fetch notifications

Your _id field is a Mongo ObjectID, so any operations you need to perform on it (like if(Notifications.find({ _id: notificationId, ... will require you use an ObjectID to search with.

Is notificationId definitely an ObjectID?

1 Like

Yes it is an ObjectID, so I really don’t know why it refuse to work. As a side note, it doesn’t matter what key I search for, I can remove the notificationId and only go for userId (or none at all) and it still doesn’t work.

Not sure how this can move forward without a minimal reproduction to work from in a repo.

I think I found the issue, it lays in the publication itself. I finally managed to solve this.