Class: SC.Query


Extends SC.Copyable, SC.Freezable, SC.Object.

This class permits you to perform queries on your data store or a remote data store. Here is a simple example of a local (see below) query,

query = SC.Query.create({
  conditions: "firstName = 'Johnny' AND lastName = 'Cash'"
});

This query, when used with the store, will return all records which have record attributes of firstName equal to 'Johnny' and lastName equal to 'Cash'. To use the query with the store simple pass it to find like so,

records = MyApp.store.find(query);

In this example, records will be an SC.RecordArray array containing all matching records. The amazing feature of record arrays backed by local queries is that they update automatically whenever the records in the store change. This means that once we've run the query once, any time data is loaded or unloaded from the store, the results of the query (i.e. records) will update automatically. This allows for truly powerful and dynamic uses, such as having a list of filtered results that continually updates instantly as data pages in or out in the background.

To limit the query to a record type of MyApp.MyModel, you can specify the type as a property of the query like this,

query = SC.Query.create({
  conditions: "firstName = 'Johnny' AND lastName = 'Cash'",
  recordType: MyApp.MyModel
});

Calling find() like above will now return only records of type MyApp.MyModel. It is recommended to limit your query to a record type, since the query will have to look for matching records in the whole store if no record type is given.

You can also give an order for local queries, which the resulting records will use,

query = SC.Query.create({
  conditions: "firstName = 'Johnny' AND lastName = 'Cash'",
  recordType: MyApp.MyModel,
  orderBy: "lastName, year DESC"
});

The default order direction is ascending. You can change it to descending by writing 'DESC' behind the property name as was done in the example above. If no order is given, or records are equal in respect to a given order, records will be ordered by their storeKeys which increment depending on load order.

Note, you can check if a certain record matches the query by calling query.contains(record).

Local vs. Remote Queries

The default type for queries is 'local', but there is another type we can use called 'remote'. The distinction between local and remote queries is a common tripping point for new SproutCore developers, but hopefully the following description helps keep it clear. The terms local and remote refer to the location of the data where the query is performed and by whom. A local query will be run by the client against the store of data within the client, while a remote query will be run on by the server against some remote store of data. This seems simple enough, but it can lead to a few misconceptions.

The first misconception is that local queries don't ever result in a call to a server. This is not the case; when a local query is first used with the store it will generally result in a call to the server, but whether or not it does depends on your store's data source. Keep this in mind, local queries only run against the data loaded in the client's store. If the client store is empty, the results of the query will be empty even though there may be thousands of matching records on a server somewhere. That's why when a query is first used, the store will look for a data source that implements the fetch(store, query) method. Your data source can then decide whether additional data should be loaded into the store first in order to better fulfill the query.

This is entirely up to your client/server implementation, but a common use case is to have a general query trigger the load of a large set of data and then any more specific queries will only run against what was already loaded. For more details, @see SC.DataSource.prototype.fetch. So to recap, local queries are passed to the data source fetch method the first time they are used so that the data source has a chance to load additional data that the query may need.

Once we get past the first misconception; local queries are actually pretty easy to understand and to work with. We run the queries, get the resulting record array and watch as the results almost magically update as the data in the client changes. Local queries are the default type, and typically we will use local queries almost exclusively in SproutCore apps. So why do we have a remote type?

In a previous paragraph, we considered how a local query would be empty if the local store was empty even though there may be thousands of matching records on a server. Well what if there were millions of records on the server? When dealing with extremely large datasets, it's not feasible to load all of the records into the client so that the client can run a query against them. This is the role of the 'remote' type. Remote queries are not actually "run" by the client at all, which is the next misconception; you cannot run a remote query.

This misconception is another way of saying that you can't set conditions or order on a remote query. The 'remote' SC.Query type is simply a reflection of some database query that was run against a data store somewhere outside of the client. For example, say we want to still find all the records on the server with firstName equal to 'Johnny' and lastName equal to 'Cash', but now there are millions of records. This type of query is best left to a MySQL or other database on a server and thus the server will have exposed an API endpoint that will return the results of such a search when passed some search terms.

Again, this is entirely up to your client/server configuration, but the way it is handled by the data source is nearly identical to how local queries will be handled. In both situations, the data source is passed the query the first time it is run and when the data source is done it calls the same method on the store, dataSourceDidFetchQuery. In both situations too, any data that the data source receives should be loaded into the client store using loadRecords. However, because there may be lots of other data in the client store, the 'remote' query must also be told which records pertain to it and in what order, which is done by passing the store keys of the new data also to dataSourceDidFetchQuery.

So to recap, use 'local' queries to filter the data currently in the client store and use 'remote' queries to represent results filtered by a remote server. Both may be used by a data source to load data from a server.

SproutCore Query Language

Features of the query language:

Primitives:

Parameters:

Wild cards are used to identify parameters by the order in which they appear in the query string. Named parameters can be used when tracking the order becomes difficult. Both types of parameters can be used by giving the parameters as a property to your query object:

