/* * Hibernate Search, full-text search for your domain model * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.search.test.spatial; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.QueryWrapperFilter; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.resource.transaction.spi.TransactionStatus; import org.hibernate.search.FullTextQuery; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.hibernate.search.query.dsl.QueryBuilder; import org.hibernate.search.query.engine.spi.FacetManager; import org.hibernate.search.query.facet.Facet; import org.hibernate.search.query.facet.FacetSortOrder; import org.hibernate.search.query.facet.FacetingRequest; import org.hibernate.search.spatial.impl.Point; import org.hibernate.search.spatial.impl.Rectangle; import org.hibernate.search.spatial.impl.SpatialQueryBuilderFromCoordinates; /** * Hibernate Search spatial : Benchmarks with <a href="http://www.geonames.org">GeoNames</a> * as data source (nearly 8M points) * * Test many queries types : * * - pure distance post filtering * - double numeric range * - spatial hash * - double range + distance * - spatial hash + distance * * To test you must download <a href="http://download.geonames.org/export/dump/FR.zip">FR GeoBames file</a> * and extract it at the root directory of Hibernate Search * * @author Nicolas Helleringer */ public final class BenchWithGeonames { private BenchWithGeonames() { //not allowed } private static final String hibernateConfigurationFile = "/org/hibernate/search/test/spatial/hibernate.cfg.xml"; private static final String geonamesDataFile = "FR.txt"; public static void main(String args[]) { LoadGeonames(); Bench(); FacetTest(); } public static void LoadGeonames() { Session session = null; FullTextSession fullTextSession = null; BufferedReader buffRead = null; SessionFactory sessionFactory = null; try { sessionFactory = new Configuration().configure( hibernateConfigurationFile ).buildSessionFactory(); session = sessionFactory.openSession(); session.createSQLQuery( "delete from MemberLevelTestPoI" ).executeUpdate(); session.beginTransaction(); fullTextSession = Search.getFullTextSession( session ); File geonamesFile = new File( geonamesDataFile ); buffRead = new BufferedReader( new InputStreamReader( new FileInputStream( geonamesFile ), "UTF-8" ) ); String line = null; int line_number = 0; while ( ( line = buffRead.readLine() ) != null ) { String[] data = line.split( "\t" ); POI current = new POI( Integer.parseInt( data[0] ), data[1], Double.parseDouble( data[4] ), Double.parseDouble( data[5] ), data[7] ); session.save( current ); if ( ( line_number % 10000 ) == 0 ) { fullTextSession.flushToIndexes(); session.getTransaction().commit(); session.close(); session = sessionFactory.openSession(); fullTextSession = Search.getFullTextSession( session ); session.beginTransaction(); System.out.println( Integer.toString( line_number ) ); } line_number++; } session.getTransaction().commit(); fullTextSession.close(); sessionFactory.close(); buffRead.close(); } catch (Exception e) { e.printStackTrace(); } finally { if ( fullTextSession != null && fullTextSession.isOpen() ) { Transaction transaction = fullTextSession.getTransaction(); if ( transaction != null && transaction.getStatus() == TransactionStatus.ACTIVE ) { transaction.rollback(); } fullTextSession.close(); } if ( sessionFactory != null && !sessionFactory.isClosed() ) { sessionFactory.close(); } try { if ( buffRead != null ) { buffRead.close(); } } catch (Exception e) { e.printStackTrace(); } } } public static void Bench() { Session session = null; FullTextSession fullTextSession = null; SessionFactory sessionFactory = null; try { sessionFactory = new Configuration().configure( hibernateConfigurationFile ).buildSessionFactory(); session = sessionFactory.openSession(); session.beginTransaction(); fullTextSession = Search.getFullTextSession( session ); long spatialHashTotalDuration = 0; long spatialTotalDuration = 0; long doubleRangeTotalDuration = 0; long distanceDoubleRangeTotalDuration = 0; long spatialHashDocsFetched = 0; long spatialDocsFetched = 0; long doubleRangeDocsFetched = 0; long distanceDoubleRangeDocsFetched = 0; org.apache.lucene.search.Query luceneQuery; long startTime, endTime, duration; FullTextQuery hibQuery; List spatialHashResults, rangeResults; final QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity( POI.class ).get(); org.apache.lucene.search.Query query; final Integer iterations = 2000; final Integer warmUp = 50; Random random = new Random( 42 ); for ( int i = 0; i < iterations; i++ ) { Point center = Point.fromDegrees( random.nextDouble() * 2 + 44 , random.nextDouble() * 2 + 3 ); double radius = 25.0d; Rectangle boundingBox = Rectangle.fromBoundingCircle( center, radius ); query = queryBuilder.bool() .must( queryBuilder.range() .onField( "latitude" ) .from( boundingBox.getLowerLeft().getLatitude() ) .to( boundingBox.getUpperRight().getLatitude() ) .createQuery() ) .must( queryBuilder.range() .onField( "longitude" ) .from( boundingBox.getLowerLeft().getLongitude() ) .to( boundingBox.getUpperRight().getLongitude() ) .createQuery() ) .createQuery(); hibQuery = fullTextSession.createFullTextQuery( query, POI.class ); hibQuery.setProjection( "id", "name" ); startTime = System.nanoTime(); try { doubleRangeDocsFetched += hibQuery.getResultSize(); } finally { endTime = System.nanoTime(); } duration = endTime - startTime; if ( i > warmUp ) { doubleRangeTotalDuration += duration; } session.clear(); query = queryBuilder.bool() .must( queryBuilder.range() .onField( "latitude" ) .from( boundingBox.getLowerLeft().getLatitude() ) .to( boundingBox.getUpperRight().getLatitude() ) .createQuery() ) .must( queryBuilder.range() .onField( "longitude" ) .from( boundingBox.getLowerLeft().getLongitude() ) .to( boundingBox.getUpperRight().getLongitude() ) .createQuery() ) .createQuery(); org.apache.lucene.search.Query filteredQuery = new ConstantScoreQuery( SpatialQueryBuilderFromCoordinates.buildDistanceQuery( new QueryWrapperFilter( query ), center, radius, "location" ) ); hibQuery = fullTextSession.createFullTextQuery( filteredQuery, POI.class ); hibQuery.setProjection( "id", "name" ); startTime = System.nanoTime(); try { distanceDoubleRangeDocsFetched += hibQuery.getResultSize(); } finally { endTime = System.nanoTime(); } duration = endTime - startTime; if ( i > warmUp ) { distanceDoubleRangeTotalDuration += duration; } rangeResults = hibQuery.list(); session.clear(); luceneQuery = SpatialQueryBuilderFromCoordinates.buildSpatialHashQuery( center, radius, "location" ); hibQuery = fullTextSession.createFullTextQuery( luceneQuery, POI.class ); hibQuery.setProjection( "id", "name" ); startTime = System.nanoTime(); try { spatialHashDocsFetched += hibQuery.getResultSize(); } finally { endTime = System.nanoTime(); } duration = endTime - startTime; if ( i > warmUp ) { spatialHashTotalDuration += duration; } session.clear(); luceneQuery = SpatialQueryBuilderFromCoordinates.buildSpatialQueryByHash( center, radius, "location" ); hibQuery = fullTextSession.createFullTextQuery( luceneQuery, POI.class ); hibQuery.setProjection( "id", "name" ); startTime = System.nanoTime(); try { spatialDocsFetched += hibQuery.getResultSize(); } finally { endTime = System.nanoTime(); } duration = endTime - startTime; if ( i > warmUp ) { spatialTotalDuration += duration; } spatialHashResults = hibQuery.list(); session.clear(); if ( rangeResults.size() != spatialHashResults.size() ) { luceneQuery = SpatialQueryBuilderFromCoordinates.buildDistanceQuery( center, radius, "location" ); hibQuery = fullTextSession.createFullTextQuery( luceneQuery, POI.class ); hibQuery.setProjection( "id", "name" ); System.out.println( ">>>>> Different numbers of documents fetched for point (" + Double.toString( center.getLatitude() ) + "," + Double.toString( center.getLongitude() ) + ") and radius " + Double.toString( radius ) ); System.out.println( "Range results : " + rangeResults ); System.out.println( "Spatial Hash results : " + spatialHashResults ); System.out.println( "Pure distance results : " + hibQuery.getResultSize() ); List<Integer> rangeIds = new ArrayList<Integer>(); for ( int index = 0; index < rangeResults.size(); index++ ) { rangeIds.add( (Integer) ( (Object[]) rangeResults.get( index ) )[0] ); } List<Integer> spatialHashIds = new ArrayList<Integer>(); for ( int index = 0; index < spatialHashResults.size(); index++ ) { spatialHashIds.add( (Integer) ( (Object[]) spatialHashResults.get( index ) )[0] ); } rangeIds.removeAll( spatialHashIds ); System.out.println( "Missing Ids : " + rangeIds ); } } session.getTransaction().commit(); session.close(); sessionFactory.close(); System.out .println( "Mean time with Spatial Hash : " + Double.toString( spatialHashTotalDuration * Math.pow( 10, -6 ) / ( iterations - warmUp ) ) + " ms. Average number of docs fetched : " + Double.toString( spatialHashDocsFetched / ((iterations - warmUp) * 1.0d) ) ); System.out .println( "Mean time with Spatial Hash + Distance filter : " + Double.toString( spatialTotalDuration * Math.pow( 10, -6 ) / ( iterations - warmUp ) ) + " ms. Average number of docs fetched : " + Double.toString( spatialDocsFetched / ((iterations - warmUp) * 1.0d) ) ); System.out .println( "Mean time with DoubleRange : " + Double.toString( doubleRangeTotalDuration * Math.pow( 10, -6 ) / (iterations - warmUp) ) + " ms. Average number of docs fetched : " + Double.toString( doubleRangeDocsFetched / ((iterations - warmUp) * 1.0d) ) ); System.out .println( "Mean time with DoubleRange + Distance filter : " + Double.toString( distanceDoubleRangeTotalDuration * Math.pow( 10, -6 ) / ( iterations - warmUp ) ) + " ms. Average number of docs fetched : " + Double.toString( distanceDoubleRangeDocsFetched / ((iterations - warmUp) * 1.0d) ) ); } catch (Exception e) { e.printStackTrace(); } finally { if ( fullTextSession != null && fullTextSession.isOpen() ) { Transaction transaction = fullTextSession.getTransaction(); if ( transaction != null && transaction.getStatus() == TransactionStatus.ACTIVE ) { transaction.rollback(); } fullTextSession.close(); } if ( sessionFactory != null && !sessionFactory.isClosed() ) { sessionFactory.close(); } } } public static void FacetTest() { Session session = null; FullTextSession fullTextSession = null; SessionFactory sessionFactory = null; try { sessionFactory = new Configuration().configure( hibernateConfigurationFile ).buildSessionFactory(); session = sessionFactory.openSession(); session.beginTransaction(); fullTextSession = Search.getFullTextSession( session ); org.apache.lucene.search.Query luceneQuery; FullTextQuery hibQuery; Point center = Point.fromDegrees( 46, 4 ); double radius = 50.0d; luceneQuery = SpatialQueryBuilderFromCoordinates.buildSpatialQueryByHash( center, radius, "location" ); hibQuery = fullTextSession.createFullTextQuery( luceneQuery, POI.class ); hibQuery.setProjection( "id", "name", "type" ); FacetManager facetManager = hibQuery.getFacetManager(); QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity( POI.class ).get(); FacetingRequest facetingRequest = queryBuilder.facet() .name( "typeFacet" ) .onField( "type" ) .discrete() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .createFacetingRequest(); facetManager.enableFaceting( facetingRequest ); Integer size = hibQuery.getResultSize(); hibQuery.list(); List<Facet> facets = facetManager.getFacets( "typeFacet" ); System.out.println( facets ); session.getTransaction().commit(); session.close(); sessionFactory.close(); } catch (Exception e) { e.printStackTrace(); } finally { if ( fullTextSession != null && fullTextSession.isOpen() ) { Transaction transaction = fullTextSession.getTransaction(); if ( transaction != null && transaction.getStatus() == TransactionStatus.ACTIVE ) { transaction.rollback(); } fullTextSession.close(); } if ( sessionFactory != null && !sessionFactory.isClosed() ) { sessionFactory.close(); } } } }