6 Simplifying Cache Calls and Aggregations

This exercise illustrates how you can simplify Java expressions that use aggregations and filters by using the Coherence createFilter and createExtractor factory methods in the QueryHelper class.

In Chapter 5, "Loading Data Into a Cache," you created a file, QueryExample.java, that used a variety of filters, such as AlwaysFilter, LikeFilter, and EqualsFilter, to pull and aggregate information from the cache. The file also created indexes on the data by using a variety of specialized ValueExtractors: ChainedExtractors, KeyExtractors, and ReflectionExtractors. This created some verbose Java statements. These statements can be simplified by using the QueryHelper API.

This chapter contains the following sections:

6.1 Introduction

To simplify filter and extractor statements, and the way in which you interact with the Coherence caches, Coherence provides the QueryHelper API. QueryHelper (com.tangosol.util.QueryHelper) is a utility class that provides a set of createFilter and createExtractor factory methods that can build instances of Filter and ValueExtractor. The methods in the class accept a String data type that specifies the creation of rich Filters in a format that is familiar to anyone who understands SQL WHERE clauses.

For example, the following statement uses createFilter(String s) to construct a filter for employees who live in Massachusetts but work in another state.

..
QueryHelper.createFilter("homeAddress.state = 'MA' and workAddress.state !='MA'")
...

This statement is more simple and easier to read than the equivalent filter and extractor statement using the Coherence API:

new AndFilter(new EqualsFilter("getHomeAddress.getState", "MA"), 
      new NotEqualsFilter("getWorkAddress.getState", "MA")) 

For more information, see the Javadoc for the QueryHelper API. For information on the syntax of the WHERE clause within the Coherence Query Language, see "Using Coherence Query Language" in Oracle Fusion Middleware Developing Applications with Oracle Coherence.

6.2 Simplifying the Query Example

This section describes how you can simplify the indexes, cache calls, and aggregations in the QueryExample.java file that you created in the previous chapter.

  1. Import the QueryHelper API into the QueryExample.java file as a static class.

    import static com.tangosol.util.QueryHelper.*;
    
  2. Comment out the imports for the ChainedExtractor, KeyExtractor, and ReflectionExtractor classes.

  3. Comment out the imports for the AlwaysFilter, AndFilter, EqualsFilter, GreaterFilter, LikeFilter, and NotEqualsFilter classes.

  4. In the cache.addIndex statements, replace instances of ReflectionExtractor with createExtractor from the QueryHelper API.

    Table 6-1 lists the ReflectionExtractor instances and their createExtractor equivalents.

    Table 6-1 ReflectionExtractors and Their Equivalent createExtractor Statements

    Replace This ReflectionExtractor Statement ... With the Equivalent createExtractor Statement ...

    cache.addIndex(new ReflectionExtractor("getAge"), true, null);

    cache.addIndex(createExtractor("age"), true, null);

    cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getState")), true, null);

    cache.addIndex(createExtractor("homeAddress.state"), false, null);

    cache.addIndex(new ChainedExtractor(new ReflectionExtractor("getWorkAddress"), new ReflectionExtractor("getState")), true, null);

    cache.addIndex(createExtractor("workAddress.state"),false, null);

    cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getCity")), true, null);

    cache.addIndex(createExtractor("homeAddress.city"), true, null);


  5. Replace the calls to the *Filter methods in the setResults statements with calls to createFilter with the appropriate Coherence Query Language.

    Table 6-2 lists the *Filter instances and their createFilter equivalents.

    Table 6-2 *Filter Statements and Their Equivalent createFilter Statements in Queries

    Replace This *Filter Statement ... With the Equivalent createFilter Statement ...

    Set setResults = cache.entrySet(new EqualsFilter("getHomeAddress.getState", "MA"));

    Set setResults = cache.entrySet(createFilter("homeAddress.state = 'MA'"));

    Set setResults = cache.entrySet(new AndFilter(new EqualsFilter("getHomeAddress.getState", "MA"), new NotEqualsFilter("getWorkAddress.getState", "MA")));

    setResults = cache.entrySet( createFilter("homeAddress.state is 'MA' and workAddress is not 'MA'"));

    Set setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%"));

    Set setResults = cache.entrySet(createFilter("homeAddress.city like 'S%'"));

    Set setResults = cache.entrySet(new GreaterFilter("getAge", nAge));

    // Initialize nAge and aEnv

    final int nAge = 42;

    Object[] aEnv = new Object[] {new Integer(nAge)};

    ...

    Set setResults = cache.entrySet(createFilter("age > ?1",aEnv));

    Set setResults = cache.entrySet(new AndFilter(new LikeFilter(new KeyExtractor("getLastName"), "S%", (char) 0, false), new EqualsFilter("getHomeAddress.getState", "MA")));

    Set setResults = cache.entrySet(createFilter("key(lastName) like 'S%' and homeAddress.state = 'MA'"));


  6. Replace the calls to the *Filter methods in the aggregate statements with calls to createFilter with the appropriate Coherence Query Language.

    Table 6-3 lists the *Filter instances and their createFilter equivalents.

    Table 6-3 Filter Statements and Their Equivalent createFilter Statements in Aggregations

    Replace This *Filter Statement ... With the Equivalent createFilter Statement ...

    System.out.println("count > " + nAge + ": "+ cache.aggregate(new GreaterFilter("getAge", nAge), new Count()));

    System.out.println("count > " + nAge + ": " + cache.aggregate(createFilter("age > ?1", aEnv), new Count()));

    System.out.println("min age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge")));

    Filter always = createFilter("true");

    System.out.println("min age: " + cache.aggregate(always, new LongMin("getAge")));

    System.out.println("avg age: " + cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge")));

    System.out.println("avg age: " + cache.aggregate(always, new DoubleAverage("getAge")));

    System.out.println("max age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge")));

    System.out.println("max age: " + cache.aggregate(always, new LongMax("getAge")));


