/* * 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.spatial.impl; import java.io.IOException; import java.util.Collections; import java.util.List; import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Term; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitDocIdSet; import org.apache.lucene.util.FixedBitSet; import org.hibernate.search.spatial.SpatialFieldBridgeByHash; /** * Lucene distance Query for documents which have been indexed with Hibernate Search {@link SpatialFieldBridgeByHash} * Use denormalized spatial hash cell ids to return a sub set of documents near the center * * @author Nicolas Helleringer * @see org.hibernate.search.spatial.SpatialFieldBridgeByHash * @see org.hibernate.search.spatial.Coordinates */ public final class SpatialHashQuery extends Query { private final List<String> spatialHashCellsIds; private final String fieldName; public SpatialHashQuery(List<String> spatialHashCellsIds, String fieldName) { this.spatialHashCellsIds = spatialHashCellsIds; this.fieldName = fieldName; } @Override public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException { return new ConstantScoreWeight( this ) { @Override public Scorer scorer(LeafReaderContext context) throws IOException { DocIdSetIterator iterator = createDocIdSetIterator( context ); return new ConstantScoreScorer( this, score(), iterator ); } }; } /** * Search the index for document having the correct spatial hash cell id at given grid level. * * @param context the {@link LeafReaderContext} for which to return the {@link DocIdSet}. * @return a {@link DocIdSetIterator} with the matching document ids */ private DocIdSetIterator createDocIdSetIterator(LeafReaderContext context) throws IOException { if ( spatialHashCellsIds.size() == 0 ) { return null; } final LeafReader atomicReader = context.reader(); BitDocIdSet matchedDocumentsIds = new BitDocIdSet( new FixedBitSet( atomicReader.maxDoc() ) ); boolean found = false; for ( int i = 0; i < spatialHashCellsIds.size(); i++ ) { Term spatialHashCellTerm = new Term( fieldName, spatialHashCellsIds.get( i ) ); PostingsEnum spatialHashCellsDocs = atomicReader.postings( spatialHashCellTerm ); if ( spatialHashCellsDocs != null ) { while ( true ) { final int docId = spatialHashCellsDocs.nextDoc(); if ( docId == DocIdSetIterator.NO_MORE_DOCS ) { break; } else { matchedDocumentsIds.bits().set( docId ); found = true; } } } } if ( found ) { return matchedDocumentsIds.iterator(); } else { return DocIdSetIterator.empty(); } } public List<String> getSpatialHashCellsIds() { return Collections.unmodifiableList( spatialHashCellsIds ); } public String getFieldName() { return fieldName; } @Override public int hashCode() { int hashCode = 31 * super.hashCode() + spatialHashCellsIds.hashCode(); hashCode = 31 * hashCode + fieldName.hashCode(); return hashCode; } @Override public boolean equals(Object obj) { if ( obj == this ) { return true; } if ( obj instanceof SpatialHashQuery ) { SpatialHashQuery other = (SpatialHashQuery) obj; return spatialHashCellsIds.equals( other.spatialHashCellsIds ) && fieldName.equals( other.fieldName ); } return false; } @Override public String toString(String field) { final StringBuilder sb = new StringBuilder(); sb.append( "SpatialHashFilter" ); sb.append( "{spatialHashCellsIds=" ).append( spatialHashCellsIds ); sb.append( ", fieldName='" ).append( fieldName ).append( '\'' ); sb.append( '}' ); return sb.toString(); } }