/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat, Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.search.test.filter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.DocIdBitSet;
import org.apache.lucene.util.OpenBitSet;
import org.hibernate.search.filter.impl.FilterOptimizationHelper;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Used to test org.hibernate.search.filter.FiltersOptimizationHelper
* @see org.hibernate.search.filter.impl.FilterOptimizationHelper
* @author Sanne Grinovero
*/
public class FiltersOptimizationTest {
/**
* in some cases optimizations are not possible,
* test that mergeByBitAnds returns the same instance
* in that case.
*/
@Test
public void testSkipMerging() {
List<DocIdSet> dataIn = new ArrayList<DocIdSet>( 3 );
dataIn.add( makeOpenBitSetTestSet( 1,2,3,5,8,9,10,11 ) );
dataIn.add( makeBitSetTestSet( 1,2,3,5,8,9,10,11,20 ) );
dataIn.add( makeAnonymousTestSet( 1,2,3,5,8,9,10,11 ) );
dataIn.add( makeAnonymousTestSet( 1,2,3,5,8,9,10,11,12 ) );
List<DocIdSet> merge = FilterOptimizationHelper.mergeByBitAnds( dataIn );
assertSame( dataIn, merge );
}
/**
* In case two filters are of OpenBitSet implementation,
* they should be AND-ed by using bit operations
* (rather than build the iterator).
* @throws IOException should not be thrown
*/
@Test
public void testDoMergingOnOpenBitSet() throws IOException {
List<DocIdSet> dataIn = new ArrayList<DocIdSet>( 3 );
dataIn.add( makeOpenBitSetTestSet( 1,2,5,8,9,10,11 ) );
dataIn.add( makeOpenBitSetTestSet( 1,2,3,5,8,11 ) );
DocIdSet unmergedSet = makeAnonymousTestSet( 1,2,3,5,8,9,10,11 );
dataIn.add( unmergedSet );
List<DocIdSet> merge = FilterOptimizationHelper.mergeByBitAnds( dataIn );
assertNotSame( dataIn, merge );
assertEquals( 2, merge.size() );
assertSame( unmergedSet, merge.get( 0 ) );
assertTrue( isIdSetSequenceSameTo( merge.get( 1 ), 1,2,5,8,11 ) );
}
/**
* In case two filters are of OpenBitSet implementation,
* they should be AND-ed by using bit operations
* (rather than build the iterator).
* @throws IOException should be thrown
*/
@Test
public void testDoMergingOnJavaBitSet() throws IOException {
List<DocIdSet> dataIn = new ArrayList<DocIdSet>( 3 );
dataIn.add( makeBitSetTestSet( 1,2,5,8,9,10,11 ) );
dataIn.add( makeBitSetTestSet( 1,2,3,5,8,11 ) );
DocIdSet unmergedSet = makeAnonymousTestSet( 1,2,3,5,8,9,10,11 );
dataIn.add( unmergedSet );
List<DocIdSet> merge = FilterOptimizationHelper.mergeByBitAnds( dataIn );
assertNotSame( dataIn, merge );
assertEquals( 2, merge.size() );
assertSame( unmergedSet, merge.get( 0 ) );
assertTrue( isIdSetSequenceSameTo( merge.get( 1 ), 1,2,5,8,11 ) );
}
/**
* Used to this test the testcase's helper method isIdSetSequenceSameTo
* @throws IOException
*/
@Test
public void testSelfIdSequenceTester() throws IOException {
assertTrue( isIdSetSequenceSameTo(
makeOpenBitSetTestSet( 1,2,3,5,8,11 ),
1,2,3,5,8,11 ) );
assertFalse( isIdSetSequenceSameTo(
makeOpenBitSetTestSet( 1,2,3,5,8 ),
1,2,3,5,8,11 ) );
assertFalse( isIdSetSequenceSameTo(
makeOpenBitSetTestSet( 1,2,3,5,8,11 ),
1,2,3,5,8 ) );
}
/**
* Verifies if the docIdSet is representing a specific
* sequence of docIds.
* @param docIdSet the docIdSet to test
* @param expectedIds an array of document ids
* @return true if iterating on docIdSet returns the expectedIds
* @throws IOException should not happen
*/
private boolean isIdSetSequenceSameTo(DocIdSet docIdSet, int...expectedIds) throws IOException {
DocIdSetIterator idSetIterator = docIdSet.iterator();
for ( int setBit : expectedIds ) {
int currentId = idSetIterator.nextDoc();
if ( currentId == DocIdSetIterator.NO_MORE_DOCS ) {
return false;
}
if ( currentId != setBit ) {
return false;
}
}
// and now test both sequences are at the end:
return idSetIterator.nextDoc() == DocIdSetIterator.NO_MORE_DOCS;
}
/**
* test helper, makes an implementation of a DocIdSet
* @param docIds the ids it should contain
* @return
*/
private DocIdSet makeAnonymousTestSet(int... docIds) {
DocIdSet idSet = makeOpenBitSetTestSet( docIds );
return new DocIdSetHiddenType( idSet );
}
/**
* test helper, makes a prefilled OpenBitSet
* @param enabledBits the ids it should contain
* @return a new OpenBitSet
*/
private OpenBitSet makeOpenBitSetTestSet(int... enabledBits) {
OpenBitSet set = new OpenBitSet();
for (int position : enabledBits ) {
// a minimal check for input duplicates:
assertFalse( set.get( position ) );
set.set( position );
}
return set;
}
/**
* test helper, makes a prefilled DocIdBitSet
* using the java.lang.BitSet
* @see java.lang.BitSet
* @param enabledBits the ids it should contain
* @return a ne DocIdBitSet
*/
private DocIdBitSet makeBitSetTestSet(int... enabledBits) {
BitSet set = new BitSet();
for (int position : enabledBits ) {
// a minimal check for input duplicates:
assertFalse( set.get( position ) );
set.set( position );
}
return new DocIdBitSet( set );
}
/**
* Implementation for testing: wraps a DocIdSet with a new type
* to make it not possible to cast/detect to the original type.
*/
private static class DocIdSetHiddenType extends DocIdSet {
private final DocIdSet bitSet;
DocIdSetHiddenType(DocIdSet wrapped) {
this.bitSet = wrapped;
}
@Override
public DocIdSetIterator iterator() throws IOException {
return bitSet.iterator();
}
}
}