nadouani / playframework2-elasticsearch

PlayFramework module for Elasticsearch

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

This module integrates Elasticsearch into your PlayFramework app.

Versions

Module Playframework Elasticsearch Comments
0.1 2.4.3 2.0 Initial version

Install

Add the following resolver to your build.sbt

resolvers += "eduardofcbg" at "http://dl.bintray.com/eduardofcbg/maven"

And the following dependency declaration:

  "com.github.eduardofcbg" %% "playframework2-elasticsearch" % "0.1"

Your build.sbt should look something like this:

name := """myproject"""

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayJava)

scalaVersion := "2.11.1"

libraryDependencies ++= Seq(
  cache,
  javaWs,
  "com.github.eduardofcbg" %% "playframework2-elasticsearch" % "0.1"
)

resolvers += "eduardofcbg" at "http://dl.bintray.com/eduardofcbg/maven"

Configure the plugin

You should enable and configure the plugin in conf/application.conf

## ElasticSearch

## Enable the plugin
play.modules.enabled += "com.github.eduardofcbg.plugin.es.ESModule"

## Enable embedded database for developing
es.embed=true
## Where to save embedded database (change this)
es.local.path="/home/eduardo/db"

## For connecting to a remote database
## List of hosts
#es.hosts=["127.0.0.1:9300"]

## Name of the index (defaults to "play-es")
#es.index="play-es"

## Adicional options
#es.log=true
#es.sniff=true
#es.timeout=5
#es.ping=5
#es.cluster="mycluster"
#es.mappings="{ analysis: { analyzer: { my_analyzer: { type: \"custom\", tokenizer: \"standard\" } } } }"

Usage from Java

Make your models extend the com.github.eduardofcbg.plugin.es.Index class and annotate it with the name of the type that will be associated with the indexed objects of this class. On each model add a find helper. This is the object that you will use query your ES cluster.

package models;

@Type.Name("person")
@Type.ResultsPerPage(5)
public class Person extends Index {

	public String name;
	public int age;
	
	public static final Finder<Person> find = finder(Person.class);
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public Person() {}
	
	public F.Promise<IndexResponse> index() {
		return find.index(this);
	}
	
	public static F.Promise<Optional<Person>> get(String id) {
		return find.get(id);
	}
					
	public static F.Promise<List<Person>> getAdults(int page) {
		return find.search(s -> s.setQuery(rangeQuery("age").from(18)), page);
	}
	
}

As you can see in the last method, you can easily construct any search query by passing a method that will change your SearchRequestBuilder object via side effects. This way you can use the ES java API directly in your models.

All the finder's methods are asynchronous. They return Promises which can be easily turned into actual responses and even better, they can be turned into Promises<Result>, being the Result the type returned by play's actions. This means you can easily construct asynchronous controllers like the one bellow.

public class PersonController extends Controller {

    @Inject
    public PersonController(ES es) {
        Person.registerAsType(es);
    }

    public static F.Promise<Result> getAdults(int page) {
        return Person.getAdults(page).map(persons -> {
            return ok(listOfPerson.render(persons));
        });
    }
    
    public F.Promise<Result> getPerson(String id) {
        return Person.get(id).map(p -> {
            if (p.isPresent()) return ok(person.render(p.get()));
            else return notFound("not found person with id " + id);
        });
    }

}

Additionally, every controller must register the type associated with it as seen above.

Of course you can also get the ES transport client by calling:

Finder.getClient();

You also can get the index name and the type any model, just by accessing it's find helper object.

Person.find.getType();
Person.find.getIndex();

There are also some methods that will help you parse a SearchResponse and a GetResponse:

Person person = find.parse(response);
List<Person> persons = find.parse(response);

##Dealing with concurrency problems

ElasticSearch uses the Optimistic concurrency control method. This plugin allows one to update a document without having to deal with the case when there are such problems.

public static F.Promise<UpdateResponse> incrementAge(String id) {
        return find.update(id, original -> {
            original.age++;
            return original;
        });
}

An update can be done by specifying the document's Id and a Function that will be applied to the Object associated with it. This means that the Finder is aware of what transformation you are applying to the original object and can simply redo it if a problem related with concurrency occurs.

##Document relationships and mapping

You are able to set mappings in your index using the application.conf, but mappings that affect specific types should be specified in your actual models just by passing them when you create the Find helper.

public static final Finder<Person> finder = finder(Person.class, m -> {
    try {
        m.startObject("_timestamp")
                .field("enabled", true)
                .field("path", "timestamp")
        .endObject();
    } catch (IOException e) {}
});

Additionally, if you want to set a parent-child relationship or a nested type, it is as easy as setting an annotation:

package models;

@Type.Name("person")
@Type.ResultsPerPage(5)
public class Person extends Index {

	public String name;
	public int age;
	@Type.NestedField
	public List<Book> books;
	
	public static final Finder<Person> find = finder(Person.class);
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public Person() {}
	
	public static F.Promise<List<Pet>> getPets(String personId, int page) {
	    return Pet.find.getAsChildrenOf(Person.class, personId, page);
	}
		
}
package models;

@Type.Name("pet")
@Type.Parent("person")
@Type.ResultsPerPage(5)
public class Pet extends Index {

	public String name;	
	
	public static final Finder<Pet> find = finder(Pet.class);
	
	public Pet(String name) {
		this.name = name;
	}
	
	public Pet() {}
	
	public F.Promise<IndexResponse> index(String personId) {
		return find.indexChild(this, personId);
	}
		
}

A sample application based on this guide can be found here

About

PlayFramework module for Elasticsearch

License:GNU General Public License v2.0


Languages

Language:Java 80.4%Language:Scala 19.6%