What is cursor? - meteor

In discover meteor,
posts: function() {
return Posts.find();
}
is used, not this:
posts: function() {
return Posts.find().fetch();
}
I tried function below, it also works, and can realtime update.
What is cursor exactly? And What is the different of above two functions?

The Meteor cursor is like a lazy version of the array of documents.
It's designed to iterate through the results of a query while not actually loading each of the documents until they are actually requested or the cursor is at the position containing the document.
Its better to think of the results of the query as a book. If you use .fetch() all the pages are printed even though you're not reading them.
The cursor prints pages as you're reading them.
Additionally the cursor has several enhancements with regards to Blaze. Content is rendered less often as minute details in a document's change are able to change the DOM section on its own, without recreating an entire object. It's easier for Blaze to interact with the cursor than an array of Javascript objects.
Additionally a cursor can be observed, a plain array of Javascript object's can't
TLDR; Cursor is just like an array of objects, but designed to be more efficient & is slightly more extended with features.

Related

How to avoid the use of mutable datastructure and use more functional approach?

I'm building in scheme a database using a wiredtiger key/value store.
To query a given table one needs to have a cursor over the table. The library recommends to re-use the cursor. The general behavior can be described by the following pseudo-code:
with db.cursor() as cursor:
cursor.get(key)
...
do_something(db)
...
During the extent of the with statment cursor can only be used in the current context. If do_something(db) needs a cursor, it must create/retrieve another cursor even if it's to query the same table. Otherwise the cursor loose its position and the continuation of do_something(db) doesn't expect.
You can work around it by always reseting the cursor, that's a waste. Instead it's preferable to keep a set of cursors ready to be used and when one can request via db.cursor() this will remove a cursor from the available cursors and return it. Once the "context" operation is finished, put it back.
The way I solve this in Python is by using a list. db.cursor() looks like:
def cursor(self):
cursor = self.cursors.pop()
yield cursor
self.cursors.append(cursor)
Which means, retrieve a cursor, send it to the current context, once the context is finished, put it back to the list of available cursors.
How can I avoid the mutation and use more functional approach?
Maybe you want parameters?
Lookup the exact construct used by your Scheme implementation.
Some implementations use:
http://srfi.schemers.org/srfi-39/srfi-39.html

meteor subscription/Publish + helper best practice

I have an app where I want to display a counter of elements I have in one of the collection.
To do so I use a helper that I call in my HTML file {{nbPosts}}
UI.registerHelper('nbPosts', function () {
return Posts.find().count();
});
But to display it I need to subscribe to the whole Posts collection.
It does not seem right to me, any suggestion to do that in a better way without sending the whole collection ?
Thanks,
It depends on whether you need this to update the data reactively (which I think is not the best idea), or not. If reactivity is not important you can just use a server method, so
Meteor.methods({
'nbPosts': function () {
return Posts.find().count();
},
});
If you need reactivity you can implement a custom publish method, just like in this example. Just keep in mind that this will be a lot more expensive in terms of server usage, and so a much less efficient.
The easiest way would be to have a collection that just keeps track of the number of posts, and update it whenever a post is inserted or removed.

Is there a way to tell meteor a collection is static (will never change)?

On my meteor project users can post events and they have to choose (via an autocomplete) in which city it will take place. I have a full list of french cities and it will never be updated.
I want to use a collection and publish-subscribes based on the input of the autocomplete because I don't want the client to download the full database (5MB). Is there a way, for performance, to tell meteor that this collection is "static"? Or does it make no difference?
Could anyone suggest a different approach?
When you "want to tell the server that a collection is static", I am aware of two potential optimizations:
Don't observe the database using a live query because the data will never change
Don't store the results of this query in the merge box because it doesn't need to be tracked and compared with other data (saving memory and CPU)
(1) is something you can do rather easily by constructing your own publish cursor. However, if any client is observing the same query, I believe Meteor will (at least in the future) optimize for that so it's still just one live query for any number of clients. As for (2), I am not aware of any straightforward way to do this because it could potentially mess up the data merging over multiple publications and subscriptions.
To avoid using a live query, you can manually add data to the publish function instead of returning a cursor, which causes the .observe() function to be called to hook up data to the subscription. Here's a simple example:
Meteor.publish(function() {
var sub = this;
var args = {}; // what you're find()ing
Foo.find(args).forEach(function(document) {
sub.added("client_collection_name", document._id, document);
});
sub.ready();
});
This will cause the data to be added to client_collection_name on the client side, which could have the same name as the collection referenced by Foo, or something different. Be aware that you can do many other things with publications (also, see the link above.)
UPDATE: To resolve issues from (2), which can be potentially very problematic depending on the size of the collection, it's necessary to bypass Meteor altogether. See https://stackoverflow.com/a/21835534/586086 for one way to do it. Another way is to just return the collection fetch()ed as a method call, although this doesn't have the benefits of compression.
From Meteor doc :
"Any change to the collection that changes the documents in a cursor will trigger a recomputation. To disable this behavior, pass {reactive: false} as an option to find."
I think this simple option is the best answer
You don't need to publish your whole collection.
1.Show autocomplete options only after user has inputted first 3 letters - this will narrow your search significantly.
2.Provide no more than 5-10 cities as options - this will keep your recordset really small - thus no need to push 5mb of data to each user.
Your publication should look like this:
Meteor.publish('pub-name', function(userInput){
var firstLetters = new RegExp('^' + userInput);
return Cities.find({name:firstLetters},{limit:10,sort:{name:1}});
});