yourQuery.parameters = yourParameters

where yourParameters should have one of the following formats: for wild cards: [firstParam,secondParam,thirdParam] for named params: {name1: param1, mane2: parma2}

You cannot use both types of parameters in a single query!

Operators:

Boolean Operators:

Parenthesis for grouping:

Adding Your Own Query Handlers

You can extend the query language with your own operators by calling:

SC.Query.registerQueryExtension('your_operator', your_operator_definition);

See details below. As well you can provide your own comparison functions to control ordering of specific record properties like this:

SC.Query.registerComparison(property_name, comparison_for_this_property);

Defined in: query.js

Since:
SproutCore 1.0

Field Summary

Class Methods

Instance Methods

Field Detail

conditions String

Unparsed query conditions. If you are handling a query yourself, then you will find the base query string here.

isEditable

Indicates whether a record is editable or not. Defaults to NO. Local queries should never be made editable. Remote queries may be editable or not depending on the data source.

isQuery Boolean
Walk like a duck.
SC.Query.LOCAL String
Constant used for `SC.Query#location`
location String

Indicates the location where the result set for this query is stored. Currently the available options are:

  • SC.Query.LOCAL -- indicates that the query results will be automatically computed from the in-memory store.
  • SC.Query.REMOTE -- indicates that the query results are kept on a remote server and hence must be loaded from the DataSource.

The default setting for this property is SC.Query.LOCAL.

Note that even if a query location is LOCAL, your DataSource will still have its fetch() method called for the query. For LOCAL queries, you won't need to explicitly provide the query result set; you can just load records into the in-memory store as needed and let the query recompute automatically.

If your query location is REMOTE, then your DataSource will need to provide the actual set of query results manually. Usually you will only need to use a REMOTE query if you are retrieving a large data set and you don't want to pay the cost of computing the result set client side.

orderBy String | Function

Optional orderBy parameters. This can be a string of keys, optionally ending with the strings " DESC" or " ASC" to select descending or ascending order.

Alternatively, you can specify a comparison function, in which case the two records will be sent to it. Your comparison function, as with any other, is expected to return -1, 0, or 1.

parameters Hash

Optional hash of parameters. These parameters may be interpolated into the query conditions. If you are handling the query manually, these parameters will not be used.

queryLanguage

This is the definition of the query language. You can extend it by using SC.Query.registerQueryExtension().

recordType SC.Record

The base record type or types for the query. This must be specified to filter the kinds of records this query will work on. You may either set this to a single record type or to an array or set of record types.

recordTypes SC.Enumerable

Optional array of multiple record types. If the query accepts multiple record types, this is how you can check for it.

SC.Query.REMOTE String
Constant used for `SC.Query#location`
scope SC.Query

Another query that will optionally limit the search of records. This is usually configured for you when you do find() from another record array.

Class Method Detail

build(location, recordType, conditions, parameters)

Returns a SC.Query instance reflecting the passed properties. Where possible this method will return cached query instances so that multiple calls to this method will return the same instance. This is not possible however, when you pass custom parameters or set ordering. All returned queries are frozen.

Usually you will not call this method directly. Instead use the more convenient SC.Query.local() and SC.Query.remote().

Examples

There are a number of different ways you can call this method.

The following return local queries selecting all records of a particular type or types, including any subclasses:

var people = SC.Query.local(Ab.Person);
var peopleAndCompanies = SC.Query.local([Ab.Person, Ab.Company]);

var people = SC.Query.local('Ab.Person');
var peopleAndCompanies = SC.Query.local('Ab.Person Ab.Company'.w());

var allRecords = SC.Query.local(SC.Record);

The following will match a particular type of condition:

var married = SC.Query.local(Ab.Person, "isMarried=YES");
var married = SC.Query.local(Ab.Person, "isMarried=%@", [YES]);
var married = SC.Query.local(Ab.Person, "isMarried={married}", {
  married: YES
});

You can also pass a hash of options as the second parameter. This is how you specify an order, for example:

var orderedPeople = SC.Query.local(Ab.Person, { orderBy: "firstName" });
Parameters:
location String
the query location.
recordType SC.Record|Array
the record type or types.
conditions String Optional
The conditions string.
parameters Object Optional
The parameters object.
Returns:
SC.Query
compareStoreKeys(storeKey1, storeKey2, storeKey1, storeKey2)

Default sort method that is used when calling containsStoreKeys() or containsRecords() on this query. Simply materializes two records based on storekeys before passing on to compare().

Parameters:
storeKey1 Number
a store key
storeKey2 Number
a store key
storeKey1
storeKey2
Returns:
Number
-1 if record1 < record2, +1 if record1 > record2, 0 if equal
containsRecords(query, records, store)

Will find which records match a give SC.Query and return an array of store keys. This will also apply the sorting for the query.

