Reading Data
Getting Started
We can retrieve data that has been stored in Flybase by attaching an asynchronous listener to a Flybase reference. In this document, we will cover the basics of retrieving data, how Flybase data is ordered, and how to perform queries on Flybase data.
Let’s revisit our example from the previous article to understand how we read data from Flybase.
To simply read our post data, we can do the following:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// Attach an asynchronous callback to read the data at our posts referenceref.on("value", function(snapshot) { console.log( snapshot.value() );});
If we run this code, we’ll see an object containing all of our posts logged to the console.
The callback function will receive a snapshot
, which is a snapshot of the data. A snapshot is a listing of the data in a Flybase app at a single point in time, this could be one document or multiple documents, depending on the query being called, and the number of records that are returned.
Calling .value()
on a snapshot returns the JavaScript object representation of the data. If no data exists at the location, the snapshots value will be null
.
You may have noticed that we used the value
event type in our example above, which reads the entire contents of a Flybase collection. value
is one of the five different reserved event types that we can use to read data from Flybase. We’ll discuss the other four event types below.
When reading data, we have access to a few methods besides .value()
, we can use .count()
to get a count of the number of records returned and we can use .forEach()
to iterate through each record:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// Attach an asynchronous callback to read the data at our posts referenceref.on("value", function(snapshot) { console.log( "we found " + snapshot.count() + " posts"); if( snapshot.count() ){ snapshot.forEach( function( post ){ console.log( post.value() ); }); }});
The .count()
method will return null
if no documents were found, this can be handy for making sure data actually exists so we don’t trigger any errors.Reserved Events
Reserved Events are events that are triggered when certain events happen, such as retrieving documents, adding a new document, changing a document, deleting documents or even when a device connects or disconnects.
These events will always happen, so it’s up to you to decide how you want to listen to them and handle them.Value
Whenever you query data, the value
event is used to read the contents of a given collection.
When the value
event is triggered, the event callback is passed a snapshot containing all document data.
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// Attach an asynchronous callback to read the data at our posts referenceref.on("value", function(snapshot) { console.log( "we found " + snapshot.count() + " posts"); snapshot.forEach( function( post ){ console.log( post.value() ); });});
Document Added
The added
event is typically used when retrieving a list of items in Flybase. Unlike value
which returns the entire contents of the location, added
is triggered once every time a new document is added to the specified collection. The event callback is passed a snapshot containing the new document’s data.
If we wanted to retrieve only the data on each new post added to our blogging app, we could use added
:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// Retrieve new posts as they are added to Flybaseref.on("added", function(snapshot) { const post = snapshot.value(); console.log("Author: " + post.author); console.log("Title: " + post.title);});
In this example the snapshot contains an object with an individual blog post. Because we converted our post to an object using .value()
, we have access to the post’s author and title properties by calling by calling .author
and .title
respectively.Document Updated
The changed
event is triggered any time a document is modified. Normally, it is used along with added
and removed
to respond to changes to a list of items. The snapshot passed to the event callback contains the updated data for the document.
We can use changed
to read updated data on blog posts when they are edited:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// Get the data on a post that has changedref.on("changed", function(snapshot) { const post = snapshot.value(); console.log("The updated post title is " + post.title);});
Document Removed
The removed
event is triggered when a document is removed. Normally, it is used along with added
and changed
. The snapshot passed to the event callback contains the data for the removed document.
In our blog example, we’ll use removed
to log a notification about the deleted post to the console:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// Get the data on a post that has been removedref.on("removed", function(snapshot) { const post = snapshot.value(); console.log("The blog post titled '" + post.title + "' has been deleted");});
Device Connected / Disconnected
The online
event is triggered whenever another device connects or disconnects from a Flybase app.
In our example, we’ll use online
to log a notification about the number of connected devices to the console:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");messagesRef.on('online', function (data) { const online = data.value(); console.log( "There are " + online + " devices connected");});
Custom Events
You can also listen for custom events, these are events that don’t read or write to your Flybase app, but can be set up to trigger another action.
For example, you can use this to notify a user of an event:
const ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.on("custom_event", function(message) { console.log( message );});ref.trigger("custom_event", "Hi")
In the above example, we set up a listener for a custom event called custom_event
and then triggered it using the .trigger()
function, whenever the custom_event
event is triggered, we will display the message in the console. This could be used to listen to any type of notification.
Handling How Data Is Returned
Flybase gives you a query engine that lets you selectively retrieve data based on various factors.
To construct a query in Flybase, you start by specifying how you want your data to be ordered using the orderBy
ordering function. You can then combine these with other methods to conduct complex queries: limit()
, skip()
, and where()
.Ordering Data
We can order data by a common key by passing that key to orderBy()
, followed by 1
for Ascending or -1
for Descending sort order. For example, to sort all posts by author, we can do the following:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.orderBy({"author": 1}).on("value", function(snapshot) { console.log( snapshot.value() );});
You may notice that our orderBy()
function takes an object. We’ve built our query engine heavily based on the MongoDB standard, so you can order by specifying the field, and then 1
for Ascending or -1
for Descending sort order. You can also add other fields as well:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.orderBy({"author": 1,"date": -1}).on("value", function(snapshot) { console.log( snapshot.value() );});
Now that we’ve specified how your data will be ordered, we can use the limit
or skip
methods described below to construct more complex queries. On top of that, we can actually perform queries using our query builder.Limiting Data
The limit()
function is used to set a maximum number of documents to be synced for a given callback. If we set a limit of 100, we will initially only receive up to 100 documents.Skipping Data
Using skip()
allows us to choose arbitrary starting and ending points for our queries. This is useful for pagination when combined with limit()
.Querying Data
The most powerful function you can use for querying data is the where()
function, this lets you build queries similar to a database. For example, if we only wanted to return posts that were written by baileys
, we can do the following query:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.where({"author": "baileys"}).on("value", function(snapshot) { console.log( snapshot.value() );});
This will return all posts that were written by baileys
.
We can combine all our functions, for example, we can return posts written by baileys
and sort by date:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.where({"author": "baileys"}).orderBy({"date": 1}).on("value", function(snapshot) { console.log( snapshot.value() );});
Just as with the orderBy()
functionality, we’ve built our queries based on MongoDB, so there are a few commands you can use to perform queries, which can help build pretty advanced apps:
status is "A":
{ "status": "A" } or { "status": { "$eq": "A" } }
status is NOT "A":
{ "status": { "$not": "A" } }
age > 25:
{ "age": { "$gt": 25 } }
status is "A" AND age > 25:
{ "$and": [ {"status": "A" }, {"age": { "$gt": 25 } } ] }
name contains "bc":
{ "name": { "$regex":"bc" } }
name is either ‘bob’ or ‘jake’:
{ "name" { "$in": ["bob", "jake"] } }
array contains "bob":
{ "name" { "$has": "bob" } }
array does not contain "bob":
{ "name" { "$nhas": "bob" } }
When calling fields, such as name
or age
, or when calling arguments such as $eq
, $not
, $gt
, $gte
, $lt
, $lte
, $regex
, $has
, $nhas
or $in
, always wrap them in quotes so that you would have "name"
, this is proper JSON, and is required for your queries to work. It also makes them easier to read!
As you can see, there is a lot you can do with the query system, and this helps greatly extend what you can do with your Flybase app
Getting Data
You can also use the Get
function to retrieve data, data handling is handled the same way as using the value
event, but you can include instructions inside the call:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.get(function(snapshot) { console.log( snapshot.value() );},{l:1});
This will tell the app to return one record only, you can also include other instructions and queries inside it, we’ll cover the queries below. If you leave the options area blank, then you will return all records:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");ref.get(function(snapshot) { snapshot.forEach( function( post ){ console.log( post.value() ); });});
Optional parameters
{'q':{query}}
– restrict results by the specified JSON query{'c':true}
– return the result count for this query{'fo':true}
– return a single document from the result set{'s':[sort order]}
– specify the order in which to sort each specified field (1- ascending; -1 – descending){'sk':[num results to skip]}
– specify the number of results to skip in the result set; useful for paging{'l':limit}
– specify the limit for the number of results (default is 1000)
Examples using these parameters:
// Get a reference to our postsconst ref = new Flybase("74k8064f-cd6f-4c07-8baf-b1d241496eec", "web", "posts");// "q" example - return all documents with "active" field of true:const params = { q: { active: true } };ref.get(function(snapshot) { snapshot.forEach( function( post ){ console.log( post.value() ); });},params);// "fo" example - return a single document matching "active" field of true:const params = { fo: true };ref.get(function(snapshot) { console.log( snapshot.value() );},params);// "s" example - return all documents sorted by "priority" ascending and "difficulty" descending:const params = { s: {"priority": 1, "difficulty": -1} };ref.get(function(snapshot) { snapshot.forEach( function( post ){ console.log( post.value() ); });},params);// "sk" and "l" example - sort by "name" ascending and return 10 documents after skipping 20const params = { s: {"name": 1},sk: 20,l: 10 };ref.get(function(snapshot) { snapshot.forEach( function( post ){ console.log( post.value ); });},params);
Joining Data
You can also use a lookup
to query multiple collections at once.
For example, let’s say we were building a chat system, and that chat system was set up so that chat messages were stored inside the messages collection and user info was stored inside the users collection, we’d want to display who posted what right?
const get_user_info = ( userId, cb ) => { messagesRef.lookup( userId, [ 'chat.userId', 'users._id' ], function (jdata ){ cb( jdata ); });}messagesRef.on('value').then( function(data){ data.forEach( function( row ){ var row = row.value(); get_user_info( row._id, function( data ){ console.log( data.name + ": + data.message; }); });}
This would perform an initial query on the chat page
When using the lookup
function, we pass the value to join data on, an array of collections with the field to join by and a callback or promise to handle the data.
The lookup
function will return an object of data rather than the usual Flybase object, this is so you can display data faster and also because it’s returned as a single row and is meant to be read-only.
lookup
was set up to make it handy for assembling data to display across multiple collections quickly and easily.
Now that we’ve covered retrieving data from Flybase, we’re ready for the next article on deleting data in Flybase.