Package org.hibernate.search.test.filters

Source Code of org.hibernate.search.test.filters.FreshReadersProvidedTest$Guest

/*
* 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.filters;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.CompositeReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.MultiReader;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.BitsFilteredDocIdSet;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.OpenBitSet;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.backend.spi.Work;
import org.hibernate.search.backend.spi.WorkType;
import org.hibernate.search.engine.spi.SearchFactoryImplementor;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.query.engine.spi.EntityInfo;
import org.hibernate.search.testsupport.TestForIssue;
import org.hibernate.search.testsupport.junit.SearchFactoryHolder;
import org.hibernate.search.testsupport.setup.TransactionContextForTest;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;

/**
* Verified filters don't get stale IndexReader instances after a change is applied.
* Note that filters operate on per-segment sub-readers, while we usually expose
* top level (recursive) global IndexReader views: this usually should not affect
* their usage but is relevant to how we test them.
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2013 Red Hat Inc.
* @since 4.2
*/
@TestForIssue(jiraKey = "HSEARCH-1230")
public class FreshReadersProvidedTest {

  @Rule
  public SearchFactoryHolder sfHolder = new SearchFactoryHolder( Guest.class );

  @Test
  public void filtersTest() {
    SearchFactoryImplementor searchFactory = sfHolder.getSearchFactory();
    Assert.assertNotNull( searchFactory.getIndexManagerHolder().getIndexManager( "guests" ) );

    { // Store guest "Thorin Oakenshield" in the index
      Guest lastDwarf = new Guest();
      lastDwarf.id = 13l;
      lastDwarf.name = "Thorin Oakenshield";

      Work work = new Work( lastDwarf, lastDwarf.id, WorkType.ADD, false );
      TransactionContextForTest tc = new TransactionContextForTest();
      searchFactory.getWorker().performWork( work, tc );
      tc.end();
    }

    QueryBuilder guestQueryBuilder = searchFactory.buildQueryBuilder()
        .forEntity( Guest.class )
        .get();

    Query queryAllGuests = guestQueryBuilder.all().createQuery();

    List<EntityInfo> queryEntityInfos = searchFactory.createHSQuery()
      .luceneQuery( queryAllGuests )
      .targetedEntities( Arrays.asList( new Class<?>[]{ Guest.class } ) )
      .queryEntityInfos();

    Assert.assertEquals( 1, queryEntityInfos.size() );
    Assert.assertEquals( 13L, queryEntityInfos.get( 0 ).getId() );

    RecordingFilter filter = new RecordingFilter( "name" );
    List<EntityInfo> filteredQueryEntityInfos = searchFactory.createHSQuery()
        .luceneQuery( queryAllGuests )
        .targetedEntities( Arrays.asList( new Class<?>[]{ Guest.class } ) )
        .filter( filter )
        .queryEntityInfos();

    checkFilterInspectedAllSegments( filter );
    expectedTermsForFilter( filter, "thorin", "oakenshield" );
    Assert.assertEquals( 1, filteredQueryEntityInfos.size() );
    Assert.assertEquals( 13L, filteredQueryEntityInfos.get( 0 ).getId() );

    { // Store guest "Balin"
      Guest balin = new Guest();
      balin.id = 7l;
      balin.name = "Balin";

      Work work = new Work( balin, balin.id, WorkType.ADD, false );
      TransactionContextForTest tc = new TransactionContextForTest();
      searchFactory.getWorker().performWork( work, tc );
      tc.end();
    }

    List<EntityInfo> queryEntityInfosAgain = searchFactory.createHSQuery()
      .luceneQuery( queryAllGuests )
      .targetedEntities( Arrays.asList( new Class<?>[]{ Guest.class } ) )
      .queryEntityInfos();

    Assert.assertEquals( 2, queryEntityInfosAgain.size() );
    Assert.assertEquals( 13L, queryEntityInfosAgain.get( 0 ).getId() );
    Assert.assertEquals( 7L, queryEntityInfosAgain.get( 1 ).getId() );

    RecordingFilter secondFilter = new RecordingFilter( "name" );
    List<EntityInfo> secondFilteredQueryEntityInfos = searchFactory.createHSQuery()
        .luceneQuery( queryAllGuests )
        .targetedEntities( Arrays.asList( new Class<?>[]{ Guest.class } ) )
        .filter( secondFilter )
        .queryEntityInfos();

    checkFilterInspectedAllSegments( secondFilter );
    expectedTermsForFilter( secondFilter, "thorin", "oakenshield", "balin" );

    Assert.assertEquals( 2, secondFilteredQueryEntityInfos.size() );
    Assert.assertEquals( 13L, secondFilteredQueryEntityInfos.get( 0 ).getId() );
    Assert.assertEquals( 7L, secondFilteredQueryEntityInfos.get( 1 ).getId() );
  }

