Skip to content

Using Boon's data repo by example

RichardHightower edited this page Feb 16, 2014 · 9 revisions

Boon Home | Boon Source | If you are new to boon, you might want to start here. Simple opinionated Java for the novice to expert level Java Programmer. Low Ceremony. High Productivity. A real boon to Java to developers!

What if it were easy to query a complex set of Java objects at runtime? What if there were an API that kept your object indexes (really just TreeMaps, and HashMaps) in sync.? Well then you would have Boon's data repo. This article shows how to use Boon's data repo utilities to query Java objects. This is part one. There can be many, many parts. :)

Boon's data repo makes doing index based queries on collections a lot easier.

Why Boon data repo

Boon's data repo allows you to treat Java collections more like a database at least when it comes to querying the collections. Boon's data repo is not an in-memory database, and cannot substitute arranging your objects into data structures optimized for your application.

If you want to spend your time providing customer value and building your objects and classes and using the Collections API for your data structures, then DataRepo is meant for you. This does not preclude breaking out the Knuth books and coming up with an optimized data structure. It just helps keep the mundane things easy so you can spend your time making the hard things possible.

Born out of need

This project came out of a need. I was working on a project that planned to store large collection of domain objects in-memory for speed, and somebody asked an all to important question that I overlooked. How are we going to query this data. My answer was we will use the Collections API and the Streaming API. Then I tried to do this... Hmmm...

I also tired using the JDK 8 stream API on a large data set, and it was slow. (Boon's data repo works with JDK7 and JDK8). It was a linear search/filter. This is by design, but for what I was doing, it did not work. I needed indexes to support arbitrary queries.

Boon's data repo augments the streaming API.

Boon's data repo does not endeavor to replace the JDK 8 stream API, and in fact it works well with it. Boon's data repo allows you to create indexed collections. The indexes can be anything (it is pluggable).

At this moment in time, Boon's data repo indexes are based on ConcurrentHashMap and ConcurrentSkipListMap.

By design, Boon's data repo works with standard collection libraries. There is no plan to create a set of custom collections. One should be able to plug in Guava, Concurrent Trees or Trove if one desires to do so.

It provides a simplified API for doing so. It allows linear search for a sense of completion but I recommend using it primarily for using indexes and then using the streaming API for the rest (for type safety and speed).

Sneak peak before the step by step

Let's say you have a method that creates 200,000 employee objects like this:

  List<Employee> employees = TestHelper.createMetricTonOfEmployees(200_000);

So now we have 200,000 employees. Let's search them...

First wrap Employees in a searchable query:

  employees = query(employees);

Now search:

  List<Employee> results = query(employees, eq("firstName", firstName));

So what is the main difference between the above and the stream API?

  employees.stream().filter(emp -> emp.getFirstName().equals(firstName)

About a factor of 20,000% faster to use Boon's DataRepo! Ah the power of HashMaps and TreeMaps. :)

There is an API that looks just like your built-in collections. There is also an API that looks more like a DAO object or a Repo Object.

A simple query with the Repo/DAO object looks like this:

  List<Employee> employees = repo.query(eq("firstName", "Diana"));

A more involved query would look like this:

  List<Employee> employees = repo.query(
          and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999")));

Or this:

  List<Employee> employees = repo.query(
          and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000),
                  gte("salary", 190_000)));

Or even this:

  List<Employee> employees = repo.query(
          and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000)));

Or if you want to use JDK 8 stream API, this works with it not against it:

  int sum = repo.query(eq("lastName", "Smith")).stream().filter(emp -> emp.getSalary()>50_000)
          .mapToInt(b -> b.getSalary())
          .sum();

The above would be much faster if the number of employees was quite large. It would narrow down the employees whose name started with Smith and had a salary above 50,000. Let's say you had 100,000 employees and only 50 named Smith so now you narrow to 50 quickly by using the index which effectively pulls 50 employees out of 100,000, then we do the filter over just 50 instead of the whole 100,000.

Java Boon - Getting Started with Boon DataRepo Step by Step

Using Boon DataRepo by example

Boon's data repo allows you to quickly query complex trees of Java objects that are stored inside of Java collections.

Let's start with some simple examples.

First here are the imports:

//Sample Test model
import com.examples.model.test.Email;
import com.examples.security.model.User;
import static com.examples.security.model.User.user;

