Comparing Search Performance of CQEngine with Standard Java Collections

Comparing Search Performance of CQEngine with Standard Java Collections

In a previous post, we looked at how to use CQEngine to query a java collection. We performed three rather simplistic queries, an equals search, a ends with search and a combination of the two.  In this post, we will see exactly how fast  CQEngine is compared to iterating over a collection and also how the search time grows as you increase the size of your collection.

1. Equals search

The code for an equals search on an indexed collection using cqengine is given below. A Hash index was used here. For complete code, visit the original post.


public void indexedSearchForEquals(String search) throws Exception {
    Query<User> query = equal(User.FULL_NAME, search);
    for (User user : indexedUsers.retrieve(query)) {
	// System.out.println(user.getFullname());
    }
}

Equivalent code to perform the same search iteratively:


public void searchIterativelyForEquals(String search) throws Exception {
     for (User user : users) {
       if (user.getFullname().equals(search)) {
          // System.out.println(user.getFullname());
       }
     }
}

cqengine stats

You will note that iterative search grows linearly whereas indexed cqengine search remains almost constant. For a million items, the difference in performance is significant. One thing to note is that these are search times only and do not include the time to build the index itself, which for a million objects could be substantial.

2. Ends With search

The code for performing an indexed cqengine based search to find an object whose attribute ends with a certain suffix is given below. A Suffix Tree Index is used for this search. Again, you can find the complete post here.


public void indexedSearchForEndsWith(String suffix) throws Exception {
	Query query = endsWith(User.FULL_NAME, suffix);
	for (User user : indexedUsers.retrieve(query)) {
		// System.out.println(user.getFullname());
	}
}

Equivalent code to perform the same search iteratively:

public void searchIterativelyForEndsWith(String suffix) throws Exception {
	for (User user : users) {
		if (user.getFullname().endsWith(suffix)) {
			// System.out.println(user.getFullname());
		}
	}
}

cqengine stats

For 10,000 items to 100,000 items, you will again note that the pattern is the same. Iterative search grows linearly whereas indexed cqengine search remains almost constant.

3. Ends With or Equals search

In this example, we are trying to find items in a collection whose attribute either ends with a certain string or matches equally with another string. First, the indexed search:


public void indexedSearchForEqualOrEndsWith(String equals, String endsWith) throws Exception {
	Query query = or(equal(User.FULL_NAME, equals),endsWith(User.FULL_NAME, endsWith));
		for (User user : indexedUsers.retrieve(query)) {
			// System.out.println(user.getFullname());
		}
}

Now the iterative code:


public void searchIterativelyForEqualsOrEndsWith(String equals, String endsWith) throws Exception {
      for (User user : users) {
	  if (user.getFullname().equals(equals)|| user.getFullname().endsWith(endsWith)) {
		// System.out.println(user.getFullname());
	  }
      }
}

cqengine stats

For 10,000 items to 100,000 items, the pattern continues to be the same. Both Iterative and indexed results take slightly longer this time around but grow at the same rate as before.

CQEngine is a great library that allows you to query large collections for desired results. It can improve performance of your application significantly by reducing the time required by iterations, avoiding db hits for querying data from the db especially if that data can be indexed in memory and does not change rapidly and in many other scenarios. An interesting idea would be to use the Google Guava Cache and CQEngine together to build a queryable cache of objects for your application. That’s for another post.