View on GitHub

Async-elastic-orm

Asynchronous Java POJO to Elastic Search mapper

Download this project as a .zip file Download this project as a tar.gz file

About

async-elastic-orm is an asynchronous POJO-to-ElasticSearch library for Java. It allows for quick and easy saving, loading and searching of any regular java POJO with support for complex object graphs and polymorphism.

Installing

You can either add the jar to your classpath along with the ElasticSearch Java library ( http://www.elasticsearch.org/download/ ) or you can use maven and add the dependency to your pom:

<dependency>
    <groupId>io.github.ryan-za</groupId>
    <artifactId>async-elastic-orm</artifactId>
    <version>0.2.3-SNAPSHOT</version>
</dependency>

Basic Usage

POJO definition

public class POJO {
    @ID String id;
    String name;
}

Saving a POJO

POJO pojo = new POJO();
new GDS().save(pojo).result().now();

Loading a POJO

POJO pojo = new GDS().load().fetch(POJO.class, id).now();

Searching or loading multiple

List<POJO> list = new GDS().query(POJO.class).asList();

Asynchronous Usage

async-elastic-orm allows for non-blocking operations which are preferable when creating a high performance server (such as with http://vertx.io ). All operations return a GDSResult or GDSMultiResult with the operations now() or later(). Use later() for non-blocking. The async callbacks are optimized to be used with Java8 lambdas when they become a standard language feature by only having a single method. Error results are returned as a parameter you must check rather than a distinct callback as this seems to make for neater code.

Saving a POJO

POJO pojo = new POJO();
new GDS().save(pojo).result().later(new GDSCallback<Key>() {

    @Override
    public void onSuccess(Key t, Throwable err) {
        // Check err for errors, the key contains the id
    }
});

Loading a POJO

new GDS().load().fetch(POJO.class, id).later(new GDSCallback<POJO>() {

    @Override
    public void onSuccess(POJO pojo, Throwable err) {
        // Check err for errors 
    }
});

Searching or loading multiple (full list callback)

List<POJO> list = new GDS().query(POJO.class).result()
    .laterAsList(new GDSResultListReceiver<POJO>() {

    @Override
    public void success(List<POJO> list, Throwable err) {
        // Check err for errors
    }
});

Searching or loading multiple (feeder callback)

List<POJO> list = new GDS().query(POJO.class).result()
    .later(new GDSResultReceiver<POJO>() {

    @Override
    public boolean receiveNext(POJO pojo) {
        return true; // return false to stop receiving more
    }

    @Override
    public void onError(Throwable err) {
        // Specific error callback as receiveNext()+finished()
        // block this being used as a lambda
    }

    @Override
    public void finished() {
        // Called when all results have been pushed to
        // receiveNext()
    }
});

Batching async operations

There is a GDSBatcher available for batching together multiple async operations so that you do not need a specific handler for each one. This is useful for only running operations when all saves or loads have completed.

TestParent testParent1 = new TestParent();
GDSResult<Key> result1 = getGDS().save().entity(testParent1).result();
TestParent testParent2 = new TestParent();
GDSResult<Key> result2 = getGDS().save().entity(testParent2).result();
TestParent testParent3 = new TestParent();
GDSResult<Key> result3 = getGDS().save().entity(testParent3).result();
TestParent testParent4 = new TestParent();
GDSResult<Key> result4 = getGDS().save().entity(testParent4).result();
TestParent testParent5 = new TestParent();
GDSResult<Key> result5 = getGDS().save().entity(testParent5).result();

GDSResult<Boolean> allResult = 
    new GDSBatcher(result1, result2, result3, result4, result5)
    .onAllComplete();
boolean success = allResult.now();

Object versioning

Elasticsearch provides for object versioning which can be used in places where consistency is important. async-elastic-orm provides for easy access to this functionality by declaring a version parameter in your pojo. When doing an update of a pojo with a version field, the operation will only succeed if the version matches with the pojo being updated to ensure no data is overwritten.

public class TestVersionedObject {
    @ID
    String id;
    @Version
    long ver;

    String name;
}
TestVersionedObject testVersionedObject = new TestVersionedObject();
testVersionedObject.name = "one";

// This will succeed
Key key1 = getGDS().save(testVersionedObject).now();

testVersionedObject.name = "two";
// This will also succeed as the pojo's version 
// has been updated on save
Key key2 = getGDS().save(testVersionedObject).now();

// simulate an old copy of the object       
testVersionedObject.ver = 1;
// This will fail as the version is older
getGDS().save(testVersionedObject).now();

More examples

More examples of usage are available in the tests. Please help by submitting more tests.

Performance

While this library uses reflection, all of the reflection classes are only fetched a single time and then cached for performance. With Java7+, cached reflection calls are not significantly slower than regular method calls. There are performance tests available in the tests. Performance for the Async operation mode appears to be very good and there is not much overhead compared to regular elasicsearch api commands.