//Data repo classes
import org.boon.datarepo.DataRepoException;
import org.boon.datarepo.Repo;
import org.boon.datarepo.RepoBuilder;
import org.boon.datarepo.Repos;


//Boon utility methods
import org.boon.Lists;
import org.boon.core.Typ;
import static org.boon.Boon.putl;
import static org.boon.Boon.puts;
import static org.boon.Exceptions.die;
import static org.boon.Lists.idx;
import static org.boon.criteria.CriteriaFactory.eq;
import static org.boon.criteria.CriteriaFactory.eqNested;
import static org.boon.criteria.CriteriaFactory.notEq;

We will create a repository of user objects and then perform some basic queries against them.

Some house keeping. Let's create a constant for email so we don't keep using it over an over.

    private static String EMAIL = "email";

Let's get going.

    public static void main ( String... args ) {


        boolean test = true;

Next create a repo builder that creates a user repo. User is a sample domain object, and it has one property called email.

A repo is core concept in Boon data repo. Think of a repo as a collection that allows queries. The queries can be very fast as they use search indexes (TreeMap), and lookup indexes (HashMap).

       
        RepoBuilder repoBuilder = Repos.builder ();
        repoBuilder.primaryKey ( EMAIL );

The above creates a repo whose primary key is the property email.

Next we create a repo of that has a key of type String.class and an item class of a User.class.

        final Repo< String, User > userRepo = repoBuilder.build ( Typ.string, user );
   

The above creates the userRepo using the builder. Note that Typ.string is just equal to String.class.

Now lets add some users to our repo so we can test it out.

        final List<User> users = Lists.list (
                user ( "rick.hightower@foo.com" ),
                user ( "bob.jones@foo.com" ),
                user ( "sam.jones@google.com" )
        );

The method user is a static method from the class User. I will show you that in a second. List.list is a helper method that creates java.util.List (see wiki for more detail on Boon).

The userRepo allows you to add a list of users to it with the addAll method as follows:

        userRepo.addAll ( users );

Now that we have a repo with users let's run some queries against it.

Here is a query using the eq criteria.

        List<User> results =
                userRepo.query ( eq ( EMAIL, "rick.hightower@foo.com") );

The method userRepo.query returns a java.util.List of Users.

Let's print out all of the users.

        putl ( "Simple Query using Equals Results", results );

Boon has two utility methods puts, and putl. The puts method is similar to Ruby puts. The putl method puts out lines per item.

Output:

    [User{email='rick.hightower@foo.com'}]

Now let's make sure we got what we expected.

        /** Same as results.get(0) */
        User rick = idx (results, 0);

        /* Make sure we got what we wanted. */
        test |= Objects.equals (rick.getEmail (), "rick.hightower@foo.com") ||
                die( "Rick's email not equal to 'rick.hightower@foo.com' " );

The idx method is a core concept in Boon but not in Boon data repo. The idx method indexes things. There are quite a few wiki pages on slice notation and indexing in the Boon wiki.

Now let's try some different queries on our repo collection, let's try a notEq criteria:

        results =
                userRepo.query ( notEq( EMAIL, "rick.hightower@foo.com" ) );

There are many criteria operators in Boon's data repo, here is partial list:

static Group	        and(Criteria... expressions) 
static Criterion	between(java.lang.Class clazz, java.lang.Object name, java.lang.String svalue, java.lang.String svalue2) 
static Criterion	between(java.lang.Object name, java.lang.Object value, java.lang.Object value2) 
static Criterion	between(java.lang.Object name, java.lang.String svalue, java.lang.String svalue2) 
static Criterion	contains(java.lang.Object name, java.lang.Object value) 
static Criterion	empty(java.lang.Object name) 
static Criterion	endsWith(java.lang.Object name, java.lang.Object value) 
static Criterion	eq(java.lang.Object name, java.lang.Object value) 
static Criterion	eqNested(java.lang.Object value, java.lang.Object... path) 
static Criterion	gt(java.lang.Object name, java.lang.Object value) 
static Criterion	gt(java.lang.Object name, java.lang.String svalue) 
static Criterion	gte(java.lang.Object name, java.lang.Object value) 
static Criterion	implementsInterface(java.lang.Class<?> cls) 
static Criterion	in(java.lang.Object name, java.lang.Object... values) 
static Criterion	instanceOf(java.lang.Class<?> cls) 
static Criterion	isNull(java.lang.Object name) 
static Criterion	lt(java.lang.Object name, java.lang.Object value) 
static Criterion	lte(java.lang.Object name, java.lang.Object value) 
static Not	        not(Criteria expression) 
static Criterion	notContains(java.lang.Object name, java.lang.Object value) 
static Criterion	notEmpty(java.lang.Object name) 
static Criterion	notEq(java.lang.Object name, java.lang.Object value) 
static Criterion	notIn(java.lang.Object name, java.lang.Object... values) 
static Criterion	notNull(java.lang.Object name) 
static Group	        or(Criteria... expressions) 
static Criterion	startsWith(java.lang.Object name, java.lang.Object value) 
static Criterion	typeOf(java.lang.String className) 

We will not cover them all in detail, but you can guess what they do by their name.

Let's continue our notEq example.

Now we have to make sure we get users who are not Rick. No one wants Rick. :(

        putl ( "Simple Query using Not Equals Results", results );

        /** Same as results.get(0) */
        User notRick = idx (results, 0);

        putl ( notRick );

        /* Make sure we got what we wanted, i.e. no Rick! */
        test |= !Objects.equals (notRick.getEmail (), "rick.hightower@foo.com") ||
                die( "User Not Rick's email should NOT be equal " +
                        "to 'rick.hightower@foo.com' " );

The above shows that Rick is not in the results of the not Rick query. :)

output

    [User{email='bob.jones@foo.com'}, User{email='sam.jones@google.com'}]

    User{email='bob.jones@foo.com'}

Sometimes, you know that you only want one result from a query or sometimes you just want one result period. For that you use the results method which return a ResultSet as follows:

        rick = userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) ).firstItem ();