When you are finished with the code replacements, QueryExample.java looks similar to Example 6-1.

Example 6-1 Edited QueryExample File

package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
 
import com.tangosol.util.Filter;
import static com.tangosol.util.QueryHelper.*;
 
import com.tangosol.util.aggregator.Count;
// import com.tangosol.util.extractor.ChainedExtractor;
// import com.tangosol.util.extractor.KeyExtractor;
// import com.tangosol.util.extractor.ReflectionExtractor;
 
// import com.tangosol.util.aggregator.Count;
import com.tangosol.util.aggregator.DoubleAverage;
import com.tangosol.util.aggregator.LongMax;
import com.tangosol.util.aggregator.LongMin;
 
 
// import com.tangosol.util.filter.AlwaysFilter;
// import com.tangosol.util.filter.AndFilter;
// import com.tangosol.util.filter.EqualsFilter;
// import com.tangosol.util.filter.GreaterFilter;
// import com.tangosol.util.filter.LikeFilter;
// import com.tangosol.util.filter.NotEqualsFilter;
 
import java.util.Iterator;
import java.util.Set;
 
 
/**
* QueryExample runs sample queries for contacts.
*
*/
public class QueryExample{
  
    // ----- QueryExample methods ---------------------------------------
 
    public static void main(String[] args) {
      NamedCache cache = CacheFactory.getCache("ContactsCache");
       query(cache);
    }
    /**
    * Perform the example queries
    *
    */
     public static void query(NamedCache cache)
        {
        // Add indexes to make queries more efficient
        // ReflectionExtractor reflectAddrHome =
        //     new ReflectionExtractor("getHomeAddress");
 
        // Add an index for the age
        // cache.addIndex(new ReflectionExtractor("getAge"), true, null);
        cache.addIndex(createExtractor("age"), true, null);
 
        // Add index for state within home address
        // cache.addIndex(new ChainedExtractor(reflectAddrHome,
        //        new ReflectionExtractor("getState")), true, null);
        cache.addIndex(createExtractor("homeAddress.state"), false, null); 
 
        // Add index for state within work address
        // cache.addIndex(new ChainedExtractor(
        //        new ReflectionExtractor("getWorkAddress"),
        //        new ReflectionExtractor("getState")), true, null);
        cache.addIndex(createExtractor("workAddress.state"),false, null); 
 
        // Add index for city within home address
        // cache.addIndex(new ChainedExtractor(reflectAddrHome,
        //        new ReflectionExtractor("getCity")), true, null);
        cache.addIndex(createExtractor("homeAddress.city"), true,  null); 
 
        // Find all contacts who live in Massachusetts
        // Set setResults = cache.entrySet(new EqualsFilter(
        //         "getHomeAddress.getState", "MA"));
        Set setResults = cache.entrySet(createFilter("homeAddress.state = 'MA'")); 
            printResults("MA Residents", setResults); 
 
        // Find all contacts who live in Massachusetts and work elsewhere
        // setResults = cache.entrySet(new AndFilter(
        //        new EqualsFilter("getHomeAddress.getState", "MA"),
        //        new NotEqualsFilter("getWorkAddress.getState", "MA")));
        setResults = cache.entrySet(createFilter("homeAddress.state is 'MA' and workAddress is not 'MA'"));
            printResults("MA Residents, Work Elsewhere", setResults);
 
        // Find all contacts whose city name begins with 'S'
        // setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity",
        //         "S%"));
        setResults = cache.entrySet(createFilter("homeAddress.city like 'S%'")); 
            printResults("City Begins with S", setResults);
 
        final int nAge = 42;
        Object[] aEnv  =  new Object[] {new Integer(nAge)}; 
        // Find all contacts who are older than nAge
        // setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
        setResults = cache.entrySet(createFilter("age > ?1",aEnv)); 
             printResults("Age > " + nAge, setResults);
 
        // Find all contacts with last name beginning with 'S' that live
        // in Massachusetts. Uses both key and value in the query.
        // setResults = cache.entrySet(new AndFilter(
        //     new LikeFilter(new KeyExtractor("getLastName"), "S%",
        //   (char) 0, false),
        //         new EqualsFilter("getHomeAddress.getState", "MA")));
        setResults = cache.entrySet(createFilter("key(lastName) like 'S%' and homeAddress.state = 'MA'"));
        setResults = cache.entrySet(createFilter("key().lastName like 'S%' and homeAddress.state = 'MA'"));
            printResults("Last Name Begins with S and State Is MA", setResults);
        
        // Count contacts who are older than nAge
        // System.out.println("count > " + nAge + ": "+ 
        //  cache.aggregate(new GreaterFilter("getAge", nAge), new Count()));
        System.out.println("count > " + nAge + ": " + cache.aggregate(
                    createFilter("age > ?1", aEnv), new Count()));
              
        // Find minimum age
        // System.out.println("min age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge")));
        Filter always = createFilter("true");
        System.out.println("min age: " + cache.aggregate(always, new LongMin("getAge")));
              
        // Calculate average age
        // System.out.println("avg age: " + cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge")));
        System.out.println("avg age: " + cache.aggregate(always, new DoubleAverage("getAge")));
              
        // Find maximum age
        // System.out.println("max age: " +
        // cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge")));
        System.out.println("max age: " + cache.aggregate(always, new LongMax("getAge")));

 System.out.println("------QueryLanguageExample completed------");
                    
        }
 
