Meteor parameters and where they come from - meteor

I have a question where all the parameters for the meteor functions are coming from? Things like postAttribues, id, postId, limit, etc, etc...
Meteor.publish('newPosts', function(limit) {
return Posts.find({}, {sort: {submitted: -1}, limit: limit});
});
Meteor.publish('singlePost', function(id) {
return id && Posts.find(id);
});
//related to post
Meteor.publish('comments', function(postId) {
return Comments.find({postId: postId});
});
Are they signaled from Mongo DB? It's fine and dandy to memorize these things, but it would be nice to know where these parameters are coming from and what parameters are usually available to me.
I never used any frameworks before, so this may be why I'm confused. I worked exclusively with Javascript before jumping on Meteor.
I also have the same question about Iron Router: When creating a route, we can set a route with a specific Id with /randomName/:_id and the unique code that's responsible for associating the ":_Id" with the actual page is this.params._id. Why and how does the back end associate these things?
I would appreciate any help to help me understand this better.

A meteor find() query follows the syntax find({query}, {options}) defined here: http://docs.meteor.com/#/full/find where the options parameter is an object containing sort, limit, etc... These options look similar to some Mongo operators such as .sort() and .limit() but are defined
The parameters limit and sort are part of the options parameter. It would be useful to review the documentation for Meteor found here: https://docs.mongodb.org/manual/
The parameter postId comes from the way you have defined your objects in your DB. This field is part of your query parameter which specifies what exactly to find in the DB. So by specifying a postId:, Meteor will look through your Comments collection for any containing the postId that you pass. When you pass a string as the query parameter, it is expected that that string is an _id in your collection.
For the parameters being passed into the publication itself see docs.meteor.com/#/full/meteor_subscribe . It comes from the subscription. Basically, you can pass. Parameters between the client and the server this way. To make your publication more robust, you can add parameters as you wish so that the client can specify which 'id' or 'limit' that they want.
As for your iron:router question, I am not sure exactly what you are asking about how the backend associates parameters and the page itself. Perhaps you could be more specific and update your question accordingly

Related

Meteor Observer Reuse: Will changing my publication return query to not use this.userId make my observer more reusable?

