Querying the store

Before we look at synchronizing data with a remote source, we do have to catch up on how to use the queries that we briefly saw when we introduced find. Firstly, there are two types of queries: local and remote. I'll warn you now not to get hung up on the names; using both types of queries may invoke a fetch to a remote data source. The difference is that the content and order of local queries is updated live depending on the current records in the local store, while the content and order of remote queries is set by the remote data source and can only be updated by requesting from the remote data source again.

Why have this distinction? It's simple. If you have 5,000,000 records on your server, you don't want to load them all into your application in order to perform a search for just 10 of them. Therefore, when the results of the query depend on a set of data too large to be loaded entirely into the client store, we use remote queries. When the query is based on what is loaded in the store, we can use local queries. For example, we always load the data most relevant to what the user is doing in the client. If the user then wants to do a search, we could use a local query to search the few hundred records that we've loaded extremely quickly, in order to present some best effort results immediately to the user. If the user wants to do an expanded search, we would run a remote query to get additional search results from the server.

To create a query, we create an instance of SC.Query as shown in the following code snippet:

query = SC.Query.create({
  recordType: 'MyApp.Event'
});

The query type is determined by the value of the location property, which is SC.Query.LOCAL by default. So, the previous code snippet would create a local query for records of type MyApp.Event with no conditions. This will, in turn, return all the event records in the store, which to drive the point home, may not be all the event records in existence, since there may still be tens of thousands of more records on the server that have not yet been loaded in the application.

Let's look at creating a remote query first. A lot of remote queries are unique and don't change. So, we can create them once for use across the entire application. It's important to reuse rather than recreate identical queries because the store can then track which queries it has filled and not re-fetch a query that has already been filled, unless explicitly requested. If instead, we create a new query instance, even if it looks to be the same as one that has been filled already, the store will try to fill the new query with another round trip request to the server.

To create a remote query, we generally use the SC.Query.remote helper method. For example, these two approaches yield the same result:

// Example 1: using SC.Query.create
MyApp.peopleQuery = SC.Query.create({
  location: SC.Query.REMOTE,
  recordType: MyApp.Person,
  // Information to pass to the data source adaptor.
  includeAddress: true
});

// Example 2: using SC.Query.remote helper
MyApp.peopleQuery = SC.Query.remote(MyApp.Person, { includeAddress: true });

Using the previous code, we can now call MyApp.store.find(MyApp.peopleQuery) anywhere in the application to get a record array of people records filtered and ordered by the server. By the way, did you notice the two ways we attached the includeAddress property to the query? The data source adaptor handling this query will use this property to determine which kind of request it should make. We'll see this again later.

Let's look at local queries now. These are more interesting than remote queries and great for providing that extremely fast user experience we're after. We've already seen how to create a local query instance using create but of course, there is a helper method too: SC.Query.local.

For example:

MyApp.peopleQuery = SC.Query.local(MyApp.Person);

However, when we query for the records locally, we usually want to filter and order the results. First, to order the results, we can set the orderBy property. The simplest way to order results is by using an order string like it is commonly found in SQL languages. For example, to order by firstName and lastName, we use the following code snippet:

MyApp.peopleQuery = SC.Query.local(MyApp.Person, { 
  orderBy: 'firstName, lastName DESC'
});

The default order is ascending or ASC. So, the query in this example will order people by firstName in ascending order first and then by lastName in descending order. The orderBy property can also be set to a standard comparison function for more advanced comparisons.

Next, to filter the records according to specific criteria, we can set conditions on the query. SC.Query includes an incredible SQL-like query language called the SproutCore Query Language (SCQL).

SCQL allows us to make conditional queries such as shown in the following code snippet:

// All people that are minors.
MyApp.minorsQuery = SC.Query.local(MyApp.Person, {
  conditions: 'age < 21'
});
Or:
// All items for sale with names beginning with searchName.
MyApp.localSearchQuery = SC.Query.local(MyApp.Item, {
  conditions: '(name BEGINS_WITH "' + searchName + '") AND (isForSale = true)'
});

There are several other operators that you can construct conditions with, such as !=, ENDS_WITH, CONTAINS, MATCHES, and NOT, but I will leave it up to you to look these up in the documentation for SC.Query.

Also, as you must have probably noticed by now, inserting dynamic parameters into the conditions string like we did with searchName in the previous example is not very nice. An improvement would be to mark the parameters in the conditions string and pass them as variables, which is exactly what we can do.

For example, the previous example is better written in the following way:

var searchName = 'Ro';

// All items for sale with names beginning with 'searchLetter'.
MyApp.localSearchQuery = SC.Query.local(MyApp.Item, {
  conditions: '(name BEGINS_WITH "{name}") AND (isForSale = true)',
  parameters: { name: searchName }
});

As you can see, the name value will be looked up in the parameters object and inserted into the conditions string where we specified it. If you're creating complex local queries, you will no doubt find this to be a cleaner approach.

Finally, queries can also be re-fetched after they have been filled using the refreshQuery method on the store or simply by calling refresh on the query's associated record array.

For example:

// Run a query on the store (the first time will go to the server).
var results = MyApp.store.find(MyApp.localSearchQuery);

// Later… Refresh the query (will go to the server again).
results.refresh();
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.186.143