    /**
    * Print results of the query
    *
    * @param sTitle      the title that describes the results
    * @param setResults  a set of query results
    */
    private static void printResults(String sTitle, Set setResults)
        {
        System.out.println(sTitle);
        for (Iterator iter = setResults.iterator(); iter.hasNext(); )
            {
            System.out.println(iter.next());
            }
        }
    }

6.3 Rerunning the Query Example

To rerun the query example:

  1. Stop any running cache servers. See "Stopping Cache Servers" for more information.

  2. Restart the ContactsCacheServer.

  3. Run the DataGenerator, LoaderExample, and QueryExample files.

  4. After printing all of the contact information in the cache, it displays the results of the queries. The results should look similar to the following examples.

Note:

The DataGenerator program produces random names, cities and birthdates. Your output may be different.

Example 6-2 illustrates the output of the MA Residents filter.

Example 6-2 Output of the MA Residents Filter

...
MA Residents
ConverterEntry{Key="John Hwdrrls", Value="John Hwdrrls
Addresses
Home: 369 Beacon St.
 
Fetggv, MA 24372
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, NE 84499
US
Telephone Numbers
work: +11 88 331 2307913
home: +11 64 86 2489621
Birth Date: 1976-12-29"}
...

Example 6-3 illustrates the output of the MA Residents, Work Elsewhere filter.

Example 6-3 Output of the MA Residents, Work Elsewhere Filter

...
MA Residents, Work Elsewhere
ConverterEntry{Key="John Hwdrrls", Value="John Hwdrrls
Addresses
Home: 369 Beacon St.
 
Fetggv, MA 24372
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, NE 84499
US
Telephone Numbers
work: +11 88 331 2307913
home: +11 64 86 2489621
Birth Date: 1976-12-29"}
...

Example 6-4 illustrates the output of the City Begins with S filter.

Example 6-4 Output of the City Begins with S Filter

...
City Begins with S
ConverterEntry{Key="John Pzek", Value="John Pzek
Addresses
Home: 309 Beacon St.
 
Saqrgy, OH 81353
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, CT 78117
US
Telephone Numbers
work: +11 28 790 2035988
home: +11 61 470 7634708
Birth Date: 1971-12-31"}
...

Example 6-5 illustrates the output of the age greater than 42 filter.

Example 6-5 Output of the Age Greater than 42 Filter

...
Age > 42
ConverterEntry{Key="John Gddurqqziy", Value="John Gddurqqziy
Addresses
Home: 613 Beacon St.
 
Cxyskdo, DE 28968
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, SD 07959
US
Telephone Numbers
work: +11 31 768 5136041
home: +11 87 22 3851589
Birth Date: 1958-01-03"}
...

Example 6-6 illustrates the output of the Last Name Begins with S and State is MA filter and the output of the aggregators.

Example 6-6 Output of the State and Age Aggregators

Last Name Begins with S and State Is MA   
ConverterEntry{Key="John Syaqlojl", Value="John SyaqlojlAddressesHome: 810 Beacon St.Rgtaljwph, MA 07471USWork: 200 Newbury St.Yoyodyne, Ltd.Boston, MA 02116USTelephone Numberswork: +11 37 18 1767648home: +11 98 155 1073866Birth Date: 1974-12-30"}
...
count > 42: 446  
min age: 22  
avg age: 41.126  
max age: 61