  private void expectedTermsForFilter(RecordingFilter filter, String... term) {
    Assert.assertEquals( term.length, filter.seenTerms.size() );
    Assert.assertTrue( filter.seenTerms.containsAll( Arrays.asList( term ) ) );
  }

  /**
   * Verifies that the current RecordingFilter has been fed all the same sub-readers
   * which would be obtained from a freshly checked out IndexReader.
   *
   * @param filter test filter instance
   */
  private void checkFilterInspectedAllSegments(RecordingFilter filter) {
    SearchFactoryImplementor searchFactory = sfHolder.getSearchFactory();
    IndexReader currentIndexReader = searchFactory.getIndexReaderAccessor().open( Guest.class );
    try {
      List<IndexReader> allSubReaders = getSubIndexReaders( (MultiReader) currentIndexReader );
      for ( IndexReader ir : allSubReaders ) {
        Assert.assertTrue( filter.visitedReaders.contains( ir ) );
      }
      for ( IndexReader ir : filter.visitedReaders ) {
        Assert.assertTrue( allSubReaders.contains( ir ) );
      }
    }
    finally {
      searchFactory.getIndexReaderAccessor().close( currentIndexReader );
    }
  }

  public static List<IndexReader> getSubIndexReaders(MultiReader compositeReader) {
    CompositeReaderContext compositeReaderContext = compositeReader.getContext();
    ArrayList<IndexReader> segmentReaders = new ArrayList<IndexReader>( 20 );

    for ( AtomicReaderContext readerContext : compositeReaderContext.leaves() ) {
      segmentReaders.add( readerContext.reader() );
    }

    return segmentReaders;
  }

  /**
   * Filters are invoked once for each segment, so they are invoked multiple times
   * once for each segment, each time being passed a different IndexReader.
   * These IndexReader instances are "subreaders", not the global kind representing
   * the whole index.
   */
  private static class RecordingFilter extends Filter {

    final List<IndexReader> visitedReaders = new ArrayList<IndexReader>();
    final List<String> seenTerms = new ArrayList<String>();
    final String fieldName;

    public RecordingFilter(String fieldName) {
      this.fieldName = fieldName;
    }

    @Override
    public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
      final AtomicReader reader = context.reader();
      this.visitedReaders.add( reader );
      OpenBitSet bitSet = new OpenBitSet( reader.maxDoc() );
      for ( int i = 0; i < reader.maxDoc(); i++ ) {
        bitSet.fastSet( i );
      }
      Terms terms = reader.terms( fieldName );
      TermsEnum iterator = terms.iterator( null );
      BytesRef next = iterator.next();
      while ( next != null ) {
        seenTerms.add( next.utf8ToString() );
        next = iterator.next();
      }
      return BitsFilteredDocIdSet.wrap( bitSet, acceptDocs );
    }
  }

  @Indexed(index = "guests")
  public static final class Guest {

    private long id;
    private String name;

    @DocumentId
    public long getId() {
      return id;
    }

    public void setId(long id) {
      this.id = id;
    }

    @Field
    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

}
TOP

Related Classes of org.hibernate.search.test.filters.FreshReadersProvidedTest$Guest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.