Domain Queries Collection Expressions 1 - Wolfgang-Schuetzelhofer/jcypher GitHub Wiki

Previous Next Table of Contents

Domain Queries

Collection Expressions

Collection expressions provide advanced operations on sets of domain objects.
The operations explained in this section are: SELECT, REJECT, and COLLECT. Each of these operations starts with a source set (i.e. a DomainObjectMatch which represents a set of domain objects). Each of these operations produces a result set (another DomainObjectMatch representing a set of domain obects). The result set is produced out of the source set.

SELECT

A select expression produces a result set by selecting elements from its source set. The resulting DomainObjectMatch will always be of the same type as the source DomainObjectMatch.

DomainObjectMatch<Person> allPersons = q.createMatch(Person.class);

// Select from all persons
DomainObjectMatch<Person> selectedPersons =
   q.SELECT_FROM(allPersons).ELEMENTS(
      // predicate expressions specify the selection
      q.WHERE(allPersons.atttribute("lastName")).EQUALS("Smith"),
      q.WHERE(allPersons.atttribute("firstName")).EQUALS("John")
   );
		
// execute the query
DomainQueryResult result = q.execute();
// retrieve the list of matching domain objects
List<Person> selectedPersonsList = result.resultOf(selectedPersons);

Predicate expressions within the ELEMENTS part of the SELECT clause specify the selection. Like with any predicate expressions, consecutive ones are 'AND-ed' by default. To 'OR' them, you have to explicitly separate them by an OR() statement. BR_OPEN and BR_CLOSE can be used to nest blocks of predicate expressions.
Important: Predicate expressions within a SELECT clause must be specified either on the DomainObjectMatch which specifies the source set (see code snippet above), or on a DomainObjectMatch which was directly derived from that one by means of a traversal expression. Of course any combination of those expressions is possible as well.

The next snippet demonstrates how to specify selection by expressing a constraint on a derived set.
Imagine you want to select all persons who live on 'Market Street'.

// create a DomainQuery object
DomainQuery q = domainAccess.createQuery();
// Create a DomainObjectMatch for objects of type Person.
// By default it will match all persons
DomainObjectMatch<Person> personMatch = q.createMatch(Person.class);
		
// select all person's addresses by means
// of a traversal expression
DomainObjectMatch<Address> addressMatch =
   q.TRAVERSE_FROM(personMatch).FORTH("addresses").TO(Address.class);

// Select from all persons
// those that live on market street
DomainObjectMatch<Person> onMarketStreetMatch =
   q.SELECT_FROM(personMatch).ELEMENTS(
      q.WHERE(addressMatch.atttribute("street")).EQUALS("Market Street")
   );
		
// execute the query
DomainQueryResult result = q.execute();
// retrieve the list of matching domain objects
List<Person> onMarketStreet = result.resultOf(onMarketStreetMatch);

The traversal expression can be arbitrarily complex.

CONTAINS and COUNT are two operations of predicate expressions which are specifically designed for usage in collection expressions. (In fact their usage is only valid within collection expressions). They serve to specify constraints on derived sets (derived by traversal expression).

Using CONTAINS:
Imagine you want to select all persons with addresses in europe.
For the following snippet: An object of type Address references an object of Type Area via attribute 'area'. An object of type Area references another object of type Area (the area it is located in; eg. a city is located in a country) via attribute partOf. A hierarchy of areas exists in the domain object graph. E.g. area Vienna is 'part of' area Austria is 'part of' Area Europe.

// create a DomainQuery object
DomainQuery q = domainAccess.createQuery();
// Create a DomainObjectMatch for objects of type Person.
// By default it will match all persons
DomainObjectMatch<Person> personMatch = q.createMatch(Person.class);
// Create a DomainObjectMatch for objects of type Area.
DomainObjectMatch<Area> europe = q.createMatch(Area.class);
		
// constrain the set of areas
// to contain europe only
q.WHERE(europe.atttribute("name")).EQUALS("Europe");
		
// select all areas which are reachable
// from all person's addresses by means
// of a traversal expression
DomainObjectMatch<Area> areasMatch =
   q.TRAVERSE_FROM(personMatch).FORTH("addresses")
   .FORTH("area")
   .FORTH("partOf").DISTANCE(1, -1)
   .TO(Area.class);

