Domain Queries Predicate Expressions - Wolfgang-Schuetzelhofer/jcypher GitHub Wiki
Previous | Next | Table of Contents |
Domain Queries provide the power and expressiveness of queries on a graph database, while being formulated on domain objects (graphs of pojos - plain old java objects) or on types of domain objects respectively. Domain Mapping builds the basis, allowing to map complex business domains (in form of java object graphs) to graph databases. The fact, that the graph of domain objects is backed by a graph database, is key to domain queries. It allows to execute powerful and expressive queries, which perform complex navigations of the object graph, because the queries are actually performed against the graph database.
You start creating a query for a certain domain by creating a DomainQuery
object:
IDomainAccess da = // create an IDomainAccess
DomainQuery q = da.createQuery();
When you formulate a domain query, the goal is to retrieve one or more set(s) of domain objects which match certain criteria. For every such set you create a DomainObjectMatch
.
DomainObjectMatch<Person> persons = q.createMatch(Person.class);
A DomainObjectMatch specifies a type. The resulting set will contain domain objects of that type or of its subtypes respectively.
DomainObjectMatch(es)
can be created in multiple ways. The one shown above is the most basic one.
If you don't specify anything else, the DomainObjectMatch will result in a set containing all domain objects of the appropriate type(s) found in the domain graph.
Usually you will want to specify a couple of criteria in form of expressions, in order to select matching domain objects.
You can formulate different types of expressions.
They mostly express constraints on attributes of domain objects. Predicate expressions appear in form of WHERE clauses.
// 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
q.WHERE(smithsMatch.atttribute("lastName")).EQUALS("Smith");
Once you have specified the query, you need to execute it.
// execute the query
DomainQueryResult result = q.execute();
You obtain a DomainQueryResult
which you can ask for the resulting sets of domain objects.
// retrieve the list of matching domain objects
List<Person> smiths = result.resultOf(smithsMatch);
Consecutive WHERE clauses are 'AND-ed' by default.
DomainObjectMatch<Person> j_smithMatch = q.createMatch(Person.class);
// Constrain the set of Persons to contain
// John Smith only (lastName - Smith AND firstName - John)
q.WHERE(j_smithMatch.atttribute("lastName")).EQUALS("Smith");
q.WHERE(j_smithMatch.atttribute("firstName")).EQUALS("John");
If you want to 'OR' consecutive WHERE clauses you need to explicitly separate them by OR();
.
Additionally You may define blocks by using BR_OPEN (for bracket open) and BR_CLOSE (for bracket close). You can nest blocks arbitrarily deep.
DomainObjectMatch<Person> a_j_smithMatch = q.createMatch(Person.class);
// Constrain the set of Persons to contain
// Angelina and Jeremy Smith only
// The WHERE clauses related to 'firstName' are 'OR-ed'
q.WHERE(a_j_smithMatch.atttribute("lastName")).EQUALS("Smith");
q.BR_OPEN();
q.WHERE(a_j_smithMatch.atttribute("firstName")).EQUALS("Angelina");
q.OR();
q.WHERE(a_j_smithMatch.atttribute("firstName")).EQUALS("Jeremy");
q.BR_CLOSE();
You can specify predicates expressing dependencies between different domain objects.
DomainObjectMatch<Person> a_smithMatch = q.createMatch(Person.class);
DomainObjectMatch<Person> eyeColorMatch = q.createMatch(Person.class);
// Constrain the set of Persons to contain
// Angelina Smith only
q.WHERE(a_smithMatch.atttribute("lastName")).EQUALS("Smith");
q.WHERE(a_smithMatch.atttribute("firstName")).EQUALS("Angelina");
// Now constrain the set to be matched by 'eyeColorMatch'
// to contain persons with the same eye color as Angelina Smith
q.WHERE(eyeColorMatch.atttribute("eyeColor")).EQUALS(
a_smithMatch.atttribute("eyeColor"));
You can specify the intersection (and of course union) of two or more sets in a straight forward way.
DomainObjectMatch<Person> set_1Match = q.createMatch(Person.class);
DomainObjectMatch<Person> set_2Match = q.createMatch(Person.class);
DomainObjectMatch<Person> intersectionMatch = q.createMatch(Person.class);
// Constrain set_1 to
// a set with all smiths and christa berghammer
q.WHERE(set_1Match.atttribute("lastName")).EQUALS("Smith");
q.OR();
q.BR_OPEN();
q.WHERE(set_1Match.atttribute("lastName")).EQUALS("Berghammer");
q.WHERE(set_1Match.atttribute("firstName")).EQUALS("Christa");
q.BR_CLOSE();
// Constrain set_2 to
// a set with all berghammers
q.WHERE(set_2Match.atttribute("lastName")).EQUALS("Berghammer");
// The intersction of both set_1 and set_2.
// It will contain christa berghammer only.
q.WHERE(intersectionMatch).IN(set_1Match);
// q.OR() // using an OR here would produce the union
q.WHERE(intersectionMatch).IN(set_2Match);
result = q.execute();
List<Person> set_1 = result.resultOf(set_1Match);
List<Person> set_2 = result.resultOf(set_2Match);
List<Person> intersection = result.resultOf(intersectionMatch);
You can specify sorting for result sets. Additionally you can specify pagination in terms of offset and number of the returned domain objects with respect to their total count. If you don't specify pagination, all matching domain objects will be returned.
// create a DomainQuery object
q = domainAccess.createQuery();
// create DomainObjectMatches
DomainObjectMatch<Person> personsMatch = q.createMatch(Person.class);
// Specify Pagination (offset + count)
personsMatch.setPage(1, 5);
// Specify sorting for the result set.
// First: All persons are sorted by their last name (ascending)
// Second: Having the same last name, persons are sorted
// by their first name (descending)
q.ORDER(personsMatch).BY("lastName");
q.ORDER(personsMatch).BY("firstName").DESCENDING();
result = q.execute();
List<Person> sortedPersons = result.resultOf(personsMatch);
After once having executed the query, you can simply change pagination and retrieve the appropriate result set again.
// Change Pagination (offset + count)
personsMatch.setPage(7, 3);
// Retrieve the result set again.
sortedPersons = result.resultOf(personsMatch);
In order to effectively work with pagination, you will want to know the total number of matching objects for a DomainObjectMatch
.
// create a DomainQuery object
q = domainAccess.createQuery();
// create a DomainObjectMatch for objects of type Person
DomainObjectMatch<Person> smithMatch = q.createMatch(Person.class);
// define a predicate expression in form of a WHERE clause
// which constrains the set of Persons
q.WHERE(smithMatch.atttribute("lastName")).EQUALS("Smith");
// Retrieve the number of matching objects
CountQueryResult countResult = q.executeCount();
long numberOfSmiths = countResult.countOf(smithMatch);
Previous | Next | Table of Contents |