Oracle® Coherence Tutorial for Oracle Coherence Release 3.5 Part Number E14527-01 |
|
|
View PDF |
In this chapter, you will learn how to populate a Coherence cache with domain objects that are read from text files.
This chapter contains the following sections:
Until now, you have been putting and retrieving objects individually. Each put
can result in increased network traffic, especially for partitioned and replicated caches. Additionally, each call to put
returns the object it just replaced in the cache, which adds more unnecessary overhead. Loading the cache can be made much more efficient by using the putAll
method instead.
This chapter assumes that you have completed "Creating and Caching Complex Objects". It also assumes that you are familiar with using java.io.BufferedReader
to read text files, java.lang.String.split
method to parse text files, and java.text.SimpleDateFormat
to parse dates.
This exercise demonstrates how to create a console application that populates a Coherence cache with domain objects. The application can use the Coherence com.tangosol.io.pof.PortableObject
implementation.
This exercise has three parts. You will create a key that will help to obtain the Contact
object, a generator to provide data for the cache, and a loader to load the cache.
To populate a cache with domain objects:
Create a new project called Loading
.
See "Creating and Caching Complex Objects" for information on creating a new project.
Use the Address
Phone
, and Contact
classes that you created in an earlier exercise (Contacts
). Add c:\home\oracle\labs\Contacts\classes
to the project classpath (Hint: right-click the project and choose Project Properties>Libraries and Classpath. You can find more detailed information in "Adding JARS and Libraries to the Project Classpath".)
Create a contact ID class that provides a key to the employee for whom information is tracked. See "Creating a Java Class" if you need detailed information on creating a Java class.
Create the contact ID based on the employee's first name and last name. This object acts as the key to obtain the Contact
object.
Since this class will use POF serialization, it must implement PortableObject
and provide implementations for the writeExternal
and readExternal
PortableObject
methods and the equals
, hashCode
, and toString
object methods.
Note:
Cache keys and values must be serializable (for example,java.io.Serializable
). Cache keys must also provide an implementation of the hashCode()
and equals()
methods, and those methods must return consistent results across cluster nodes. This implies that the implementation of hashCode()
and equals()
must be based solely on the object's serializable state (that is, the object's non-transient fields); most built-in Java types, such as String, Integer and Date, meet this requirement. Some cache implementations (specifically the partitioned cache) use the serialized form of the key objects for equality testing, which means that keys for which equals()
returns true must serialize identically; most built-in Java types meet this requirement as well.Example 4-1 illustrates a possible solution.
Example 4-1 Simple Contact ID Class
package com.oracle.coherence.handson; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; import com.tangosol.io.pof.PortableObject; import com.tangosol.util.Base; import com.tangosol.util.HashHelper; import java.io.IOException; /** * ContactId is a key to the person for whom information is * tracked. */ public class ContactId implements PortableObject { // ----- constructors --------------------------------------------------- /** * Default constructor (necessary for PortableObject implementation). */ public ContactId() { } /** * Construct a contact person. * */ public ContactId(String FirstName, String LastName) { super(); this.FirstName = FirstName; this.LastName = LastName; } // ----- accessors ------------------------------------------------------ /** * Return the first name. * */ public String getFirstName() { return FirstName; } /** * Return the last name. * */ public String getLastName() { return LastName; } // ----- PortableObject interface --------------------------------------- public void readExternal(PofReader reader) throws IOException { FirstName = reader.readString(0); LastName = reader.readString(1); } public void writeExternal(PofWriter writer) throws IOException { writer.writeString(0, FirstName); writer.writeString(1, LastName); } // ----- Object methods ------------------------------------------------- public boolean equals(Object oThat) { if (this == oThat) { return true; } if (oThat == null) { return false; } ContactId that = (ContactId) oThat; return Base.equals(getFirstName(), that.getFirstName()) && Base.equals(getLastName(), that.getLastName()); } public int hashCode() { return HashHelper.hash(getFirstName(), HashHelper.hash(getLastName(), 0)); } public String toString() { return getFirstName() + " " + getLastName(); } // ----- data members --------------------------------------------------- /** * First name. */ private String FirstName; /** * Last name. */ private String LastName; }
Add a <user-type>
entry for ContactID
to the contacts-pof-config.xml
file.
Example 4-2 POF Configuration File with the ContactId Entry
<?xml version="1.0"?> <!DOCTYPE pof-config SYSTEM "pof-config.dtd"> <pof-config> <user-type-list> <!-- coherence POF user types --> <include>coherence-pof-config.xml</include> <!-- com.tangosol.examples package --> <user-type> <type-id>1001</type-id> <class-name>com.oracle.coherence.handson.Contact</class-name> </user-type> <user-type> <type-id>1002</type-id> <class-name>com.oracle.coherence.handson.Address</class-name> </user-type> <user-type> <type-id>1003</type-id> <class-name>com.oracle.coherence.handson.Phone</class-name> </user-type> <user-type> <type-id>1004</type-id> <class-name>com.oracle.coherence.handson.ContactId</class-name> </user-type> </user-type-list> <allow-interfaces>true</allow-interfaces> <allow-subclasses>true</allow-subclasses> </pof-config>
Create a Java class named DataGenerator
to generate random contact names and addresses. See "Creating a Java Class" if you need detailed information on creating a Java class.
Hint: Use the Address
, Phone
, and Contact
classes that you created in the earlier exercise. Use java.util.Random
to generate some random names, addresses, telephone numbers, and ages.
Example 4-3 illustrates a possible solution. This solution creates a text file, contacts.cvs
, that contains the employee contact information. The file will be stored in the root of the project directory, in this case, C:\home\oracle\labs\Loading
You might see an error in JDeveloper caused by the LoaderExample.FILENAME
declaration. This can be ignored—you will create LoaderExample
in the next step.
Example 4-3 Sample Data Generation Class
package com.oracle.coherence.handson; import com.oracle.coherence.handson.Address; import com.oracle.coherence.handson.Contact; import com.oracle.coherence.handson.Phone; import com.tangosol.util.Base; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.sql.Date; import java.util.Collections; import java.util.Random; /** * DataGenerator is a generator of sample contacts. */ public class DataGenerator { // ----- static methods ------------------------------------------------- /** * Generate contacts. */ public static void main(String[] asArg) throws IOException { String sFile = asArg.length > 0 ? asArg[0] : LoaderExample.FILENAME; int cCon = asArg.length > 1 ? Integer.parseInt(asArg[1]) : 1000; OutputStream out = new FileOutputStream(sFile); generate(out, cCon); out.close(); } /** * Generate the contacts and write them to a file. */ public static void generate(OutputStream out, int cContacts) throws IOException { PrintWriter writer = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out))); for (int i = 0; i < cContacts; ++i) { StringBuffer sb = new StringBuffer(256); //contact person sb.append("John,") .append(getRandomName()) .append(','); // home and work addresses sb.append(Integer.toString(Base.getRandom().nextInt(999))) .append(" Beacon St.,,") /*street1,empty street2*/ .append(getRandomName()) /*random city name*/ .append(',') .append(getRandomState()) .append(',') .append(getRandomZip()) .append(",US,Yoyodyne Propulsion Systems,") .append("330 Lectroid Rd.,Grover's Mill,") .append(getRandomState()) .append(',') .append(getRandomZip()) .append(",US,"); // home and work telephone numbers sb.append("home,") .append(Base.toDelimitedString(getRandomPhoneDigits(), ",")) .append(",work,") .append(Base.toDelimitedString(getRandomPhoneDigits(), ",")) .append(','); // random birth date in millis before or after the epoch sb.append(getRandomDateInMillis()); writer.println(sb); } writer.flush(); } /** * Return a random name. * */ private static String getRandomName() { Random rand = Base.getRandom(); int cCh = 4 + rand.nextInt(7); char[] ach = new char[cCh]; ach[0] = (char) ('A' + rand.nextInt(26)); for (int of = 1; of < cCh; ++of) { ach[of] = (char) ('a' + rand.nextInt(26)); } return new String(ach); } /** * Return a random phone muber. * The phone number includes access, country, area code, and local * number. * */ private static int[] getRandomPhoneDigits() { Random rand = Base.getRandom(); return new int[] { 11, // access code rand.nextInt(99), // country code rand.nextInt(999), // area code rand.nextInt(9999999) // local number }; } /** * Return a random Phone. * */ private static Phone getRandomPhone() { int[] anPhone = getRandomPhoneDigits(); return new Phone((short)anPhone[0], (short)anPhone[1], (short)anPhone[2], anPhone[3]); } /** * Return a random Zip code. * */ private static String getRandomZip() { return Base.toDecString(Base.getRandom().nextInt(99999), 5); } /** * Return a random state. * */ private static String getRandomState() { return STATE_CODES[Base.getRandom().nextInt(STATE_CODES.length)]; } /** * Return a random date in millis before or after the epoch. * */ private static long getRandomDateInMillis() { return (Base.getRandom().nextInt(40) - 20) * Contact.MILLIS_IN_YEAR; } /** * Generate a Contact with random information. * */ public static Contact getRandomContact() { return new Contact("John", getRandomName(), new Address("1500 Boylston St.", null, getRandomName(), getRandomState(), getRandomZip(), "US"), new Address("8 Yawkey Way", null, getRandomName(), getRandomState(), getRandomZip(), "US"), Collections.singletonMap("work", getRandomPhone()), new Date(getRandomDateInMillis())); } // ----- constants ------------------------------------------------------ /** * US Postal Service two letter postal codes. */ private static final String[] STATE_CODES = { "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "OF", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY" }; }
Create a console application (Java class) called LoadingExample
. See "Creating a Java Class" if you need detailed information.
Use input streams and buffered readers to load the entire employee information contained in the contacts.csv
file into a single Coherence cache.
The application should contain the code to parse the employee information in the data file. Once you have this information, construct the individual contacts to put into the cache. To conserve processing effort and minimize network traffic, use the putAll
method to load the cache.
Example 4-4 illustrates a possible solution.
Example 4-4 Sample Cache Loading Program
package com.tangosol.examples.contacts; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; import com.tangosol.examples.model.ContactId; import com.tangosol.examples.model.Address; import com.tangosol.examples.model.Phone; import com.tangosol.examples.model.Contact; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.sql.Date; import java.util.HashMap; import java.util.Map; /** * LoaderExample loads contacts into the cache from a files. * */ public class LoaderExample { // ----- static methods ------------------------------------------------- /** * Load contacts. */ public static void main (String[] asArg) throws IOException { String sFile = asArg.length > 0 ? asArg[0] : FILENAME; String sCache = asArg.length > 1 ? asArg[1] : CACHENAME; System.out.println("input file: " + sFile); System.out.println("cache name: " + sCache); new LoaderExample().load(CacheFactory.getCache(sCache), new FileInputStream(sFile)); CacheFactory.shutdown(); } /** * Load cache from stream. * */ public void load(NamedCache cache, InputStream in) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); Map mapBatch = new HashMap(1024); String sRecord; int cRecord = 0; while ((sRecord = reader.readLine()) != null) { // parse record String[] asPart = sRecord.split(","); int ofPart = 0; String sFirstName = asPart[ofPart++]; String sLastName = asPart[ofPart++]; ContactId id = new ContactId(sFirstName, sLastName); Address addrHome = new Address( /*streetline1*/ asPart[ofPart++], /*streetline2*/ asPart[ofPart++], /*city*/ asPart[ofPart++], /*state*/ asPart[ofPart++], /*zip*/ asPart[ofPart++], /*country*/ asPart[ofPart++]); Address addrWork = new Address( /*streetline1*/ asPart[ofPart++], /*streetline2*/ asPart[ofPart++], /*city*/ asPart[ofPart++], /*state*/ asPart[ofPart++], /*zip*/ asPart[ofPart++], /*country*/ asPart[ofPart++]); Map mapTelNum = new HashMap(); for (int c = asPart.length - 1; ofPart < c; ) { mapTelNum.put(/*type*/ asPart[ofPart++], new Phone( /*access code*/ Short.parseShort(asPart[ofPart++]), /*country code*/ Short.parseShort(asPart[ofPart++]), /*area code*/ Short.parseShort(asPart[ofPart++]), /*local num*/ Integer.parseInt(asPart[ofPart++]))); } Date dtBirth = new Date(Long.parseLong(asPart[ofPart])); // Construct Contact and add to batch mapBatch.put(id, new Contact(sFirstName, sLastName, addrHome, addrWork, mapTelNum, dtBirth)); ++cRecord; if (cRecord % 1024 == 0) { // load batch cache.putAll(mapBatch); mapBatch.clear(); System.out.print('.'); System.out.flush(); } } if (!mapBatch.isEmpty()) { // load final batch cache.putAll(mapBatch); } System.out.println("Added " + cRecord + " entries to cache"); } // ----- constants ------------------------------------------------------ /** * Default cache name. */ public static final String CACHENAME = "ContactsCache"; /** * Default file name. */ public static final String FILENAME = "contacts.csv"; }
Add the cache configuration file (C:\home\oracle\labs
) to the project's classpath. See "Adding JARS and Libraries to the Project Classpath" if you need detailed information.
Add the path to the cache configuration to the Java Options field of the Project Properties>Run/Debug/Profile.
-Dtangosol.coherence.cacheconfig=\home\oracle\labs\contacts-cache-config.xml
Edit the contacts-cache-server.cmd
file to add the compiled classes for the Loading
project (in this case, C:\home\oracle\labs\Loading\classes
). Ensure that the path to the directory where the cache configuration file is stored (in this case, C:\home\oracle\labs
) is already listed.
The contacts-cache-server.cmd
file should look similar to Example 4-5:
Example 4-5 Sample contacts-cache-server.cmd File
@echo off
setlocal
if (%COHERENCE_HOME%)==() (
set COHERENCE_HOME=c:\oracle\product\coherence
)
set COH_OPTS=%COH_OPTS% -server -cp %COHERENCE_HOME%\lib\coherence.jar;C:\home\oracle\labs\Contacts\classes;C:\home\oracle\labs\Loading\classes;C:\home\oracle\labs;
set COH_OPTS=%COH_OPTS% -Dtangosol.coherence.cacheconfig=\home\oracle\labs\contacts-cache-config.xml
java %COH_OPTS% -Xms1g -Xmx1g -Xloggc: com.tangosol.net.DefaultCacheServer %2 %3 %4 %5 %6 %7
:exit
Start the cache server with the contacts-cache-server.cmd
file.
Run DataGenerator
from JDeveloper, then run LoaderExample
.
A stream of employee contact information should appear in the JDeveloper output window. Figure 4-1 illustrates an example.
This exercise introduces the concept of querying and aggregating data in a cache. This exercise demonstrates how to:
query the cache for specific data
aggregate information within the cache
After putting complex objects in the named caches, you look at querying and aggregating information within the grid. The com.tangosol.util.QueryMap
interface provides methods for managing the values or keys within a cache. You can use filters to restrict your results. You can also define indexes to optimize your queries.
Because Coherence serializes information when storing, you also have the overhead of deserializing when querying. When indexes are added, the values kept in the index itself are deserialized and therefore, offer quicker access time. The objects that are indexed are always kept serialized.
Some of the more useful methods in the QueryMap
interface are:
Set
entrySet(Filterfilter)
—Returns a set of entries that are contained in the map that satisfy the filter
addIndex(ValueExtractorextractor,booleanfOrdered, Comparator comparator)
—Adds an index
Set
keySet(Filter filter)
—Similar to entrySet
, but returns keys, not values
It is important to note that filtering occurs at Cache Entry Owner level. In a partitioned topology, filtering can be done in parallel because it is the primary partitions that do the filtering. The QueryMap
interface uses the Filter
classes. You can find more information on these classes in the API for the com.tangosol.util.filter
package.
All Coherence NamedCaches
implement the com.tangosol.util.QueryMap
interface. This allows NamedCaches
to support the searching for keys or entries in a cache that satisfy some condition. The condition can being represented as an object that implements the com.tangosol.util.Filter
interface.
The com.tangosol.util.filter
package contains several predefined classes that provide implementations of standard query expressions. Examples of these classes include GreaterFilter
, GreaterEquals
, LikeFilter
, NotEqualsFilter
, InFilter
, and so on. You can use these filters to construct and compose object-based equivalents of most SQL WHERE
clause expressions.
Note:
Coherence does not provide aSQLFilter
because it is unlikely that the objects placed in a cache are modeled in a relational manner, that is, using rows and columns (as they are typically represented in a database). Additionally, it is common that objects placed in a cache are not easily modeled using relational models, for example, large blobs.The Filter
classes use standard Java method reflection to represent test conditions. For example, the following filter represents the condition where the value returned from the getHomeAddress.getState
method on an object in a cache is for Massachusetts (MA
):
(new EqualsFilter("getHomeAddress.getState", "MA");
If the object tested with this filter fails to have a getSymbol
method, then the test will fail.
A couple of examples will make things clearer:
Return a set of people who live in a city whose name begins with the letter "S
":
Set sPeople = cache.entrySet(new LikeFilter("getHomeAddress.getCity","S%");
Return a set containing people over age 42:
final int nAge = 42;// Find all contacts who are older than nAgeSet sSeniors = cache.entrySet(new GreaterFilter("getAge", nAge));
In addition to the entrySet
and keySet
methods defined by the QueryMap
interface, Coherence supports the definition of indexes, using the addIndex
method, to improve query performance. Unlike relational database systems, where indexes are defined according to well-known and strictly enforced collections of named columns (that is, a schema), Coherence does not have a schema. Though lacking a formal schema for data allows for significant flexibility and polymorphism, within applications, it means that an approach different from that of traditional database systems is required to define indexes and therefore, increase query performance.
To define the values that are to be indexed for each object placed in a cache, Coherence introduces the concept of a ValueExtractor
. A com.tangosol.util.ValueExtractor
is a simple interface that defines an "extract
" method. If given an object parameter, a ValueExtractor
returns some value based on the parameter.
A simple example of a ValueExtractor
implementation is the com.tangosol.util.extractor.ReflectionExtractor, which uses reflection to return the result of a method call on an object. For example:
new ReflectionExtractor("getCity")
ValueExtractors
may be used throughout the Coherence API. Typically, however, they are used to define indexes.
An especially useful type of extractor is the ChainedExtractor. This is a composite ValueExtractor
implementation based on an array of extractors. Extractors in the array are applied sequentially left-to-right, so a result of a previous extractor serves as a target object for a next one. For example:
new ChainedExtractor(new ReflectionExtractor("getHomeAddress"),new ReflectionExtractor("getState"))
This example assumes that HomeAddress
and State
belong to a complex Contact
object. ChainedExtractor
first uses reflection to call getHomeAddress
on each cached Contact
object, and then uses reflection to call getState
on the set of returned HomeAddress
.
To aggregate and query data in the cache:
Create a new Java class called QueryExample
to perform your queries. Ensure that the class has a main
method. See "Creating a Java Class" if you need detailed information.
Use the entrySet
method to get the employee contact information for:
all employees who live in Massachusetts. Hint:
cache.entrySet(new EqualsFilter("getHomeAddress.getState", "MA"));
all employees who live in Massachusetts and work elsewhere. Hint:
cache.entrySet(new AndFilter( new EqualsFilter("getHomeAddress.getState", "MA"), new NotEqualsFilter("getWorkAddress.getState", "MA")));
all employees whose city name begins with 'S'. Hint:
cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%"));
all employees with last name beginning with 'S' that live in Massachusetts. Use both key and value in the query. Hint:
cache.entrySet(new AndFilter( new LikeFilter(new KeyExtractor("getLastName"), "S%", (char) 0, false), new EqualsFilter("getHomeAddress.getState", "MA")));
all employees who are older than a specified age. Hint:
final int nAge = 42; setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
Use indexes to improve performance. Hints: Find the addIndex
method in the Javadoc for the QueryMap
interface.
Example 4-6 illustrates a possible solution.
Example 4-6 Sample QueryExample Class
package com.oracle.coherence.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; import com.tangosol.util.extractor.ChainedExtractor; import com.tangosol.util.extractor.KeyExtractor; import com.tangosol.util.extractor.ReflectionExtractor; 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 indices to make queries more efficient ReflectionExtractor reflectAddrHome = new ReflectionExtractor("getHomeAddress"); // Add an index for the age cache.addIndex(new ReflectionExtractor("getAge"), true, null); // Add index for state within home address cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getState")), true, null); // Add index for state within work address cache.addIndex(new ChainedExtractor( new ReflectionExtractor("getWorkAddress"), new ReflectionExtractor("getState")), true, null); // Add index for city within home address cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getCity")), true, null); // Find all contacts who live in Massachusetts Set setResults = cache.entrySet(new EqualsFilter( "getHomeAddress.getState", "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"))); printResults("MA Residents, Work Elsewhere", setResults); // Find all contacts whose city name begins with 'S' setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%")); printResults("City Begins with S", setResults); final int nAge = 42; // Find all contacts who are older than nAge setResults = cache.entrySet(new GreaterFilter("getAge", nAge)); 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"))); printResults("Last Name Begins with S and State Is MA", setResults); } /** * 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()); } } }
Stop all running cache servers. Restart the Contacts
cache server with contacts-cache-server.cmd
. Run the DataGenerator
file, the LoadExample
file, then the QueryExample
file. After printing all of the contact information in the cache, it displays the results of the queries. The results should look similar to Example 4-1.
Add code in QueryExample.java
to perform aggregations on the data in the cache.
An EntryAggregator
(com.tangosol.util.InvocableMap.EntryAggregator
) enables you to perform operations on all or a specific set of objects and return an aggregation. EntryAggregators
are essentially agents that execute services in parallel against the data within the cluster.
Aggregations are performed in parallel and can benefit from the addition of cluster members.
There are two ways of aggregating: aggregate over a collection of keys or by specifying a filter. Example 4-7 illustrates the methods that perform these aggregations.
Example 4-7 Methods to Aggregate Over Keys or by Specifying Filters
Object aggregate(Collection keys, InvocableMap.entryAggregator agg) Object aggregate(Filter filter, InvocableMap.entryAggregator agg)
The following example uses a filter.
Using aggregations, write code in the QueryExample class to calculate the following:
—the number of employees that are greater than a specified age. Hint: use the GreaterFilter
and the Count
class:
cache.aggregate(new GreaterFilter("getAge", nAge), new Count())
—lowest age of the set of employees. Hint: use the AlwaysFilter
and the LongMin
class:
cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))
—highest age of the set of employees. Hint: use the AlwaysFilter
and the LongMax
class:
cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))
—average age of employees. Hint: use the AlwaysFilter
and the DoubleAverage
class:
cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge")
Import the Count
, DoubleAverage
, LongMax
, and LongMin
aggregator classes.
import com.tangosol.util.aggregator.Count; import com.tangosol.util.aggregator.DoubleAverage; import com.tangosol.util.aggregator.LongMax; import com.tangosol.util.aggregator.LongMin;
The QueryExample.java
file should now look similar to this:
Example 4-8 QueryExample with Aggregation
package com.oracle.coherence.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; 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.extractor.ChainedExtractor; import com.tangosol.util.extractor.KeyExtractor; import com.tangosol.util.extractor.ReflectionExtractor; 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 indices to make queries more efficient ReflectionExtractor reflectAddrHome = new ReflectionExtractor("getHomeAddress"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getState")), true, null); cache.addIndex(new ChainedExtractor( new ReflectionExtractor("getWorkAddress"), new ReflectionExtractor("getState")), true, null); cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getCity")), true, null); // Find all contacts who live in Massachusetts Set setResults = cache.entrySet(new EqualsFilter( "getHomeAddress.getState", "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"))); printResults("MA Residents, Work Elsewhere", setResults); // Find all contacts whose city name begins with 'S' setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%")); printResults("City Begins with S", setResults); final int nAge = 42; // Find all contacts who are older than nAge setResults = cache.entrySet(new GreaterFilter("getAge", nAge)); 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"))); 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())); // Find minimum age System.out.println("min age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))); // Calculate average age System.out.println("avg age: " + cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge"))); // Find maximum age System.out.println("max age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))); } /** * Print results of the query * */ private static void printResults(String sTitle, Set setResults) { System.out.println(sTitle); for (Iterator iter = setResults.iterator(); iter.hasNext(); ) { System.out.println(iter.next()); } } }
Stop and start the cache server after making this change.
Start the Contacts cache server with the contacts-cache-server.cmd
file. Run the QueryExample
application.
You should see out output similar to Example 4-3 in JDeveloper.