/* * Hibernate, Relational Persistence for Idiomatic Java * * JBoss, Home of Professional Open Source * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.hibernate.search.test.spatial; 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.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.SpatialQueryBuilderFromPoint; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * 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 * - quad tree * - double range + distance * - quad tree + 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 <nicolas.helleringer@novacodex.net> */ public class BenchWithGeonames { private static String hibernateConfigurationFile = "/org/hibernate/search/test/spatial/hibernate.cfg.xml"; private static 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 FileReader( geonamesFile ) ); 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.isActive() ) { 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 quadTreeTotalDuration = 0; long spatialTotalDuration = 0; long doubleRangeTotalDuration = 0; long distanceDoubleRangeTotalDuration = 0; long quadTreeDocsFetched = 0; long spatialDocsFetched = 0; long doubleRangeDocsFetched = 0; long distanceDoubleRangeDocsFetched = 0; org.apache.lucene.search.Query luceneQuery; long startTime, endTime, duration; FullTextQuery hibQuery; List quadTreeResults, 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( SpatialQueryBuilderFromPoint.buildDistanceFilter( 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 = SpatialQueryBuilderFromPoint.buildQuadTreeQuery( center, radius, "location" ); hibQuery = fullTextSession.createFullTextQuery( luceneQuery, POI.class ); hibQuery.setProjection( "id", "name" ); startTime = System.nanoTime(); try { quadTreeDocsFetched += hibQuery.getResultSize(); } finally { endTime = System.nanoTime(); } duration = endTime - startTime; if ( i > warmUp ) { quadTreeTotalDuration += duration; } session.clear(); luceneQuery = SpatialQueryBuilderFromPoint.buildSpatialQueryByQuadTree( 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; } quadTreeResults= hibQuery.list(); session.clear(); if ( rangeResults.size() != quadTreeResults.size() ) { luceneQuery = SpatialQueryBuilderFromPoint.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( "Quad Tree results : " + quadTreeResults ); 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> quadTreeIds = new ArrayList<Integer>(); for( int index= 0; index< quadTreeResults.size(); index++ ) { quadTreeIds.add((Integer) ((Object[]) quadTreeResults.get(index))[0]); } rangeIds.removeAll( quadTreeIds ); System.out.println( "Missing Ids : " + rangeIds); } } session.getTransaction().commit(); session.close(); sessionFactory.close(); System.out .println( "Mean time with Quad Tree : " + Double.toString( ( double ) quadTreeTotalDuration * Math.pow( 10, -6 ) / (iterations - warmUp) ) + " ms. Average number of docs fetched : " + Double.toString( quadTreeDocsFetched / ((iterations - warmUp) * 1.0d) ) ); System.out .println( "Mean time with Quad Tree + Distance filter : " + Double.toString( ( double ) 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( ( double ) 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( ( double ) 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.isActive() ) { 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 = SpatialQueryBuilderFromPoint.buildSpatialQueryByQuadTree( 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(); List list = 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.isActive() ) { transaction.rollback(); } fullTextSession.close(); } if ( sessionFactory != null && !sessionFactory.isClosed() ) { sessionFactory.close(); } } } }