Elasticsearch maintenant supporté par Spring Data

L’un des nouveaux modules intégré dans la nouvelle version de Spring Data est spring-data-elasticsearch. Il permet, comme son nom l’indique, de requêter un serveur/cluster Elasticsearch. Franck de Agaetis est un contributeur du projet.
Dans la liste des fonctionalités apportées par ce projet communautaire de Spring Data, il existe celle de faire des requêtes géolocalisées. Pour être plus clair, il est possible de retrouver, par exemple, toutes les personnes dont le nom contient « blaise » et qui habitent à Clermont-ferrand.

Plusieurs saveurs de requête : « Criteria » ou « Repository »

Il existe plusieurs manières de faire une requête dans le monde de Spring Data :

  • Utilisation des Criteria, où l’on construit la requête gâce à une API « fluent ».
  • Un repository où le nom des méthodes et leurs paramètres permettent de construire la requête.

Spring Data est très selectif : il faut respecter ses « Criteria »

Voici un exemple qui permet d’illustrer la manière de construire des requêtes Spring Data Elasticsearch avec les « Criteria » :

Dans le cas de cet exemple, les données sont des noms de personnes associés à un identifiant et des coordonnées géographiques.
Voici comment on indexe ces données :

...
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();

// Blaise PASCAL in Clermont-Ferrand
indexQueries.add(new AuthorMarkerEntityBuilder("1")
   .name("Blaise PASCAL")
   .location(45.7806d, 3.0875d)
   .buildIndex());

// and Isaac NEWTON in London
indexQueries.add(new AuthorMarkerEntityBuilder("2")
   .name("Isaac NEWTON")
   .location(51.5171d, 0.1062d)
   .buildIndex());

esTemplate.bulkIndex(indexQueries);
...

Voici la classe AuthorMarkerEntity qui permet de transformer et de récupérer les résultats de requête (marshal/unmarshal).

@Document(indexName = "test-geo-index", type = "geo-class-point-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class AuthorMarkerEntity {

	@Id
	private String id;
	private String name;

	private GeoPoint location;

	private AuthorMarkerEntity() {
	}

	public AuthorMarkerEntity(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public GeoPoint getLocation() {
		return location;
	}

	public void setLocation(GeoPoint location) {
		this.location = location;
	}
}

Ensuite, un exemple de requête pour trouver toutes les personnes qui vivent à moins de 20 kilomètres de Clermont-ferrand :

CriteriaQuery criteriaQuery = new CriteriaQuery(
                new Criteria("location")
                    .within(new GeoPoint(45.7806d, 3.0875d), "20km"));

List<AuthorMarkerEntity> result = esTemplate.queryForList(criteriaQuery, AuthorMarkerEntity.class);

Cela devrait renvoyer uniquement « Blaise PASCAL ».

Il est aussi possible de combiner les critères de recherche. Par exemple : la requête ci-dessous retrouve toutes les personnes dont le nom contient « Isaac » ET qui habitent à moins de 20 kilomètres de Londres.

CriteriaQuery criteriaQuery = new CriteriaQuery(
                new Criteria("name")
                    .is("Isaac")
                    .and("location")
                    .within(new GeoPoint(51.5171d, 0.1062d), "20km")); 

List<AuthorMarkerEntity> result = esTemplate.queryForList(criteriaQuery, AuthorMarkerEntity.class);

 

Des noms de méthode qui définissent une requête : pas bête la bête !

Spring Data peut utiliser le nom d’une méthode et la valeur des paramètres fournis pour contruire une requête.

Pour commencer voici un extrait de la classe SampleEntity :

@Document(indexName = "test-index", type = "test-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class SampleEntity {
...
  @Id
  private String id;
...
  private GeoPoint location;

  public GeoPoint getLocation() {
    return location;
  }

  public void setLocation(GeoPoint location) {
    this.location = location;
  }
...
}

Voici un extrait du « repository » qui pourrait être utilisé pour requêter des « SampleEntity ».


public interface SampleCustomMethodRepository extends ElasticsearchRepository<SampleEntity, String> {

Page<SampleEntity> findByLocationWithin(GeoPoint point, String distance, Pageable pageable);

}

Ainsi quand la méthode « findByLocationWithin » sera appelée, une requête pour retrouver tous les « SampleEntity » autour des coordonnées passées en argument, sera contruite et executée.

Voici deux écritures équivalentes :

CriteriaQuery criteriaQuery = new CriteriaQuery(
                new Criteria("location")
                    .within(new GeoPoint(45.7806d, 3.0875d), "20km"));

List<AuthorMarkerEntity> result = esTemplate.queryForList(criteriaQuery, AuthorMarkerEntity.class);

Page<SampleEntity> page = repository.findByLocationWithin(new GeoPoint(45.7806d, 3.0875d), "20km", new PageRequest(0, 10));


Pour plus d’informations sur le projet : https://github.com/spring-projects/spring-data-elasticsearch

Tagged on: