Queries, RecordLists and RecordStreams

This page describes how you can access records in your database using the CrossmarX Scripting API.

Searching for records

All records of a particular class are typically stored in a database table in the database of your application. To search for records in those tables from your scripts, i.e. to run a query on your database, you have to make use of an object of type Query. Queries act on a specifiec class of the blueprint of your application, and define a filter that specifies which of the records in your database are to be retrieved. To obtain a Query object to define and execute your query, you have to construct one. How this is done precisely is dependent on the scripting language you are working in, but in JavaScript this is done through the newkeyword, followed by the name of the constructor (which is typically equal to the name of the type of the object), and the class as argument. For example, to get a Query object to search for records of class "person":

let myQuery = new Query(Blueprint.Classes.person);

Once you have constructed a Query object, you can now add filter conditions to specify which records you want to retrieve. Just like the filter you specify on the search manager of the class in the user interface of your application, these filters consist of leaves, which specify a certain value for a field and one of a number of field operators, and branches which combine leaves and other branches using boolean operators. These leaves and branches are added to Query (and Filter) objects using various methods which are documented in detail here and here, but in this case we will only use the method Query.getFilter() to access the filter associated with the query, and Filter.or() to create a new OR-branch, and Filter.add(Field, Object) to add a leave to (exactly) match a field value. For example. the following code snippet shows how to define a filter for myQuery to search for persons with last name "Smith" or "Johnson":

let myFilter = myQuery.getFilter(),
    orBranch = myFilter.or();

orBranch.add(Classes.person.Last_name, "Smith");
orBranch.add(Classes.person.Last_name, "Johnson");

RecordList

Now we have constructed a Query object and defined a filter, we are ready to retrieve the matching records from the database. For this, use the method Query.find(), which returns an object of type RecordList. This type defines a number of methods to get individual records from the list, but can also be directly used in a for loop to iterate over the contained records:

let myResult = myQuery.find();
for (let myRecord of myResult) {
   // do usefull stuff with myRecord
}

Note that every record retrieved from the database is now - within the loop - available as an object (myRecord) of type Record. Of course RecordStream can do a lot more than merely iterate over its contained records, for instance add and delete records, return a subset, etc. Please see the API documentation for an overview.

RecordStream

In the example above we did nothing more than iterate over the records returned by a Query, and did not require any other functionality from RecordList. Especially when dealing with a large database tables, RecordList is not the most efficient option to iterate over records, since it preloads all records in memory, thereby consuming valuable resources on your server. When no further functionality is required, using a RecordStream instead is much preferred, because it does not preload records in memory but streams those records directly from the database to, for instance, a for loop. To obtain a RecordStream object simply use the method Query.stream() instead of Query.find():

for (let myRecord of myQuery.stream()) {
   // still do exactly the same stuff with myRecord, but using fewer resources
}

While using RecordStream instead of RecordList can potentially save a lot of resources, it comes at a cost. That is: not all functionality available through RecordList is available to RecordStream. It also may not be the most efficient option in case your query would only return a small number of records. As a rule of thumb, you should use Query.stream() / RecordStream whenever you expect to work wih a lot of records, and when there is no need to use the advanced functionality provided by RecordList.

Other uses of RecordList and RecordStream

Constructing and running a query is not the only way records can be retrieved from your database. If you define an action for the search manager of a class in your blueprint, and you happen to define this action using a script, this script will have access to the applicable records - either a selection by the user or the full search result, by means of either a RecordList or a RecordStream, depending on the situation. Jobs specified to act on a class also make a RecordStream available to implementing scripts.

A third case is retrieving the connected records for a record. If you have a record of class "person" and you want to retrieve, for example, the connected records "orders" for that person:

for (let myOrder of myPerson.getConnectedRecords(Classes.order)) {
   // do something with every order record
}

The method Record.getConnectedRecords(Class) will return a RecordList, in this case containing all records of class "order" connected to record myPerson.