Notice that we are using _**userRepo.results(...).firstItem() **_to get one item instead of a list of items.

What if we only expect one item and only one item then we would use a results(...).expectOne().firstItem() as follows:

        rick =  (User)     //expectOne is not generic
                userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) )
                        .expectOne ().firstItem ();

Notice that we have to use a cast. You can use results with selects, which means you can select any part of an object so firstItem may not always return user. If you don't want to cast, you can use the following form.

        rick =  userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) )
                .expectOne (user).firstItem ();

Notice we pass user to expectOne (user is of type Class and is set to User.class).

You could write the above like this instead:

        rick =  userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) )
                .expectOne (User.class).firstItem ();

I prefer having types that are easier on the eyes since they get used a bit in Boon data repo support.

If you expect one and only one, then you can use the expectOne method and if there are more than one item or zero items, then it will throw an exception demonstrated as follows:

        /** Example 6: Expect only one item with expectOne(user).firstItem() and we have many. */

        try {
            putl ( "Example 6: Failure case, we have more than one for",
                    "query using ResultSet.expectOne(user).firstItem");

            rick =  userRepo.results ( notEq ( EMAIL, "rick.hightower@foo.com" ) )
                    .expectOne (user).firstItem ();

            die("We should never get here!");

        } catch (DataRepoException ex) {
            puts ("success for Example 6");
        }

