Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

HSEARCH-1168 #375

Merged
merged 1 commit into from

2 participants

@Sanne
Owner

"just" documentation.. still it took me several hours.

https://hibernate.onjira.com/browse/HSEARCH-1168

...umentation/src/main/docbook/en-US/modules/spatial.xml
@@ -108,44 +121,15 @@
</listitem>
<listitem>
- <para>Poor performance on worldwide data set (For example when
- indexed points of interest in the United States, in Europe and in
- Asia collide because they share the same latitude, the latitude
- range query returns large amounts of data that need to be crossed
- check with those returned by the longitude range).</para>
+ <para>Poor performance if your data set is distributed across the whole
+ world (For example when indexing points of interest in the United States,
+ in Europe and in Asia, large areas collide because they share the same latitude.
+ The latitude range query returns large amounts of data that need to be cross
+ checked with those returned by the longitude range).</para>
</listitem>
@DavideD Collaborator
DavideD added a note

Why the uppercase? "(for example"

@Sanne Owner
Sanne added a note

thanks, fixing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@DavideD DavideD commented on the diff
...umentation/src/main/docbook/en-US/modules/spatial.xml
((49 lines not shown))
- <listitem>
- <para>Provide simple methods for querying</para>
- </listitem>
@DavideD Collaborator
DavideD added a note

This is just me, but I would write:"Provide a simple way " instead of "Provide a simple method "

@Sanne Owner
Sanne added a note

updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Sanne Sanne merged commit 2bf24b5 into hibernate:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 15, 2013
  1. @Sanne
This page is out of date. Refresh to see the latest.
Showing with 290 additions and 183 deletions.
  1. +290 −183 hibernate-search-documentation/src/main/docbook/en-US/modules/spatial.xml
View
473 hibernate-search-documentation/src/main/docbook/en-US/modules/spatial.xml
@@ -30,46 +30,48 @@
<chapter id="spatial">
<title>Spatial</title>
- <para>The spatial features of Hibernate Search enable indexed entities with
- latitude and longitude fields to be spatially indexed then searched and
- filtered by defining a search discus, providing a center and a
- radius.</para>
+ <para>With the Spatial extensions you can combine fulltext queries
+ with restrictions based on distance from a point in space, filter
+ results based on distances from coordinates or sort results
+ on such a distance criteria.</para>
- <para>Typical usage is to find nearby locations of interest (between 500m
- and 5 km) in a 5 millions worldwide data set (for instance hotels, pubs,
- airports).</para>
+ <para>The spatial support of Hibernate Search has a few goals:</para>
- <section id="spatial-goals">
- <title>Goals</title>
+ <itemizedlist>
+ <listitem>
+ <para>Enable spatial search on entities: find entities within x km
+ from a location (latitude, longitude) on Earth</para>
+ </listitem>
- <para>The spatial support of Hibernate Search has a few goals:</para>
+ <listitem>
+ <para>Provide an easy way to enable spatial indexing via expressive
+ annotations</para>
+ </listitem>
- <itemizedlist>
- <listitem>
- <para>Enable spatial search on entities, find entities within x km
- from location (latitude, longitude) on earth</para>
- </listitem>
+ <listitem>
+ <para>Provide a simple way for querying</para>
+ </listitem>
- <listitem>
- <para>Provide simple method to implement a spatial index via
- annotations</para>
- </listitem>
+ <listitem>
+ <para>Hide geographical complexity</para>
+ </listitem>
+ </itemizedlist>
- <listitem>
- <para>Provide simple methods for querying</para>
- </listitem>
@DavideD Collaborator
DavideD added a note

This is just me, but I would write:"Provide a simple way " instead of "Provide a simple method "

@Sanne Owner
Sanne added a note

updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ <para>For example, you might search for that Italian place named
+ approximately "Il Ciociaro" and is somewhere in the 2 km area around
+ your office.</para>
- <listitem>
- <para>Hide geographical complexity</para>
- </listitem>
- </itemizedlist>
- </section>
+ <para>To be able to filter an <classname>@Indexed</classname> <classname>@Entity</classname>
+ on a distance criteria you need to add the <classname>@Spatial</classname>
+ annotation (<classname>org.hibernate.search.annotations.Spatial</classname>)
+ and specify one or more sets of coordinates.</para>
- <section id="spatial-strategy">
- <title>Choosing an indexing strategy</title>
+ <section id="spatial-indexing">
+ <title>Enable indexing of Spatial Coordinates</title>
- <para>There are two strategies you can choose in HS spatial to index the
- latitude and longitude properties of your entities:</para>
+ <para>There are different techniques to index point coordinates,
+ in particular Hibernate Search Spatial offers a choice between
+ two strategies:</para>
<itemizedlist>
<listitem>
@@ -81,22 +83,33 @@
</listitem>
</itemizedlist>
- <section id="spatial-strategy-range">
- <title>Double range queries</title>
+ <para>We will now describe both methods so you can make a suitable choice; of
+ course you can pick different strategies for each set of coordinates.
+ These strategies are selected by specifying <literal>spatialMode</literal>,
+ an attribute of the <classname>@Spatial</classname> annotation.</para>
+
+ <section id="spatial-indexing-range">
+ <title>Indexing coordinates for Double Range Queries</title>
+
+ <para>When setting the <classname>@Spatial</classname>.<methodname>spatialMode</methodname>
+ attribute to <classname>SpatialMode</classname>.<methodname>RANGE</methodname>
+ (which is the default) coordinates are indexed as numeric fields,
+ so that range queries can be performed to narrow down the initial area
+ of interest.</para>
<para>Pros:</para>
<itemizedlist>
<listitem>
- <para>Quick on small data sets (&lt; 100k entities)</para>
+ <para>Is quick on small data sets (&lt; 100k entities)</para>
</listitem>
<listitem>
- <para>Straight forward to debug/analyze</para>
+ <para>Is very simple: straightforward to debug/analyze</para>
</listitem>
<listitem>
- <para>Index size is moderate</para>
+ <para>Impact on index size is moderate</para>
</listitem>
</itemizedlist>
@@ -108,17 +121,54 @@
</listitem>
<listitem>
- <para>Poor performance on worldwide data set (For example when
- indexed points of interest in the United States, in Europe and in
- Asia collide because they share the same latitude, the latitude
- range query returns large amounts of data that need to be crossed
- check with those returned by the longitude range).</para>
+ <para>Poor performance if your data set is distributed across the whole
+ world (for example when indexing points of interest in the United States,
+ in Europe and in Asia, large areas collide because they share the same latitude.
+ The latitude range query returns large amounts of data that need to be cross
+ checked with those returned by the longitude range).</para>
</listitem>
</itemizedlist>
- </section>
- <section id="spatial-strategy-quad-tree">
- <title>Two stages spatial queries on quad tree</title>
+ <para>To index your entities for range querying you have to:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>add the <classname>@Spatial</classname> annotation on your entity</para>
+ </listitem>
+
+ <listitem>
+ <para>add the <classname>@Latitude</classname> and
+ <classname>@Longitude</classname> annotations on your properties representing
+ the coordinates; these must be of type <classname>Double</classname></para>
+ </listitem>
+ </itemizedlist>
+
+ <example>
+ <title>Sample Spatial indexing: Hotel class</title>
+
+ <programlisting language="JAVA" role="JAVA">import org.hibernate.search.annotations.*;
+
+@Spatial @Indexed @Entity
+public class Hotel {
+
+ @Latitude
+ Double latitude
+
+ @Longitude
+ Double longitude
+ [..]</programlisting>
+ </example>
+ </section>
+
+ <section id="spatial-indexing-quadtree">
+ <title>Indexing coordinates in a Grid with Quad Trees</title>
+
+ <para>When setting <classname>@Spatial</classname>.<methodname>spatialMode</methodname>
+ to <classname>SpatialMode</classname>.<methodname>GRID</methodname>
+ the coordinates are encoded in several fields representing different
+ zoom levels. Each box for each level is labelled so coordinates are assigned
+ matching labels for each zoom level. This results in a tree encoding of
+ labels called <literal>quad tree</literal>.</para>
<para>Pros :</para>
@@ -136,153 +186,168 @@
<itemizedlist>
<listitem>
- <para>Index size is big (denormalization)</para>
+ <para>Index size is larger: need to encode multiple labels per
+ pair of coordinates</para>
</listitem>
</itemizedlist>
- </section>
- </section>
-
- <section id="spatial-range-queries">
- <title>Range query indexing and querying</title>
- <para>To index your entities for range querying you:</para>
+ <para>To index your entities you have to:</para>
<itemizedlist>
<listitem>
- <para>add the <classname>@Spatial</classname> annotation at class
- level</para>
+ <para>add the <classname>@Spatial</classname> annotation on the
+ entity with the <classname>SpatialMode</classname> set to GRID :
+ <code>@Spatial(spatialMode = SpatialMode.GRID)</code></para>
</listitem>
<listitem>
<para>add the <classname>@Latitude</classname> and
- <classname>@Longitude</classname> annotations at field level</para>
+ <classname>@Longitude</classname> annotations on the properties
+ representing your coordinates; these must be of type
+ <classname>Double</classname></para>
</listitem>
</itemizedlist>
<example>
- <title>Hotel class sample</title>
+ <title>Indexing coordinates in a Grid using Quad Trees</title>
- <programlisting language="JAVA" role="JAVA">import org.hibernate.search.annotations.*;
-
-@Entity
+ <programlisting language="JAVA" role="JAVA">@Spatial(spatialMode = SpatialMode.GRID)
@Indexed
-@Spatial
+@Entity
public class Hotel {
@Latitude
- Double latitude
+ Double latitude;
@Longitude
- Double longitude
- [..]</programlisting>
+ Double longitude;
+
+ [...]
+</programlisting>
</example>
+ </section>
- <para>To query your entities, you :</para>
+ <section id="spatial-coordinatesinterface">
+ <title>Implementing the Coordinates interface</title>
- <itemizedlist>
- <listitem>
- <para>use the DSL to build a spatial query with your search center and
- radius</para>
- </listitem>
+ <para>Instead of using the <literal>@</literal><classname>Latitude</classname>
+ and <literal>@</literal><classname>Longitue</classname> annotations
+ you can choose to implement the <classname>org.hibernate.search.spatial.Coordinates</classname>
+ interface.</para>
- <listitem>
- <para>eventually combine the resulting Query with other filters</para>
- </listitem>
+<example>
+ <title>Implementing the Coordinates interface</title>
- <listitem>
- <para>call the <methodname>createFullTextQuery()</methodname> and use
- the results</para>
- </listitem>
- </itemizedlist>
+ <programlisting language="JAVA" role="JAVA">import org.hibernate.search.annotations.*;
+import org.hibernate.search.spatial.Coordinates;
- <example>
- <title>Hotel class sample</title>
+@Spatial @Indexed @Entity
+public class Song implements Coordinates {
- <programlisting language="JAVA" role="JAVA">final QueryBuilder builder = fullTextSession.getSearchFactory()
- .buildQueryBuilder().forEntity( Hotel.class ).get();
+ @Id long id;
+ double latitude;
+ double longitude;
+ [...]
-org.apache.lucene.search.Query luceneQuery = builder.spatial()
- .onDefaultCoordinates()
- .within( radius, Unit.KM )
- .ofLatitude( centerLatitude )
- .andLongitude( centerLongitude )
- .createQuery();
+ @Override
+ Double getLatitude() {
+ return latitude;
+ }
-org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery( luceneQuery,
- Hotel.class );
-List results = hibQuery.list();</programlisting>
+ @Override
+ Double getLongitude() {
+ return longitude;
+ }
+
+ [...]
+</programlisting>
</example>
- <para>A fully working example can be found in the source code, in the
- testsuite. See
- <methodname>SpatialIndexingTest.testSpatialAnnotationOnClassLevel()</methodname>
- and in <classname>Hotel</classname> class.</para>
+ <para>As we will see in the section <xref linkend="spatial-multiplecoordinates"/>,
+ a <literal>@</literal><classname>Spatial</classname>
+ <literal>@</literal><classname>Indexed</classname> <literal>@</literal><classname>Entity</classname>
+ can have multiple <literal>@</literal><classname>Spatial</classname> annotations;
+ when having the entity implement <classname>Coordinates</classname>, the implemented
+ methods refer to the default Spatial name: the default pair of coordinates.</para>
+
+ <para>An alternative is to use properties implementing the <classname>Coordinates</classname>
+ interface; this way you can have multiple Spatial instances:</para>
+
+<example>
+ <title>Using attributes of type Coordinates</title>
+
+ <programlisting language="JAVA" role="JAVA">@Indexed @Entity
+public class Event {
+ @Id
+ Integer id;
+
+ @Field(store = Store.YES)
+ String name;
+
+ double latitude;
+ double longitude;
+
+ @Spatial(spatialMode = SpatialMode.GRID)
+ public Coordinates getLocation() {
+ return new Coordinates() {
+ @Override
+ public Double getLatitude() {
+ return latitude;
+ }
+
+ @Override
+ public Double getLongitude() {
+ return longitude;
+ }
+ };
+ }
+
+ [...]
+</programlisting>
+ </example>
+ <para>When using this form the <literal>@</literal><classname>Spatial</classname>
+ <literal>.name</literal> automatically defaults to the propery name.</para>
</section>
+ </section>
- <section id="spatial-two-stages-quad-tree">
- <title>Two stages spatial queries with quad tree</title>
+ <section id="spatial-queries">
+ <title>Performing Spatial Queries</title>
- <para>To index your entities you:</para>
+ <para>The Hibernate Search DSL has been extended to support the spatial
+ feature. You can build a query to search around a pair of coordinates
+ (latitude,longitude) or around a bean implementing the <classname>Coordinates</classname>
+ interface.</para>
+ <para>As with any fulltext queries, also for Spatial queries you:</para>
<itemizedlist>
<listitem>
- <para>add the <classname>@Spatial</classname> annotation at class
- level with a name for referencing the quad tree fields in the index and with
- the <classname>SpatialMode</classname> set to QUAD :
- <code>@Spatial(name="location", spatialMode =
- SpatialMode.QUAD)</code></para>
+ <para>retrieve a <classname>QueryBuilder</classname> from the
+ <classname>SearchFactory</classname> as a starting point</para>
</listitem>
<listitem>
- <para>add the <classname>@Latitude</classname> and
- <classname>@Longitude</classname> annotations at field level</para>
- </listitem>
- </itemizedlist>
-
- <example>
- <title>Querying range queries indexed entities</title>
-
- <programlisting language="JAVA" role="JAVA">@Entity
-@Indexed
-@Spatial(name="location", spatialMode = SpatialMode.QUAD)
-public class Hotel {
-
- @Latitude(of="location")
- Double latitude;
-
- @Longitude(of="location")
- Double longitude;
-
- [...]
-</programlisting>
- </example>
-
- <para>To query your entities, you:</para>
-
- <itemizedlist>
- <listitem>
- <para>use the DSL to build a spatial query with your center, radius
- and the name of the quad tree fields (location passed above)</para>
+ <para>use the DSL to build a spatial query with your search center and
+ radius</para>
</listitem>
<listitem>
- <para>eventually combine the resulting Query with other filters</para>
+ <para>optionally combine the resulting Query with other filters</para>
</listitem>
<listitem>
<para>call the <methodname>createFullTextQuery()</methodname> and use
- the results</para>
+ run it as any standard Hibernate or JPA <classname>Query</classname></para>
</listitem>
</itemizedlist>
- <example>
- <title>Querying quad tree indexed entities</title>
+ <example id="spatial-example-firstquery">
+ <title>Search for an Hotel by distance</title>
- <programlisting language="JAVA" role="JAVA">final QueryBuilder builder = fullTextSession.getSearchFactory()
+ <programlisting language="JAVA" role="JAVA">QueryBuilder builder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity( Hotel.class ).get();
org.apache.lucene.search.Query luceneQuery = builder.spatial()
- .onCoordinates( "location" )
+ .onDefaultCoordinates()
.within( radius, Unit.KM )
.ofLatitude( centerLatitude )
.andLongitude( centerLongitude )
@@ -293,43 +358,14 @@ org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery( luceneQuery,
List results = hibQuery.list();</programlisting>
</example>
- <para>Sample code is at
+ <para>A fully working example can be found in the source code, in the
+ testsuite. See
<methodname>SpatialIndexingTest.testSpatialAnnotationOnClassLevel()</methodname>
- and in Hotel class.</para>
-
- <tip>
- <para>There is a <classname>Coordinates</classname> interface that can
- be used to pass latitude and longitude to the @Spatial annotation
- instead of the @Latitude and @Longitude annotations. This can be used to
- expose latitude and longitude when their values are not fields of the
- indexed bean. See the <literal>Hotel</literal> class in the testsuite
- for sample working code.</para>
- </tip>
- </section>
-
- <section id="spatial-dsl">
- <title>Spatial query DSL</title>
+ and in the <classname>Hotel</classname> class.</para>
- <para>The Hibernate Search DSL has been extended to support the spatial
- feature. You can build a query for searching around a (latitude,longitude)
- or around an bean implementing the Coordinates interface :</para>
-
- <example>
- <title>DSL example with latitude and longitude</title>
-
- <programlisting language="JAVA" role="JAVA">QueryBuilder builder = fullTextSession.getSearchFactory()
- .buildQueryBuilder().forEntity( RangeEvent.class ).get();
-
-double centerLatitude = 24;
-double centerLongitude = 31.5;
-
-org.apache.lucene.search.Query luceneQuery = builder.spatial().onCoordinates( "location" )
- .within( 50, Unit.KM ).ofLatitude( centerLatitude ).andLongitude( centerLongitude )
- .createQuery();
-
-org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery( luceneQuery,
- RangeEvent.class );</programlisting>
- </example>
+ <para>As an alternative to passing separate values for latitude and longitude
+ values, you can also pass an object implementing the <classname>Coordinates</classname>
+ interface:</para>
<example>
<title>DSL example with Coordinates</title>
@@ -344,9 +380,8 @@ org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery( luceneQuery,
List results = fullTextSession.createFullTextQuery( query, POI.class ).list();</programlisting>
</example>
- </section>
- <section id="spatial-distance">
+ <section id="spatial-queries-distance">
<title>Returning distance to query point in the search results</title>
<section id="spatial-distance-projection">
@@ -393,15 +428,16 @@ List results = hibQuery.list();</programlisting>
</listitem>
</itemizedlist>
+ <note><title>Distance projection and null values</title>
<para>Using distance projection on non @Spatial enabled entities and/or
with a non spatial Query will have unexpected results as entities not
spatially indexed and/or having null values for latitude or longitude
- will be considered to be at (0,0)/(lat,0)/(0,long)</para>
+ will be considered to be at (0,0)/(lat,0)/(0,long).</para>
<para>Using distance projection with a spatial query on spatially
indexed entities having, eventually, <literal>null</literal> values for
latitude and/or longitude is safe as they will not be found by the
- spatial query and won't have distance calculated</para>
+ spatial query and won't have distance calculated.</para></note>
</section>
<section id="spatial-distance-sort">
@@ -417,7 +453,7 @@ List results = hibQuery.list();</programlisting>
<programlisting language="JAVA" role="JAVA">double centerLatitude = 24.0d;
double centerLongitude = 32.0d;
-final QueryBuilder builder = fullTextSession.getSearchFactory()
+QueryBuilder builder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity( POI.class ).get();
org.apache.lucene.search.Query luceneQuery = builder.spatial()
.onCoordinates("location")
@@ -438,25 +474,96 @@ hibQuery.setSort(distanceSort);</programlisting>
the query. This repetition is needed to allow you to define Queries with
any tool.</para>
+ <note><title>Sorting and null values</title>
<para>Using distance sort on non @Spatial enabled entities and/or with a
non spatial Query will have also unexpected results as entities non
spatially indexed and/or with null values for latitude or longitude will
be considered to be at (0,0)/(lat,0)/(0,long)</para>
<para>Using distance sort with a spatial query on spatially indexed
- entities having, eventually, <literal>null</literal> values for latitude
+ entities having, potentially, <literal>null</literal> values for latitude
and/or longitude is safe as they will not be found by the spatial query
- and so won't be sorted</para>
+ and so won't be sorted</para></note>
</section>
</section>
+ </section>
+
+ <section id="spatial-multiplecoordinates">
+ <title>Multiple Coordinate pairs</title>
+ <para>You can associate multiple pairs of coordinates to the same entity,
+ as long as each pair is uniquelly identified by using a different name. This is achieved
+ by stacking multiple <literal>@</literal><classname>Spatial</classname>
+ annotations in a <literal>@</literal><classname>Spatials</classname> annotation,
+ and specifying the <literal>name</literal> attribute on the
+ <literal>@</literal><classname>Spatial</classname> annotation.</para>
+
+<example>
+ <title>Multiple sets of coordinates</title>
+ <programlisting language="JAVA" role="JAVA">import org.hibernate.search.annotations.*;
+
+@Spatials({
+ @Spatial,
+ @Spatial(name="work", spatialMode = SpatialMode.GRID)
+})
+@Entity
+@Indexed
+public class UserEx {
+
+ @Id
+ Integer id;
+
+ @Latitude
+ Double homeLatitude;
+
+ @Longitude
+ Double homeLongitude;
+
+ @Latitude(of="work")
+ Double workLatitude;
+
+ @Longitude(of="work")
+ Double workLongitude;
+</programlisting>
+ </example>
+
+ <para>In the example <xref linkend="spatial-example-firstquery"/> we used
+ <literal>onDefaultCoordinates()</literal> which points to the coordinates
+ defined by a <literal>@</literal><classname>Spatial</classname> annotation
+ whose <literal>name</literal> attribute was not specified.</para>
+
+ <para>To target an alternative pair of coordinates at query time, we need
+ to specify the pair by name using <methodname>onCoordinates</methodname>
+ <literal>(String)</literal> instead of
+ <methodname>onDefaultCoordinates()</methodname>:</para>
+
+ <example>
+ <title>Querying on non-default coordinate set</title>
+
+<programlisting language="JAVA" role="JAVA">QueryBuilder builder = fullTextSession.getSearchFactory()
+ .buildQueryBuilder().forEntity( UserEx.class ).get();
+
+org.apache.lucene.search.Query luceneQuery = builder.spatial()
+ .onCoordinates( "work" )
+ .within( radius, Unit.KM )
+ .ofLatitude( centerLatitude )
+ .andLongitude( centerLongitude )
+ .createQuery();
+
+org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery( luceneQuery,
+ Hotel.class );
+List results = hibQuery.list();</programlisting>
+ </example>
+ </section>
<section id="spatial-behind-curtain">
- <title>Quad Tree - Behind the curtain</title>
+ <title>Insight: implementation details of Quad Tree indexing</title>
- <para>Here are the gory details of the quad tree based indexation.</para>
+ <para>The present chapter is meant to provide a technical insight in
+ quad-tree (grid) indexing: how coordinates are mapped to the index and
+ how queries are implemented.</para>
<section>
- <title>At indexation level</title>
+ <title>At indexing level</title>
<para>When Hibernate Search indexes the entity annotated with @Spatial,
it instantiates a SpatialFieldBridge to transform the latitude and
@@ -484,7 +591,7 @@ hibQuery.setSort(distanceSort);</programlisting>
<para>The index is divided into n levels labeled from 0 to n-1.</para>
- <para>At the level 0 the projected space is the whole earth. At the
+ <para>At the level 0 the projected space is the whole Earth. At the
level 1 the projected space is devided into 4 rectangles (called boxes
as in bounding box):</para>
@@ -504,7 +611,7 @@ hibQuery.setSort(distanceSort);</programlisting>
space.</para>
<para>Beware! The boxes are rectangles in projected space but the
- related area on earth is not a rectangle!</para>
+ related area on Earth is not a rectangle!</para>
<para>Now that we have all these boxes at all these levels will be
indexing points "into" them.</para>
Something went wrong with that request. Please try again.