How can I make a reactive array from a Meteor collection?

I want to take a list of item names from a collection as a simple array to use for things like autocompleting user input and checking for duplicates. I would like this list to be reactive so that changes in the data will be reflected in the array. I have tried the following based on the Meteor documentation:
setReactiveArray = (objName, Collection, field) ->
update = ->
context = new Meteor.deps.Context()
context.on_invalidate update
context.run ->
list = Collection.find({},{field: 1}).fetch()
myapp[objName] = _(list).pluck field
update()
Meteor.startup ->
if not app.items?
setReactiveArray('items', Items, 'name')
#set autocomplete using the array
Template.myForm.set_typeahead = ->
Meteor.defer ->
$('[name="item"]').typeahead {source: app.items}
This code seems to work, but it kills my app's load time (takes 5-10 seconds to load on dev/localhost vs. ~1 second without this code). Am I doing something wrong? Is there a better way to accomplish this?
You should be able to use Items.find({},{name: 1}).fetch(), which will return an array of items and is reactive, so it will re-run its enclosing function whenever the query results change, as long as it's called in a reactive context.
For the Template.myForm.set_typeahead helper, you might want to call that query inside the helper itself, store the result in a local variable, and then call Meteor.defer with a function that references that variable. Otherwise I'm not sure that the query will be inside a reactive context when it's called.
Edit: I have updated the code below both because it was fragile, and to put it in context so it's easier to test. I have also added a caution - in most cases, you will want to use #zorlak's or #englandpost's methods (see below).
First of all, kudos to #zorlak for digging up my old question that nobody answered. I have since solved this with a couple of insights gleaned from #David Wihl and will post my own solution. I will hold off on selecting the correct answer until others have a chance to weigh in.
#zorlak's answer solves the autocomplete issue for a single field, but as stated in the question, I was looking for an array that would update reactively, and the autocomplete was just one example of what it would be used for. The advantage of having this array is that it can be used anywhere (not just in template helpers) and that it can be used multiple times in the code without having to re-execute the query (and the _.pluck() that reduces the query to an array). In my case, this array ends up in multiple autocomplete fields as well as validation and other places. It's possible that the advantages I'm putting forth are not significant in most Meteor apps (please leave comments), but this seems like an advantage to me.
To make the array reactive, simply build it inside a Meteor.autorun() callback - it will re-execute any time the target collection changes (but only then, avoiding repetitive queries). This was the key insight I was looking for. Also, using the Template.rendered() callback is cleaner and less of a hack than the set_typeahead template helper I used in the question. The code below uses underscore.js's _.pluck() to extract the array from the collection and uses Twitter bootstrap's $.typeahead() to create the autocomplete.
Updated code: I have edited the code so you can try this with a stock meteor created test environment. Your html will need a line <input id="typeahead" /> in the 'hello' template. #Items has the # sign to make Items available as a global on the console (Meteor 0.6.0 added file-level variable scoping). That way you can enter new items in the console, such as Items.insert({name: "joe"}), but the # is not necessary for the code to work. The other necessary change for standalone use is that the typeahead function now sets the source to a function (->) so that it will query items when activated instead of being set at rendering, which allows it to take advantage of changes to items.
#Items = new Meteor.Collection("items")
items = {}
if Meteor.isClient
Meteor.startup ->
Meteor.autorun ->
items = _(Items.find().fetch()).pluck "name"
console.log items #first result will be empty - see caution below
Template.hello.rendered = ->
$('#typeahead').typeahead {source: -> _(Items.find().fetch()).pluck "name"}
Caution! The array we created is not itself a reactive data source. The reason that the typeahead source: needed to be set to a function -> that returned items is that when Meteor first starts, the code runs before Minimongo has gotten its data from the server, and items is set to an empty array. Minimongo then receives its data, and items is updated You can see this process if you run the above code with the console open: console.log items will log twice if you have any data stored.
Template.x.rendered() calls don't don't set a reactivity context and so won't retrigger due to changes in reactive elements (to check this, pause your code in the debugger and examine Deps.currentComputation -- if it's null, you are not in a reactive context and changes to reactive elements will be ignored). But you might be surprised to learn that your templates and helpers will also not react to items changing -- a template using #each to iterate over items will render empty and never rerender. You could make it act as a reactive source (the simplest way being to store the result with Session.set(), or you can do it yourself), but unless you are doing a very expensive calculation that should be run as seldom as possible, you are better off using #zorlak's or #englandpost's methods. It may seem expensive to have your app querying the database repetitively, but Minimongo is caching the data locally, avoiding the network, so it will be quite fast. Thus in most situations, it's better just to use
Template.hello.rendered = ->
$('#typeahead').typeahead {source: -> _(Items.find().fetch()).pluck "name"}
unless you find that your app is really bogging down.
here is my quick solution for bootstrap typeahead
On client side:
Template.items.rendered = ->
$("input#item").typeahead
source: (query, process) ->
subscription = Meteor.subscribe "autocompleteItems", query, ->
process _(Items.find().fetch()).pluck("name")
subscription.stop() # here may be a bit different logic,
# such as keeping all opened subsriptions until autocomplete
# will be successfully completed and so on
items: 5
On server side:
Meteor.publish "autocompleteItems", (query) ->
Items.find
name: new RegExp(query, "i"),
fields: { name: 1 },
limit: 5
I actually ended up approaching the autocompletion problem completely differently, using client-side code instead of querying servers. I think this is superior because Meteor's data model allows for fast multi-rule searching with custom rendered lists.
https://github.com/mizzao/meteor-autocomplete
Autocompleting users with #, where online users are shown in green:
In the same line, autocompleting something else with metadata and bootstrap icons:
Please fork, pull, and improve!