By the way, you have seen this die(...) method use. It basically just throws a runtime exception. It is a boon helper method. Boon got the die concept from Perl (http://perldoc.perl.org/functions/die.html). Boon endeavors to take good ideas from other languages, and keep them to make a productive sets of APIs.

Boon gets indexing and slicing from Python, puts from Ruby and die from Perl. There is more to come. Boon's older brother was called EasyJava (later named Facile), and EasyJava/Facile borrow many more ideas from Perl, Python and Ruby. But EasyJava/Facile were more experimental whilst Boon is better designed and meant to be curated and used on projects. Boon is not new per se. Boon is an evolution of DataRepo, EasyJava/Facile and Crank. When Boon grows up, it will be a very capable library. But I digress....

Let's take a look at our User object to show that is is just a plain old Java Object:

User class for example

    package com.examples.security.model;

    public class User  {

        public static final Class<User> user = User.class;

        public static User user (String email) {
              return new User( email );
        }


        private final String email;

        ...

        public String getEmail () {
            return email;
        }

User is a pretty simple POJO.

Boon's data repo can work with complex object hierarchies and relationships and it can setup search indexes (TreeMap), lookup indexes (HashMap), non-unique indexes (TreeMap with multi-value), and non unique lookup indexes, but that is for another time.

Just to give you an idea of what a Repo can do. A Repo is an ObjectEditor and a SearchableCollection which can produce ResultSets so it has the following method:

void	updateByFilter(java.util.List<Update> values, Criteria... expressions) 
void	updateByFilter(java.lang.String property, byte value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, char value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, double value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, float value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, int value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, long value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, java.lang.Object value, Criteria... expressions) 
void	updateByFilter(java.lang.String property, short value, Criteria... expressions) 
void	updateByFilterUsingValue(java.lang.String property, java.lang.String value, Criteria... expressions) 

void	addAll(ITEM... items) 
void	addAll(java.util.List<ITEM> items) 
void	addAllAsync(java.util.Collection<ITEM> items) 
boolean	compareAndIncrement(KEY key, java.lang.String property, byte compare) 
boolean	compareAndIncrement(KEY key, java.lang.String property, int compare) 
boolean	compareAndIncrement(KEY key, java.lang.String property, long compare) 
boolean	compareAndIncrement(KEY key, java.lang.String property, short compare) 
boolean	compareAndUpdate(KEY key, java.lang.String property, byte compare, byte value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, char compare, char value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, double compare, double value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, float compare, float value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, int compare, int value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, long compare, long value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, java.lang.Object compare, java.lang.Object value) 
boolean	compareAndUpdate(KEY key, java.lang.String property, short compare, short value) 
ITEM	get(KEY key) 
byte	getByte(ITEM item, java.lang.String property) 
char	getChar(ITEM item, java.lang.String property) 
double	getDouble(ITEM item, java.lang.String property) 
float	getFloat(ITEM item, java.lang.String property) 
int	getInt(ITEM item, java.lang.String property) 
KEY	getKey(ITEM item) 
long	getLong(ITEM item, java.lang.String property) 
java.lang.Object	getObject(ITEM item, java.lang.String property) 
short	getShort(ITEM item, java.lang.String property) 
<T> T	getValue(ITEM item, java.lang.String property, java.lang.Class<T> type) 
void	modify(ITEM item) 
void	modify(ITEM item, java.lang.String property, byte value) 
void	modify(ITEM item, java.lang.String property, char value) 
void	modify(ITEM item, java.lang.String property, double value) 
void	modify(ITEM item, java.lang.String property, float value) 
void	modify(ITEM item, java.lang.String property, int value) 
void	modify(ITEM item, java.lang.String property, long value) 
void	modify(ITEM item, java.lang.String property, java.lang.Object value) 
void	modify(ITEM item, java.lang.String property, short value) 
void	modify(ITEM item, Update... values) 
void	modifyAll(java.util.Collection<ITEM> items) 
void	modifyAll(ITEM... items) 
void	modifyByValue(ITEM item, java.lang.String property, java.lang.String value) 
void	put(ITEM item) 
byte	readByte(KEY key, java.lang.String property) 
char	readChar(KEY key, java.lang.String property) 
double	readDouble(KEY key, java.lang.String property) 
float	readFloat(KEY key, java.lang.String property) 
int	readInt(KEY key, java.lang.String property) 
long	readLong(KEY key, java.lang.String property) 
byte	readNestedByte(KEY key, java.lang.String... properties) 
char	readNestedChar(KEY key, java.lang.String... properties) 
double	readNestedDouble(KEY key, java.lang.String... properties) 
float	readNestedFloat(KEY key, java.lang.String... properties) 
int	readNestedInt(KEY key, java.lang.String... properties) 
long	readNestedLong(KEY key, java.lang.String... properties) 
short	readNestedShort(KEY key, java.lang.String... properties) 
java.lang.Object	readNestedValue(KEY key, java.lang.String... properties) 
java.lang.Object	readObject(KEY key, java.lang.String property) 
short	readShort(KEY key, java.lang.String property) 
<T> T	readValue(KEY key, java.lang.String property, java.lang.Class<T> type) 
void	removeAll(ITEM... items) 
void	removeAllAsync(java.util.Collection<ITEM> items) 
void	removeByKey(KEY key) 
void	update(KEY key, java.lang.String property, byte value) 
void	update(KEY key, java.lang.String property, char value) 
void	update(KEY key, java.lang.String property, double value) 
void	update(KEY key, java.lang.String property, float value) 
void	update(KEY key, java.lang.String property, int value) 
void	update(KEY key, java.lang.String property, long value) 
void	update(KEY key, java.lang.String property, java.lang.Object value) 
void	update(KEY key, java.lang.String property, short value) 
void	update(KEY key, Update... values) 
void	updateByValue(KEY key, java.lang.String property, java.lang.String value)
void	addLookupIndex(java.lang.String name, LookupIndex<?,?> si) 
void	addSearchIndex(java.lang.String name, SearchIndex<?,?> si) 
java.util.List<ITEM>	all() 
int	count(KEY key, java.lang.String property, byte value) 
int	count(KEY key, java.lang.String property, char value) 
int	count(KEY key, java.lang.String property, double value) 
int	count(KEY key, java.lang.String property, float value) 
int	count(KEY key, java.lang.String property, int value) 
int	count(KEY key, java.lang.String property, long value) 
int	count(KEY key, java.lang.String property, java.lang.Object value) 
int	count(KEY key, java.lang.String property, short value) 
boolean	delete(ITEM item) 
ITEM	get(KEY key) 
KEY	getKey(ITEM item) 
void	invalidateIndex(java.lang.String property, ITEM item) 
<T> T	max(KEY key, java.lang.String property, java.lang.Class<T> type) 
double	maxDouble(KEY key, java.lang.String property) 
int	maxInt(KEY key, java.lang.String property) 
long	maxLong(KEY key, java.lang.String property) 
java.lang.Number	maxNumber(KEY key, java.lang.String property) 
java.lang.String	maxString(KEY key, java.lang.String property) 
<T> T	min(KEY key, java.lang.String property, java.lang.Class<T> type) 
double	minDouble(KEY key, java.lang.String property) 
int	minInt(KEY key, java.lang.String property) 
long	minLong(KEY key, java.lang.String property) 
java.lang.Number	minNumber(KEY key, java.lang.String property) 
java.lang.String	minString(KEY key, java.lang.String property) 
java.util.List<ITEM>	query(Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>>	query(java.util.List<Selector> selectors, Criteria... expressions) 
void	query(Visitor<KEY,ITEM> visitor, Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>>	queryAsMaps(Criteria... expressions) 
void	removeByKey(KEY key) 
ResultSet<ITEM>	results(Criteria... expressions) 
java.util.List<ITEM>	sortedQuery(Sort sortBy, Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>>	sortedQuery(Sort sortBy, java.util.List<Selector> selectors, Criteria... expressions) 
java.util.List<ITEM>	sortedQuery(java.lang.String sortBy, Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>>	sortedQuery(java.lang.String sortBy, java.util.List<Selector> selectors, Criteria... expressions) 
void	sortedQuery(Visitor<KEY,ITEM> visitor, Sort sortBy, Criteria... expressions) 
void	sortedQuery(Visitor<KEY,ITEM> visitor, java.lang.String sortBy, Criteria... expressions) 
void	validateIndex(java.lang.String property, ITEM item) 

Here is what a ResultSet produces from results:

package org.boon.datarepo;

public interface ResultSet<T> extends Iterable<T> {

    ResultSet expectOne();


    <EXPECT> ResultSet <EXPECT> expectOne(Class<EXPECT> clz);


    ResultSet expectMany();

    ResultSet expectNone();

    ResultSet expectOneOrMany();

    ResultSet removeDuplication();

    ResultSet sort(Sort sort);

    Collection<T> filter(Criteria criteria);

    ResultSet<List<Map<String, Object>>> select(Selector... selectors);

    int[] selectInts(Selector selector);

    float[] selectFloats(Selector selector);

    short[] selectShorts(Selector selector);

    double[] selectDoubles(Selector selector);

    byte[] selectBytes(Selector selector);

    char[] selectChars(Selector selector);

    Object[] selectObjects(Selector selector);

    <OBJ> OBJ[] selectObjects(Class<OBJ> cls, Selector selector);

    <OBJ> ResultSet<OBJ> selectObjectsAsResultSet(Class<OBJ> cls, Selector selector);


    Collection<T> asCollection();

    String asJSONString();

    List<Map<String, Object>> asListOfMaps();

    List<T> asList();

    Set<T> asSet();

    List<PlanStep> queryPlan();

    T firstItem();

    Map<String, Object> firstMap();

    String firstJSON();

    int firstInt(Selector selector);

    float firstFloat(Selector selector);

    short firstShort(Selector selector);

    double firstDouble(Selector selector);

    byte firstByte(Selector selector);

    char firstChar(Selector selector);

    Object firstObject(Selector selector);

    <OBJ> OBJ firstObject(Class<OBJ> cls, Selector selector);


    List<T> paginate(int start, int size);

    List<Map<String, Object>> paginateMaps(int start, int size);

    String paginateJSON(int start, int size);

    //Size can vary if you allow duplication.
    //The size can change after removeDuplication.
    int size();


}

Also ObjectEditor is a Bag and SearchableColleciton is a java.util.Collection so Repo is a Bag and a Collection too. The point is to say that you could write a book on what a Repo does. It does not do it all (in fact it is just an interface), but it does a lot with containment and delegation (compose-able objects) that form a Repo. A Repo is just a collection that allows queries through indexes (HashMap, TreeMap).

By default Boon works with the fields in the class, but you can instruct it to only use properties (getters and setters).

Let's see how Boon's data repo handles composite objects and property access as follows, but first let's define some more classes:

Email class*

package com.examples.model.test;

public class Email {


    private String content;

    public String getEmail () {
        return content;
    }

    public void setEmail ( String content ) {
        this.content = content;
    }

    public Email ( String content ) {
        this.content = content;
    }



    public Email (  ) {
    }

    @Override
    public boolean equals ( Object o ) {
        if ( this == o ) return true;
        if ( !( o instanceof Email ) ) return false;

        Email email = ( Email ) o;

        if ( content != null ? !content.equals ( email.content ) : email.content != null ) return false;

        return true;
    }

    @Override
    public int hashCode () {
        return content != null ? content.hashCode () : 0;
    }
}

The new User object does not have a simple email field but a complex email field (Email).

UserEmail class that uses email instead of a simple string

package com.examples.model.test;



public class UserEmail {

    private Email email;

    public UserEmail ( String email ) {

        this.email = new Email ( email ) ;

    }

    public Email getEmail () {
        return email;
    }
}

Let's setup a repo again, but this time we will only use properties not fields:

        boolean ok = true;

        RepoBuilder repoBuilder = Repos.builder ();

        repoBuilder.usePropertyForAccess ( true );


        putl ("The primary key is set to email");

        repoBuilder.primaryKey ( "email" );


        putl ("For ease of use you can setup nested properties ",
                "UserEmail.email property is a Email object not a string",
                "Email.email is a string.");

        //You can index component objects if you want
        repoBuilder.nestedIndex ( "email", "email" );

Read the above comments and putl calls, and puts method calls for more instruction.

Next we define our userRepo. Notice that I opted to use the Email.class, and UserEmail.class to show you exactly what we are passing. Email.class is the primary key class, and UserEmail.class is the item type.

        /** Create a repo of type String.class and User.class */
        final Repo<Email, UserEmail> userRepo = repoBuilder.build (
                                                Email.class, UserEmail.class );

Let's add some test data using the repo.add method.

        puts("Adding three test objects for bob, sam and joe ");
        userRepo.add ( new UserEmail ( "bob@bob.com" ) );
        userRepo.add ( new UserEmail ( "sam@bob.com" ) );
        userRepo.add ( new UserEmail ( "joe@bob.com" ) );

Now we can query userRepo and look for Bob's email:

        UserEmail bob = (UserEmail) userRepo.results (
                    eqNested ( "bob@bob.com", "email", "email" ) )
                .expectOne ().firstItem ();

Notice that we are using an eqNested operator. So in effect we are looking for the property path of user.email.email. Since the root object of userRepo are users (UserEmail to be precise) then we are looking for root.email.email. This is because UserEmail has a property of type Email called email, and Email has a property called email.

We can test we got the right email address:

        ok |= bob.getEmail ().getEmail ().equals ( "bob@bob.com" ) || die();

The above is boon speak for if Bob's email's property called email is not equal to "bob@bob.com", then die (which just means throw a runtime exception).

We can avoid the cast as follows (read putl and comments):

        putl("Avoid the cast with using nested query Repo.eqNested(UserEmail.class)");

        bob = userRepo.results ( eqNested ( "bob@bob.com", "email", "email" ) )
                .expectOne (UserEmail.class).firstItem ();


        ok |= bob.getEmail ().getEmail ().equals ( "bob@bob.com" ) || die();

You are not stuck with strings, you can query primitives and any complex object that implements an equals method and a hashCode method as follows:

        Email email = new Email ( "bob@bob.com" );
        bob = (UserEmail) userRepo.results ( eq ( EMAIL, email ) )
                .expectOne ().firstItem ();

        ok |= bob.getEmail ().getEmail ().equals ( "bob@bob.com" ) || die();

        puts("success=", ok);

Notice you can query with email (complex object) direct and remember that the email property is not a string but a class called Email that has an equals and a hashCode method.

That is all for part 1 as far as examples go (it goes on and on).

There is a lot more to dataRepo than meets the eye. It can do many things. This was just an introductory article to whet your appetite, and hopefully, you want to learn more.

Here are some of the classes in Boon's data repo package.

Interfaces

Bag                    //Like a java.util.List
CollectionDecorator    //Decorates built-in collecitons to add indexes and searches
Filter                 //The filter interface
LookupIndex            //LookupIndex implemented with HashMaps or equiv
ObjectEditor           //Abilty to edit fields and peform Select Queries against collecitons
Repo                   //Like a data repo but uses collections api to sync one or more indexes
RepoBuilder            //Allows you to build a Repo, setup indexes, etc.
ResultSet              //The results from a query.
SearchableCollection   //A collection that is searchable.

Classes

Collections   //converts regular collections to SearchableCollections with indexes to/for
Repos         //Helper class for creating RepBuilders

DataRepoException  //Exception class for DataRepo

There is also a Criteria API that works with the DataRepo queries. DataRepo queries start by using the index, and then when an item is not in the index or if the index query did not fully satisfy the criteria, DataRepo can then use the criteria API to perform the rest in a linear search:

Criteria                 //Criteria
CriteriaFactory          //Criteria Factory, we say this earlier, eq, notEq, etc.
Criterion                //Concrete classes
Criterion.PrimitiveCriterion //Primitive criteria to avoid wrappers and lots of temp objects
Group                   //Group of Criteria, Group is also a Criteria so it is nested
Group.And               //And criteria together
Group.Or                //Or Criteria
Not                     //Inverse the logic
ProjectedSelector       //Perform projections over a Criteria
QueryFactory            //Create queries
Selector                //Selectors like SQL select
Sort                    //Define how you want the data Sorted
Update                  //Ability to select and update objects based on criteria

Enums

Grouping
Operator
SortType

To learn more see the JavaDocs.

http://richardhightower.github.io/site/javadocs/index.html

Let me know what you think, and if you have issues, please file a bug.

If you want to get a sneak peek at what is coming in Boon see https://github.com/RichardHightower/datarepo.

There is a lot more to Boon's data repo then we can cover in one article.

Thoughts

Thoughts? Write me at richard high tower AT g mail dot c-o-m (Rick Hightower).

Further Reading:

If you are new to boon start here:

Why Boon?

Easily read in files into lines or a giant string with one method call. Works with files, URLs, class-path, etc. Boon IO support will surprise you how easy it is. Boon has Slice notation for dealing with Strings, Lists, primitive arrays, Tree Maps, etc. If you are from Groovy land, Ruby land, Python land, or whatever land, and you have to use Java then Boon might give you some relief from API bloat. If you are like me, and you like to use Java, then Boon is for you too. Boon lets Java be Java, but adds the missing productive APIs from Python, Ruby, and Groovy. Boon may not be Ruby or Groovy, but its a real Boon to Java development.

Core Boon Philosophy

Core Boon will never have any dependencies. It will always be able to run as a single jar. This is not just NIH, but it is partly. My view of what Java needs is more inline with what Python, Ruby and Groovy provide. Boon is an addition on top of the JVM to make up the difference between the harder to use APIs that come with Java and the types of utilities that are built into Ruby, Python, PHP, Groovy etc. Boon is a Java centric view of those libs. The vision of Boon and the current implementation is really far apart.

===

Contact Info

blog|[twitter](https://twitter.com/RickHigh|[infoq]http://www.infoq.com/author/Rick-Hightower|[stackoverflow](http://stackoverflow.com/users/2876739/rickhigh)|[java lobby](http://java.dzone.com/users/rhightower)|Other | richard high tower AT g mail dot c-o-m (Rick Hightower)|work|cloud|nosql

Clone this wiki locally