Parameters:
query SC.Query
to apply
records SC.RecordArray
to search within
store SC.Store
to materialize record from
Returns:
Array
array instance of store keys matching the SC.Query (sorted)
local(recordType, properties, oldParameters)

Returns a LOCAL query with the passed properties.

For example,

// Show all the accounts with a value greater than 100.
query = SC.Query.local(MyApp.Account, {
  conditions: 'value > {amt}',
  parameters: { amt: 100 },
  orderBy: 'value DESC'
});
Parameters:
recordType SC.Record|Array
the record type or types.
properties Object Optional
Additional properties to be added to the query.
oldParameters
Returns:
SC.Query
orderStoreKeys(storeKeys, query, store)

Sorts a set of store keys according to the orderBy property of the SC.Query.

Parameters:
storeKeys Array
to sort
query SC.Query
to use for sorting
store SC.Store
to materialize records from
Returns:
Array
sorted store keys. may be same instance as passed value
registerComparison(name, custom)

Call to register a comparison for a specific property name. The function you pass should accept two values of this property and return -1 if the first is smaller than the second, 0 if they are equal and 1 if the first is greater than the second.

Parameters:
name String
of the record property
custom Function
comparison function
Returns:
SC.Query
receiver
registerQueryExtension(tokenName, token)

Call to register an extension for the query language. You should provide a name for your extension and a definition specifying how it should be parsed and evaluated.

Have a look at queryLanguage for examples of definitions.

TODO add better documentation here

Parameters:
tokenName String
name of the operator
token Object
extension definition
Returns:
SC.Query
receiver
remote(recordType, properties, oldParameters)

Returns a REMOTE query with the passed properties.

For example,

// The data source can alter its remote request using the value of
// `query.beginsWith`.
query = SC.Query.remote(MyApp.Person, { beginsWith: 'T' });
Parameters:
recordType SC.Record|Array
the record type or types.
properties Object Optional
Additional properties to be added to the query.
oldParameters
Returns:
SC.Query
storeKeyFor(query)

Given a query, returns the associated storeKey. For the inverse of this method see SC.Store.queryFor().

Parameters:
query SC.Query
the query
Returns:
Number
a storeKey.

Instance Method Detail

buildOrder(orderOp)

Takes a string containing an order statement and returns an array describing this order for easier processing. Called by parse().

Parameters:
orderOp String | Function
the string containing the order statement, or a comparison function
Returns:
Array | Function
array of order statement, or a function if a function was specified
buildTokenTree(tokenList, treeLogic)

Takes an array of tokens and returns a tree, depending on the specified tree logic. The returned object will have an error property if building of the tree failed. Check it to get some information about what happend. If everything worked, the tree can be evaluated by calling

tree.evaluate(record, parameters)

If tokenList is empty, a single token will be returned which will evaluate to true for all records.

Parameters:
tokenList Array
the list of tokens
treeLogic Object
the logic definition (normally queryLanguage)
Returns:
Object
token tree
compare(record1, record2)

Returns the sort order of the two passed records, taking into account the orderBy property set on this query. This method does not verify that the two records actually belong in the query set or not; this is checked using contains().

Parameters:
record1 SC.Record
the first record
record2 SC.Record
the second record
Returns:
Number

-1 if record1 < record2, +1 if record1 > record2, 0 if equal

contains(record, parameters)

Returns YES if record is matched by the query, NO otherwise. This is used when computing a query locally.

Parameters:
record SC.Record
the record to check
parameters Hash
optional override parameters
Returns:
Boolean
YES if record belongs, NO otherwise
containsRecordTypes(types)

Returns YES if the query matches one or more of the record types in the passed set.

Parameters:
types SC.Set
set of record types
Returns:
Boolean
YES if record types match
expandedRecordTypes()

Returns the complete set of recordTypes matched by this query. Includes any named recordTypes plus their subclasses.

isLocal()

Returns YES if query location is Local. This is sometimes more convenient than checking the location.

isRemote()

Returns YES if query location is Remote. This is sometimes more convenient than checking the location.

parse()

This method has to be called before the query object can be used. You will normally not have to do this; it will be called automatically if you try to evaluate a query. You can, however, use this function for testing your queries.

Returns:
Boolean
true if parsing succeeded, false otherwise
queryWithScope(recordArray)

Returns the same query but with the scope set to the passed record array. This will copy the receiver. It also stores these queries in a cache to reuse them if possible.

Parameters:
recordArray SC.RecordArray
the scope
Returns:
SC.Query
new query
tokenizeString(inputString, grammar)

Takes a string and tokenizes it based on the grammar definition provided. Called by parse().

Parameters:
inputString String
the string to tokenize
grammar Object
the grammar definition (normally queryLanguage)
Returns:
Array
list of tokens
toString()
Documentation generated by JsDoc Toolkit 2.4.0 on Wed Apr 08 2015 10:02:21 GMT-0600 (CST)