Hibernate Search - Mapping geo-point
Hibernate Search provides a variety of spatial features such as a distance predicate and a distance sort. These features require that spatial coordinates are indexed. More precisely, it requires that a geo-point, i.e. a latitude and longitude in the geographic coordinate system, are indexed.
Geo-points are a bit of an exception, because there isn’t any type in the standard Java library to represent them. For that reason, Hibernate Search defines its own interface, org.hibernate.search.engine.spatial.GeoPoint. Since your model probably uses a different type to represent geo-points, mapping geo-points requires some extra steps.
Two options are available:
If your geo-points are represented by a dedicated, immutable type, simply use @GenericField and the GeoPoint interface, as explained here.
For every other case, use the more complex (but more powerful) @GeoPointBinding, as explained here.
public class MyCoordinates implements GeoPoint {
@Basic
private Double latitude;
@Basic
private Double longitude;
protected MyCoordinates() {
// For Hibernate ORM
}
public MyCoordinates(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
@Override
public double getLatitude() {
return latitude;
}
@Override
public double getLongitude() {
return longitude;
}
}
@Entity
@Indexed
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Embedded
@GenericField
private MyCoordinates placeOfBirth;
public Author() {
}
// Getters and setters
// ...
}
Model the geo-point as an embeddable implementing GeoPoint. A custom type with a corresponding Hibernate ORM UserType would work as well.
The geo-point type must be immutable: it does not declare any setter.
Apply the @GenericField annotation to the placeOfBirth property holding the coordinates. An index field named placeOfBirth will be added to the index. Options generally used on @GenericField can be used here as well.
The geo-point type must be immutable, i.e. the latitude and longitude of a given instance may never change.
This is a core assumption of @GenericField and generally all @*Field annotations: changes to the coordinates will be ignored and will not trigger reindexing as one would expect.
If the type holding your coordinates is mutable, do not use @GenericField and refer to Using @GeoPointBinding, @Latitude and @Longitude instead.
If your geo-point type is immutable, but extending the GeoPoint interface is not an option, you can also use a custom value bridge converting between the custom geo-point type and GeoPoint. GeoPoint offers static methods to quickly build a GeoPoint instance.
@Indexed
@GeoPointBinding(fieldName = "placeOfBirth")
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Latitude
private Double placeOfBirthLatitude;
@Longitude
private Double placeOfBirthLongitude;
public Author() {
}
// Getters and setters
// ...
}
Apply the @GeoPointBinding annotation to the type, setting fieldName to the name of the index field.
Apply @Latitude to the property holding the latitude. It must be of double or Double type.
Apply @Longitude to the property holding the longitude. It must be of double or Double type.
The @GeoPointBinding annotation may also be applied to a property, in which case the @Latitude and @Longitude must be applied to properties of the property’s type:
public class MyCoordinates {
@Basic
@Latitude
private Double latitude;
@Basic
@Longitude
private Double longitude;
protected MyCoordinates() {
// For Hibernate ORM
}
public MyCoordinates(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
}
@Entity
@Indexed
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Embedded
@GeoPointBinding
private MyCoordinates placeOfBirth;
public Author() {
}
// Getters and setters
// ...
}
Model the geo-point as an embeddable. An entity would work as well.
In the geo-point type, apply @Latitude to the property holding the latitude.
In the geo-point type, apply @Longitude to the property holding the longitude.
The geo-point type may safely declare setters (it can be mutable).
Apply the @GeoPointBinding annotation to the property. Setting fieldName to the name of the index field is possible, but optional: the property name will be used by default.
It is possible to handle multiple sets of coordinates by applying the annotations multiple times and setting the markerSet attribute to a unique value:
@Indexed
@GeoPointBinding(fieldName = "placeOfBirth", markerSet = "birth")
@GeoPointBinding(fieldName = "placeOfDeath", markerSet = "death")
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Latitude(markerSet = "birth")
private Double placeOfBirthLatitude;
@Longitude(markerSet = "birth")
private Double placeOfBirthLongitude;
@Latitude(markerSet = "death")
private Double placeOfDeathLatitude;
@Longitude(markerSet = "death")
private Double placeOfDeathLongitude;
public Author() {
}
// Getters and setters
// ...
}
Apply the @GeoPointBinding annotation to the type, setting fieldName to the name of the index field, and markerSet to a unique value.
Apply the @GeoPointBinding annotation to the type a second time, setting fieldName to the name of the index field (different from the first one), and markerSet to a unique value (different from the first one).
Apply @Latitude to the property holding the latitude for the first geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Apply @Longitude to the property holding the longitude for the first geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Apply @Latitude to the property holding the latitude for the second geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Apply @Longitude to the property holding the longitude for the second geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Geo-points are a bit of an exception, because there isn’t any type in the standard Java library to represent them. For that reason, Hibernate Search defines its own interface, org.hibernate.search.engine.spatial.GeoPoint. Since your model probably uses a different type to represent geo-points, mapping geo-points requires some extra steps.
Two options are available:
If your geo-points are represented by a dedicated, immutable type, simply use @GenericField and the GeoPoint interface, as explained here.
For every other case, use the more complex (but more powerful) @GeoPointBinding, as explained here.
Using @GenericField and the GeoPoint interface
When geo-points are represented in your entity model by a dedicated, immutable type, you can simply make that type implement the GeoPoint interface, and use simple property/field mapping with @GenericField:Mapping spatial coordinates by implementing GeoPoint
@Embeddablepublic class MyCoordinates implements GeoPoint {
@Basic
private Double latitude;
@Basic
private Double longitude;
protected MyCoordinates() {
// For Hibernate ORM
}
public MyCoordinates(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
@Override
public double getLatitude() {
return latitude;
}
@Override
public double getLongitude() {
return longitude;
}
}
@Entity
@Indexed
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Embedded
@GenericField
private MyCoordinates placeOfBirth;
public Author() {
}
// Getters and setters
// ...
}
Model the geo-point as an embeddable implementing GeoPoint. A custom type with a corresponding Hibernate ORM UserType would work as well.
The geo-point type must be immutable: it does not declare any setter.
Apply the @GenericField annotation to the placeOfBirth property holding the coordinates. An index field named placeOfBirth will be added to the index. Options generally used on @GenericField can be used here as well.
The geo-point type must be immutable, i.e. the latitude and longitude of a given instance may never change.
This is a core assumption of @GenericField and generally all @*Field annotations: changes to the coordinates will be ignored and will not trigger reindexing as one would expect.
If the type holding your coordinates is mutable, do not use @GenericField and refer to Using @GeoPointBinding, @Latitude and @Longitude instead.
If your geo-point type is immutable, but extending the GeoPoint interface is not an option, you can also use a custom value bridge converting between the custom geo-point type and GeoPoint. GeoPoint offers static methods to quickly build a GeoPoint instance.
Using @GeoPointBinding, @Latitude and @Longitude
For cases where coordinates are stored in a mutable object, the solution is the @GeoPointBinding annotation. Combined with the @Latitude and @Longitude annotation, it can map the coordinates of any type that declares a latitude and longitude of type double:Mapping spatial coordinates using @GeoPointBinding
@Entity@Indexed
@GeoPointBinding(fieldName = "placeOfBirth")
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Latitude
private Double placeOfBirthLatitude;
@Longitude
private Double placeOfBirthLongitude;
public Author() {
}
// Getters and setters
// ...
}
Apply the @GeoPointBinding annotation to the type, setting fieldName to the name of the index field.
Apply @Latitude to the property holding the latitude. It must be of double or Double type.
Apply @Longitude to the property holding the longitude. It must be of double or Double type.
The @GeoPointBinding annotation may also be applied to a property, in which case the @Latitude and @Longitude must be applied to properties of the property’s type:
Mapping spatial coordinates using @GeoPointBinding on a property
@Embeddablepublic class MyCoordinates {
@Basic
@Latitude
private Double latitude;
@Basic
@Longitude
private Double longitude;
protected MyCoordinates() {
// For Hibernate ORM
}
public MyCoordinates(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
}
@Entity
@Indexed
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Embedded
@GeoPointBinding
private MyCoordinates placeOfBirth;
public Author() {
}
// Getters and setters
// ...
}
Model the geo-point as an embeddable. An entity would work as well.
In the geo-point type, apply @Latitude to the property holding the latitude.
In the geo-point type, apply @Longitude to the property holding the longitude.
The geo-point type may safely declare setters (it can be mutable).
Apply the @GeoPointBinding annotation to the property. Setting fieldName to the name of the index field is possible, but optional: the property name will be used by default.
It is possible to handle multiple sets of coordinates by applying the annotations multiple times and setting the markerSet attribute to a unique value:
Mapping spatial coordinates using @GeoPointBinding on a property
@Entity@Indexed
@GeoPointBinding(fieldName = "placeOfBirth", markerSet = "birth")
@GeoPointBinding(fieldName = "placeOfDeath", markerSet = "death")
public class Author {
@Id
@GeneratedValue
private Integer id;
@FullTextField(analyzer = "name")
private String name;
@Latitude(markerSet = "birth")
private Double placeOfBirthLatitude;
@Longitude(markerSet = "birth")
private Double placeOfBirthLongitude;
@Latitude(markerSet = "death")
private Double placeOfDeathLatitude;
@Longitude(markerSet = "death")
private Double placeOfDeathLongitude;
public Author() {
}
// Getters and setters
// ...
}
Apply the @GeoPointBinding annotation to the type, setting fieldName to the name of the index field, and markerSet to a unique value.
Apply the @GeoPointBinding annotation to the type a second time, setting fieldName to the name of the index field (different from the first one), and markerSet to a unique value (different from the first one).
Apply @Latitude to the property holding the latitude for the first geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Apply @Longitude to the property holding the longitude for the first geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Apply @Latitude to the property holding the latitude for the second geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Apply @Longitude to the property holding the longitude for the second geo-point field. Set the markerSet attribute to the same value as the corresponding @GeoPointBinding annotation.
Comments
Post a Comment