I have a publication that returns a cursor that looks like this:
// Publication RETURNS THIS QUERY
Notepads.find({_id: notepadId, $or: [{userId: this.userId}, {'collaborators.userId': this.userId}], archived: false})
As you can see the query is unique to the user since it includes this.userId
I use this.userId in the publication as a form of security. You will only get back data if you are associated with that particular notepad.
In my app, multiple people can collaborate on a single notepad. So to make the observer more reusable will this adjustment help my app?
// Optimized publication
notepad = Notepads.findOne({_id: notepadId, $or: [{userId: #userId}, {'collaborators.userId': #userId}], archived: false})
if notepad?
// Optimized publication RETURNS THIS QUERY
return Notepads.find({_id: notepad._id, archived: false})
else
return
I think this is what observer reuse means. That the publication returns the exact same query for any user that subscribes to it. So is this correct, is this optimization worth the change?
There are problem with your optimized version that is it is not reactive. Because you use Notepads.findOne as a security check to make sure user have access to the document.
Inside publication .findOne is not reactive, say at the time the publication is executed user have no access to the document so nothing is sent to client, then user is added as an collaborator but that makes no change because the publication will re re-run.
Quoting from kadira.io on How Observers are Handled in Meteor:
In order to create identical observers, you need to create cursors
with:
the same collection
the same selector (query)
the same set of options (including sort, limit, etc.)
In your example the selector is not the same for different users.
Selectors are Object Literals and are evaluated before being passed into the .find call. This means that {_id: notepad._id, archived: false} becomes
{_id: 'myNotebookID', archived: false}, or
{_id: 'anotherNotebookId', archived: false}.
As you can see, these are different selectors after you resolve notepad._id, and this happens before being passed to .find().
If your first db query had returned the same notepad because your second user was a collaborator on the first user's notebook, you would end up with the same selector, and a single cursor / observable. However for most apps this is not likely to be common enough to optimise for, especially as (as #Khang points out) you are going to lose reactivity.
So, it's not the same query, it's not going to reuse the observer, and is not worth the change.

How to get just the ID from this._id when this._id returns an object?

I'm doing the newbie tutorial 'simple-todo' and noticed that once I added security in step 9, I was no longer able to delete tasks created before that.
The issue is that my remove method is checking to make sure that the ID it receives is a string, and the to-do tasks that were made earlier via the console return an object when I use this_.id.
In other words:
Tasks created via the terminal, this._id -> ObjectId("57a128afbe5fd7e7ba9a6fca")
Tasks created with the Tasks.insert method, this._id -> "57a128afbe5fd7e7ba9a6fca"
And the new remove method doesn't like the ObjectId part. How can I get just the ID? I would figure it'd be something like this._id._id, but that's undefined. The workaround was to remove the check from the "remove" method, which is less secure.
Link: https://www.meteor.com/tutorials/blaze/security-with-methods
You can use this._id._str to get the Hex part of the ObjectId.
I would suggest that your method only uses the string, and do a check in the client to see if you need to use this._id or this._id._str

Meteor - check() VS new SimpleSchema() for verifying .publish() arguments

To ensure the type of the arguments my publications receive, should I use SimpleSchema or check()?
Meteor.publish('todos.inList', function(listId, limit) {
new SimpleSchema({
listId: { type: String },
limit: { type: Number }
}).validate({ listId, limit });
[...]
});
or
Meteor.publish('todos.inList', function(listId, limit) {
check(listId, String);
check (limit, Number);
[...]
});
check() allows you to check data type, which is one thing, but is somewhat limited.
SimpleSchema is much more powerful as it checks all keys in a documents (instead of one at a time) and lets you define not only type but also allowed values, define default (or dynamic) values when not present.
You should use SimpleSchema this way:
mySchema = new SimpleSchema({ <your schema here>});
var MyCollection = new Mongo.Collection("my_collection");
MyCollection.attachSchema(mySchema);
That way, you don't event need to check the schema in methods: it will be done automatically.
Of course it's always good practice to use the
mySchema.validate(document);
to validate a client generated document before inserting it in your collection, but if you don't and your document doesn't match the schema (extra keys, wrong types etc...) SimpleSchema will reject the parts that don't belong.
To check arguments to a publish() function or a Meteor.method() use check(). You define a SimpleSchema to validate inserts, upserts, and updates to collections. A publication is none of those - it's readonly. Your use of SimpleSchema with .validate() inline would actually work but it's a pretty unusual pattern and a bit of overkill.
You might find this helpful.
CHECK is a lightweight package for argument checking and general pattern matching. where as SimpleSchema is a huge package with one of the check features. It is just that one package was made before the other.
Both works the same. You can use CHECK externally in Meteor.methods as well. Decision is all yours.
Michel Floyd answer's, made me realize that check() actually sends Meteor.Error(400, "Match Failed") to the client, while SimpleSchema within Methods sends detailed ValidationError one can act upon to display appropriate error messages on a form for instance.
So to answer the question : should we use check() or SimpleSchema() to assess our arguments types in Meteor, I believe the answer is :
Use SimpleSchema if you need a detailed report of the error from the client, otherwise check() is the way to go not to send back critical info.

How can I use collection.find as a result of a meteor method?

I'm trying to follow the "Use the return value of a Meteor method in a template helper" pattern outlined here, except with collections.
Essentially, I've got something like this going:
(server side)
Meteor.methods({
queryTest: function(selector) {
console.log("In server meteor method...");
return MyCollection.find(selector);
}
});
(client side)
Meteor.call('queryTest', {}, function(error, results) {
console.log("in queryTest client callback...");
queryResult = [];
results.forEach(function(result) {
// massage it into something more useful for display
// and append it to queryResult...
});
Session.set("query-result", queryResult);
});
Template.query_test_template.helpers({
query_test_result: function() {
return Session.get("query-result");
}
});
The problem is, my callback (from Meteor.call) doesn't even get invoked.
If I replace the Method with just 'return "foo"' then the callback does get called. Also, if I add a ".fetch()" to the find, it also displays fine (but is no longer reactive, which breaks everything else).
What gives? Why is the callback not being invoked? I feel like I'm really close and just need the right incantation...
If it at all matters: I was doing all the queries on the client side just fine, but want to experiment with the likes of _ensureIndex and do full text searches, which from what I can tell, are basically only available through server-side method calls (and not in mini-mongo on the client).
EDIT
Ok, so I migrated things publish/subscribe, and overall they're working, but when I try to make it so a session value is the selector, it's not working right. Might be a matter of where I put the "subscribe".
So, I have a publish that takes a parameter "selector" (the intent is to pass in mongo selectors).
On the client, I have subscribe like:
Meteor.subscribe('my-collection-query', Session.get("my-collection-query-filter"));
But it has spotty behaviour. On one article, it recommended putting these on Templates.body.onCreate. That works, but doesn't result in something reactive (i.e. when I change that session value on the console, it doesn't change the displayed value).
So, if I follow the advice on another article, it puts the subscribe right in the relevant helper function of the template that calls on that collection. That works great, but if I have MULTIPLE templates calling into that collection, I have to add the subscribe to every single one of them for it to work.
Neither of these seems like the right thing. I think of "subscribing" as "laying down the pipes and just leaving them there to work", but that may be wrong.
I'll keep reading into the docs. Maybe somewhere, the scope of a subscription is properly explained.
You need to publish your data and subscribe to it in your client.
If you did not remove "autopublish" yet, all what you have will automatically be published. So when you query a collection on client (in a helper method for example), you would get results. This package is useful just for quick development and prototyping, but in a real application it should be removed. You should publish your data according to your app's needs and use cases. (Not all users have to see all data in all use cases)

Meteor - subscribe to same collection twice - keep results separate?

I have a situation in which I need to subscribe to the same collection twice. The two publish methods in my server-side code are as follows:
Meteor.publish("selected_full_mycollection", function (important_id_list) {
check(important_id_list, Match.Any); // should do better check
// this will return the full doc, including a very long array it contains
return MyCollection.find({
important_id: {$in: important_id_list}
});
});
Meteor.publish("all_brief_mycollection", function() {
// this will return all documents, but only the id and first item in the array
return MyCollection.find({}, {fields: {
important_id: 1,
very_long_array: {$slice: 1}
}});
});
My problem is that I am not seeing the full documents on the client end after I subscribe to them. I think this is because they are being over-written by the method that publishes only the brief versions.
I don't want to clog up my client memory with long arrays when I don't need them, but I do want them available when I do need them.
The brief version is subscribed to on startup. The full version is subscribed to when the user visits a template that drills down for more insight.
How can I properly manage this situation?
TL/DR - skip to the third paragraph.
I'd speculate that this is because the publish function thinks that the very_long_array field has already been sent to the client, so it doesn't send it again. You'd have to fiddle around a bit to confirm this, but sending different data on the same field is bound to cause some problems.
In terms of subscribing on two collections, you're not supposed to be able to do this as the unique mongo collection name needs to be provided to the client and server-side collections object. In practice, you might be able to do something really hacky by making one client subscription a fake remote subscription via DDP and having it populate a totally separate Javascript object. However, this cannot be the best option.
This situation would be resolved by publishing your summary on something other than the same field. Unfortunately, you can't use transforms when returning cursors from a publish function (which would be the easiest way), but you have two options:
Use the low-level publications API as detailed in this answer.
Use collection hooks to populate another field (like very_long_array_summary) with the first item in the array whenever very_long_array changes and publish just the summary field in the former publication.
A third option might be publishing the long version to a different collection that exists for this purpose on the client only. You might want to check the "Advanced Pub/Sub" Chapter of Discover Meteor (last sub chapter).

Resources