Implementing Finite State Machine for Web UI

I am intending to develop a Finite State Machine in the following manner.
Extract Control IDs from a --> web-page,
Write control IDs to a XML --> Controls-XML.
Manually declare States and Transition in the --> Controls-XML
Scan Controls-XML and attach pre-declared Jscript to eventhandlers embed them in the --> web-page..
5.
How feasible this would be..
Am I getting into can of worms ?
First, we live in a can of worms!
your questions are a bit vague, please provide more details if i'm missing your point and maybe this should be beak in parfts to deepen the discution in part details
assuming you want to work live on a client side browser over any page
by extrating ID's of controls i supouse it's all controls of a webpage (it could be by clicking or clicking possibly with key combos), but lets be simple.
Extrating ID's
here's a code than might help you:
function scan(e) {
if (e&&e.childNodes)
for(var i=0;i<e.childNodes.length;i++) {
var child=e.childNodes[i];
if (child) {
if(child.id) console.log(child.nodeName,child.id);
scan(child);
}
}
}
note: this was done with chrome, but any browser console will do i think.
just paste the function on the console and then call it like:
scan(document)
and it will list (on console) all elements that have id's showing element type and id... you can filter that eazy by just printing elements of certain tags like INPUT, SELECT TEXTAREA, etc...
About the XML
here browsers get a bit tricky, you can simplify it too your favorite browser, the following functions constructs a XML document given some XML text.
So by this aproach your scan function should compose the xml text (instead of writing to the console) and later we can feed the XML document
function makeNode(text) {
var doc;
if (window.ActiveXObject) {
doc=new ActiveXObject("Microsoft.XMLDOM");
doc.async="false";
doc.loadXML(text);
} else {// code for Mozilla, Firefox, Opera, etc.
var parser=new DOMParser();
doc=parser.parseFromString(text,"text/xml");
}// documentElement always represents the root node
return doc.documentElement;
}
other aproach is to create an empty XML document (as the above function does) and the instead of feed raw xml text your scan function should use XMLDOM commands to insert nodes into the document.
Manually declare States and Transition in the --> Controls-XML
this is an hard one, is this (XML doc) info to be copy/pasted and latter edited? is it edited inplace by inserting a bunch of code to build an interface?
many doubts here, but once we have the information in XML format we would be free to use our imagination and decide a format to handle the wanted state changes.
Scan Controls-XML and attach pre-declared Jscript to eventhandlers embed them in the --> web-page
at this point actions depend on methods followed above, but in any case using XMLDOM functions to traverse the xml doc and apply status change or event handlers to the controls is trivial.
controls need not be scanned anymore because we have stored the id's (use getElementById)
i have a form validation system that does something similar, except the XML is predefined and stored on server side, but once loaded it will do just that. attaching event handlers and do status changes based on evaluated expressions.

Resources