// Select from all persons
// those with addresses in europe
DomainObjectMatch<Person> inEuropeMatch =
   q.SELECT_FROM(personMatch).ELEMENTS(
      q.WHERE(areasMatch).CONTAINS(europe)
   );
		
// execute the query
DomainQueryResult result = q.execute();
// retrieve the list of matching domain objects
List<Person> inEurope = result.resultOf(inEuropeMatch);
// you can retrieve the intermediate results as well
List<Area> europeResult = result.resultOf(europe);
List<Area> areas = result.resultOf(areasMatch);

Using COUNT:
Think of selecting all persons who have exactly two addresses.

// create a DomainQuery object
DomainQuery q = domainAccess.createQuery();
// Create a DomainObjectMatch for objects of type Person.
// By default it will match all persons
DomainObjectMatch<Person> personMatch = q.createMatch(Person.class);
		
// select all person's addresses by means
// of a traversal expression
DomainObjectMatch<Address> addressMatch =
   q.TRAVERSE_FROM(personMatch).FORTH("addresses").TO(Address.class);

// Select from all persons those,
// who have exactly two addresses
DomainObjectMatch<Person> twoAddressesMatch =
   q.SELECT_FROM(personMatch).ELEMENTS(
      q.WHERE(addressMatch.COUNT()).EQUALS(2)
   );
		
// execute the query
DomainQueryResult result = q.execute();
// retrieve the list of matching domain objects
List<Person> twoAddresses = result.resultOf(twoAddressesMatch);

REJECT

A reject expression produces a result set by removing elements from its source set. The resulting DomainObjectMatch will always be of the same type as the source DomainObjectMatch. A reject expression is constructed in the same way as a select expression is. But in contrast, a reject expression produces the complementary set of the equivalent select expression.
If you want to retrieve all persons except those who live on Market Street, the following snippet provides an example. (Although in a real world example, the set of returned persons will probably be too large).

// create a DomainQuery object
DomainQuery q = domainAccess.createQuery();
// Create a DomainObjectMatch for objects of type Person.
// By default it will match all persons
DomainObjectMatch<Person> personMatch = q.createMatch(Person.class);
		
// select all person's addresses by means
// of a traversal expression
DomainObjectMatch<Address> addressMatch =
   q.TRAVERSE_FROM(personMatch).FORTH("addresses").TO(Address.class);

// Reject from all persons
// those that live on market street
DomainObjectMatch<Person> notOnMarketStreetMatch =
   q.REJECT_FROM(personMatch).ELEMENTS(
      q.WHERE(addressMatch.atttribute("street")).EQUALS("Market Street")
   );
		
// execute the query
DomainQueryResult result = q.execute();
// retrieve the list of matching domain objects
List<Person> notOnMarketStreet = result.resultOf(notOnMarketStreetMatch);

COLLECT

A collect expression allows to collect attributes from a set of domain objects. (The attributes must be of simple type: String, Number, Boolean, Date). The result of the expression is a set, meaning all elements of the set are unique.
The following snippet demonstrates how to collect all first names of persons with last name Smith.

// create a DomainQuery object
DomainQuery q = domainAccess.createQuery();
// create a DomainObjectMatch for objects of type Person.
DomainObjectMatch<Person> smithsMatch = q.createMatch(Person.class);
// define a predicate expression in form of a WHERE clause
// which constrains the set of Persons to contain all Smiths
q.WHERE(smithsMatch.atttribute("lastName")).EQUALS("Smith");
		
// collect the first names of all Smiths
DomainObjectMatch<String> firstNamesMatch =
   q.COLLECT(smithsMatch.atttribute("firstName")).AS(String.class);
		
// if you want to do sorting,
// you have to sort the set from which you collect the attributes
q.ORDER(smithsMatch).BY("firstName");
		
// execute the query
DomainQueryResult result = q.execute();
// retrieve the list of matching domain objects
List<String> firstNames = result.resultOf(firstNamesMatch);

Previous Next Table of Contents
⚠️ **GitHub.com Fallback** ⚠️