[Resolved] findOne returning result even if it should be undefined

I’m not sure if I’m missing something obvious here, but I just noticed a glaring issue in a certain part of my app.

I have a page that displays information according to its URL, grabbing the details of a document that matches the parameter … except that the query always returns a result, even if the query doesn’t match.

Code is simple, so hopefully easy to debug:

The Publication:

Meteor.publish('prosByUri', function(uri) {
  return Meteor.users.find({'profile.uri.active': uri}, {fields: {profile:1}});
});

The Route (with subscription):

Router.route("/:uri", {
  name: 'New',
  controller: 'NewController',
  loadingTemplate: 'Loading',
  waitOn: function () {
    return Meteor.subscribe('prosByUri', this.params.uri);
  },
  action: function () {
    this.render('New', {
      data: {
        uri: this.params.uri,
        pro: function() {
          var user = Meteor.users.findOne({});
          if(user) {
            return user;
          }else{
            Router.go('/error/404');
          }
        }
      }
    });
  }
});

The strangest thing is that I’m sure this worked fine previously, I only noticed it by chance when I pasted some random string into the URL rather than the working URI - and it still gave me a user rather than redirecting to the 404.

Logging publication I can confirm that the URI is defined correctly, and that if it’s a non-existent URI, the collection returns no results (using fetch() in the publication) - so where is the user coming from in these cases? Why isn’t it undefined?

Edit:

Ah, I’ve picked up on where the discrepancy is. If I’m logged in, and the publication finds no results, the subscription seems to use my logged in document - as if it were the result of the query. Is this expected? How do I avoid it? (I’m assuming it’s to do with the special nature of Meteor.users). I mean I could do a conditional check in the route, something like if ( Meteor.userId() === user._id ) - but I’m wondering if there’s an intended approach in these circumstances?

Try
var user = Meteor.users.findOne({'profile.uri.active': this.params.uri});

Meteor.users.findOne({}) Will find any user that is subscribed to, and you are automatically subscribed to the user you are logged in as.

Interesting, that goes against what I’d learnt about the pub/sub system. In such a case why bother using a publication at all? It’s not abstracting anything. Edit: On second thought I guess the point is that the collection should still only contain a maximum of two documents, the one we want and the one that’s logged in, so this just makes sure it’s the right one of the two?

Also, it strikes me that having a redirect in your DATA handler seems like a serious breach of separation of concerns

Perhaps, how would you recommend handling this? It just seemed like a convenient place to do the check, and as it’s not something that can be abused I hadn’t really paid much attention to it!

(I may well be swapping over to Flow Router at some point anyway, so I haven’t worked much on the routing since starting the project)

Well, you would need the publish if you are looking at any document other than your own logged in user.
The documents on the client is the sum of all publications, so you have one automatic pub for your logged in user, and one for the other user(s) you want access to.

The Meteor.users.findOne({}); doesn’t know which of the available user docs to choose, because you haven’t specified you only want the user from the url.

Yeah, I may have been a bit quick on that. With Iron router all concerns are really mixed together, so I suppose it isn’t so bad, considering.

Yea that makes sense, thanks for clarifying!

Yeah, I may have been a bit quick on that. With Iron router all concerns are really mixed together, so I suppose it isn’t so bad, considering.

Gotcha. Yes I’ve heard this is one of the issues with Iron Router generally, so something I may have to correct if I move over to Flow!

Thanks

Try including your New-template in another template, or having 2 of them side by side, and you’ll quickly discover the disadvantages of not having data and subscriptions at the template level :wink: You can do it with Iron router also, but the tutorials often encourage “bad” practice in this regard.

1 Like