Meteor uses NoSQL document-oriented storage and it comes by default with Mongo DB. The name comes form the word "humongous", meaning extremely large. The database is part of a NoSQL database family, meaning, it does not store the data as traditional relational databases. Mongo DB persists the date in a JSON-like document format, making the integration with JavaScript-based frameworks a lot easier. In this recipe, we are going to see how to use the database from Meteor, and how data access is orchestrated.
There is an icon.png
image in the example files; besides that, only Meteor is needed to be installed on your machine and to have an open command line.
movies
from the command line:>meteor create movies
To simplify the generated structure, we will create two folders: one called server
and another called client
. The movies.css
, movies.js
, and movies.html
files can be placed in the client
directory, as we will putting client-related code there.
server
directory, we create a file called bootstrap.js
that will initialize the database with the few objects that we will define:Meteor.startup(function () { if (Movies.find().count() === 0) { var data = [ { name: "North by northwest", score: "9.9" }, { name: "Gone with the wind", score:"8.3" }, { name: "1984", score: "9.9" } ]; var timestamp = (new Date()).getTime(); for (var i = 0; i < data.length; i++) { var itemId = Movies.insert({ name: data[i].name, score: data[i].score, time: timestamp }); } } });
Movies
object? It is a collection that we will define in a different file that can be called publish.js
, as in it we are going to publish the collection from the server. This file will include the following:Movies = new Meteor.Collection("movies"); Meteor.publish('movies', function () { return Movies.find(); });
<body> <div id="main"> {{> movies}} </div> </body> <template name="movies"> <h3>List of favorite movies</h3> <div id="lists"> <div> <ul> {{#each movies}} <li><b>{{name}}</b> {{score}}<li/> {{/each}} </ul> <button> <img src="icon.png" width="30px" height="30px" /> </button> </div> </div> </template>
In order to make the icon.png
image available as a static file, we need to create a folder named public
and place the image there. This follows the principle of convention over configuration, and there is no real need why you should not follow it, at least most of the time.
movies.js
file, we should automatically subscribe to the servers
collection of movies. Also, we will add a functionality to fill the movies
variable and add an event also for the button that will trigger a save of a random new movie:// Define mongo style collections to match server/publish.js. Movies = new Meteor.Collection("movies"); // Always be subscribed to the movies list. Meteor.autorun(function () { Meteor.subscribe('movies'), }); // fill the movies variable with data from the collection sorted by name Template.movies.movies = function () { return Movies.find({}, {sort: {name: 1}}); }; // on click we insert a random movie Template.movies.events({ 'click button': function(){ Movies.insert({ name: "random awesome movie", score: Math.random() * 10 }); } });
meteor
, we can access it in the browser on the default port http://localhost:3000/
. If we want to change the port on which the application runs, for example on port 3333
, we can use the following command:meteor --port 3333
We can first start with the data, if we have the server running, we can open up another console, where we can access the same directory. Then, after opening the same folder in the console, we run the following command:
meteor mongo MongoDB shell version: 2.2.3 connecting to: 127.0.0.1:3002/meteor
That opens up a simple console on which we can query our database. Mongo stores the data as collections, and in order to get the names of all of our available movies, we can use the following command:
> db.getCollectionNames() [ "movies", "system.indexes" ]
The movies
collection is the one we defined in our bootstrap.js
initialization; as for system.indexes
, it is a collection contains all the indexes of the database. To manipulate data with this collection, we can use ensureIndex()
and dropIndex()
.
In the console, we can assign the following variables:
> var x = db.getCollection("movies"); > x meteor.movies
Collections can be queried with find()
; if we try to call it without arguments, it returns all the elements:
> x.find(); { "name" : "North by northwest", "score" : "9.9", "time" : 1360630048083, "_id" : "bc8f1a7a-71bd-49a9-b6d9-ed0d782db89d" } { "name" : "Gone with the wind", "score" : "8.3", "time" : 1360630048083, "_id" : "1d7f1c43-3108-4cc5-8fbf-fc8fa10ef6e2" } { "name" : "1984", "score" : "9.9", "time" : 1360630048083, "_id" : "08633d22-aa0b-454f-a6d8-aa2aaad2fbb1" } ...
The data is basic JSON, making it easy to manipulate with JavaScript. If you take a look at the objects, you can notice the "_id" : "08633d22-aa0b-454f-a6d8-aa2aaad2fbb1"
key-value pair. This is a unique key generated by Mongo and we use it to reference and manipulate that object, commonly referred to as
document.
If we wanted to delete the record with an ID of beef20a3-c66d-474b-af32-aa3e6503f0de
, we can use the following command:
> db.movies.remove({"_id":"beef20a3-c66d-474b-af32-aa3e6503f0de"});
After that, we can call db.movies.find()
to see to notice that one is now missing. There are plenty of other commands used for data manipulation, but most of them are intuitive, and you can easily guess by their names. As a quick reminder and a learning tool, there is a help
function that can be called:
>help >db.help()
These two bring up a list of commands and a short explanation of what each does. You should not get overwhelmed by the number of commands, as we will not use most of them, but it still makes a good reference.
For a more detailed tutorial on MongoDB commands, visit http://mongodb.org and click on TRY IT OUT to try the online shell. There are tones of resources for NoSQL on the Web, but one great introduction done by Martin Flower is available at http://www.youtube.com/watch?v=qI_g07C_Q5I.
If we open a browser, we may notice that on every click on the Random button, a new record is added instantly. This looks extremely fast and it is not just because the server is running locally. Every time a client issues a write to the server, it instantly updates the local cache without response from the server if the write went successful. When the server receives the request and accepts the update, then the client does not have to do anything on the screen. This should happen most of the time and it saves the round trip waiting time, making the screen more responsive. On the other hand, if the server rejects the update, the client's cache gets updated with the correct result.
In Meteor, the same API is used for the client and the server in order to access the database. Emphasis is given to reducing the time for round trips to the server for every design decision in the framework. Requests and responses, as well as message invalidation, are orchestrated to do this.
We used autorun
to automatically get updates from the server in our movies.js
:
Meteor.autorun(function () { Meteor.subscribe('movies'), });
The block of code in the
autorun
function is a so-called
reactive context, enabling us to write code in an imperative style, but get a reactive behavior.
Reactive programming
is one of the programming paradigms that are oriented around propagation of change. In imperative programming, if we have an expression such as z = x + y
, this means that the result of the evaluation of x + y
will be assigned to z
as expected. For example, if we have x = 42
and y = 13
, then z = 42 + 13
or z = 55
. The values of x
and y
can be changed later, for example, they can be changed to x=4
and y=4
, but this will not affect z
in any way, it will still be 55
.
The simplest example for this is a modern spreadsheet program, such as Microsoft Excel or Google docs spreadsheet. Spreadsheet cells commonly contain literal values such as number for example, or can contain formulas that have derived values from some other cells. In our cell C3
we could have the formula "=A1+B1"
meaning when we change some of the values in A1
or B1
, C3
will get autoupdated.
In MVC architecture, a simplification can be made using reactive programming, where we automatically propagate the changes from the view towards the model and back that can be very beneficial in real-time systems.
The use of reactive contexts saves us from writing a whole class of calls. In our example, we would first need to unsubscribe when something has changed and then again resubscribe to get the data back from the server. This reduces a substantial amount of code that could end up being error-prone and add more complexity in the maintenance phase.
Besides Meteor.autorun
, reactive context is applied in Templates
and in the Meteor.render
and Meteor.renderList
functions.
As for the data sources that can trigger changes, we can use database collections
and session
variables, and a few other functions related to authentication and authorization. You can find more details about it in the documentation of Meteor about reactivity at http://docs.meteor.com/#reactivity.
If you open up two different browsers side by side, you may notice that the same data is shown even if the sessions are different. In order to have user-specific data, we will create an example in the next recipe.
You might wish to send the entire collection to the client, but first think thoroughly if that is what the client actually needs. Often, it might be a lot wiser to send only certain fields rather than entire documents. In order to lower the network traffic, certain parts of the client can have subscription turned off and the documents for those parts will be removed from the local cache unless used in other active subscription.
Because the data we used is stored in the database, if we changed the data there using some external application, it will also trigger changes to the client. In the next recipe, we will see how we can allow multiple users to have their own favorite list for each of them instead of a single global list.
3.138.67.27