Java tutorial
/* * 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.reader.impl; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.uninverting.UninvertingReader; import org.apache.lucene.uninverting.UninvertingReader.Type; import org.hibernate.search.indexes.spi.IndexManager; import org.hibernate.search.indexes.spi.ReaderProvider; import org.hibernate.search.query.engine.impl.SortConfigurations; import org.hibernate.search.query.engine.impl.SortConfigurations.SortConfiguration; import org.hibernate.search.util.StringHelper; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; /** * Wraps a MultiReader to keep references to owning managers. * * @author Emmanuel Bernard * @author Sanne Grinovero (C) 2011 Red Hat Inc. */ public class ManagedMultiReader extends MultiReader { private static final Log log = LoggerFactory.make(); /** * The index readers to be closed in {@link #doClose()}. Will be the readers originally given upon instantiation in * case it was required to wrap those with {@link UninvertingReader}s for the purposes of sorting prior to passing * them to super. */ final IndexReader[] readersForClosing; final ReaderProvider[] readerProviders; private ManagedMultiReader(IndexReader[] subReaders, IndexReader[] readersForClosing, ReaderProvider[] readerProviders) throws IOException { // If this flag isn't set to true, the MultiReader will increase the usage counter! super(subReaders, true); assert readersForClosing.length == readerProviders.length; this.readersForClosing = readersForClosing; this.readerProviders = readerProviders; } static ManagedMultiReader createInstance(IndexManager[] indexManagers, SortConfigurations configuredSorts, Sort sort, boolean indexUninvertingAllowed) throws IOException { final int length = indexManagers.length; IndexReader[] subReaders = new IndexReader[length]; ReaderProvider[] readerProviders = new ReaderProvider[length]; for (int index = 0; index < length; index++) { ReaderProvider indexReaderManager = indexManagers[index].getReaderProvider(); IndexReader openIndexReader = indexReaderManager.openIndexReader(); subReaders[index] = openIndexReader; readerProviders[index] = indexReaderManager; } IndexReader[] effectiveReaders = getEffectiveReaders(indexManagers, subReaders, configuredSorts, sort, indexUninvertingAllowed); return new ManagedMultiReader(effectiveReaders, subReaders, readerProviders); } /** * Gets the readers to be effectively used. A given reader will be returned itself if: * <ul> * <li>there is no sort involved.</li> * <li>doc value fields for all requested sort fields are contained in the index, for each entity type mapped to the * index</li> * </ul> * Otherwise the directory reader will be wrapped in a {@link UninvertingReader} configured in a way to satisfy the * requested sorts. */ private static IndexReader[] getEffectiveReaders(IndexManager[] indexManagers, IndexReader[] subReaders, SortConfigurations configuredSorts, Sort sort, boolean indexUninvertingAllowed) { if (sort == null || sort.getSort().length == 0) { return subReaders; } Set<String> indexesToBeUninverted = getIndexesToBeUninverted(configuredSorts, sort, indexUninvertingAllowed); Map<String, Type> mappings = indexesToBeUninverted.isEmpty() ? Collections.<String, Type>emptyMap() : getMappings(sort); IndexReader[] effectiveReaders = new IndexReader[subReaders.length]; int i = 0; for (IndexReader reader : subReaders) { // take incoming reader as is if (!indexesToBeUninverted.contains(indexManagers[i].getIndexName())) { effectiveReaders[i] = reader; } // wrap with uninverting reader else { if (reader instanceof DirectoryReader) { DirectoryReader directoryReader = (DirectoryReader) reader; try { effectiveReaders[i] = UninvertingReader.wrap(directoryReader, mappings); } catch (IOException e) { throw log.couldNotCreateUninvertingReader(directoryReader, e); } } else { log.readerTypeUnsupportedForInverting(reader.getClass()); effectiveReaders[i] = reader; } } i++; } return effectiveReaders; } /** * Checks for each involved entity type whether it maps all the required sortable fields; If not, it marks the index * for uninverting. */ private static Set<String> getIndexesToBeUninverted(SortConfigurations configuredSorts, Sort sort, boolean indexUninvertingAllowed) { Set<String> indexesToBeUninverted = new HashSet<>(); for (SortConfiguration sortConfiguration : configuredSorts) { boolean foundEntityWithAllRequiredSorts = false; boolean foundEntityWithMissingSorts = false; for (Class<?> entityType : sortConfiguration.getEntityTypes()) { List<String> uncoveredSorts = sortConfiguration.getUncoveredSorts(entityType, sort); if (!uncoveredSorts.isEmpty()) { indexesToBeUninverted.add(sortConfiguration.getIndexName()); if (indexUninvertingAllowed) { log.uncoveredSortsRequested(entityType, sortConfiguration.getIndexName(), StringHelper.join(uncoveredSorts, ", ")); } else { throw log.uncoveredSortsRequestedWithUninvertingNotAllowed(entityType, sortConfiguration.getIndexName(), StringHelper.join(uncoveredSorts, ", ")); } foundEntityWithMissingSorts = true; } else { foundEntityWithAllRequiredSorts = true; } } if (foundEntityWithAllRequiredSorts && foundEntityWithMissingSorts) { throw log.inconsistentSortableFieldConfigurationForSharedIndex(sortConfiguration.getIndexName(), StringHelper.join(sort.getSort(), ", ")); } } return indexesToBeUninverted; } /** * Returns the uninverting reader mappings required for the given non-null sort. */ private static Map<String, UninvertingReader.Type> getMappings(Sort sort) { Map<String, UninvertingReader.Type> mappings = new HashMap<>(); for (SortField sortField : sort.getSort()) { if (sortField.getField() != null) { switch (sortField.getType()) { case INT: mappings.put(sortField.getField(), Type.INTEGER); break; case LONG: mappings.put(sortField.getField(), Type.LONG); break; case FLOAT: mappings.put(sortField.getField(), Type.FLOAT); break; case DOUBLE: mappings.put(sortField.getField(), Type.DOUBLE); break; case STRING: case STRING_VAL: mappings.put(sortField.getField(), Type.SORTED); break; case BYTES: mappings.put(sortField.getField(), Type.BINARY); break; case CUSTOM: // Nothing to do; expecting doc value fields created by the user break; default: log.sortFieldTypeUnsupported(sortField.getField(), sortField.getType()); } } } return mappings; } @Override protected synchronized void doClose() throws IOException { /** * Important: we don't really close the sub readers but we delegate to the * close method of the managing ReaderProvider, which might reuse the same * IndexReader. */ final boolean debugEnabled = log.isDebugEnabled(); if (debugEnabled) { log.debugf("Closing MultiReader: %s", this); } for (int i = 0; i < readersForClosing.length; i++) { ReaderProvider container = readerProviders[i]; container.closeIndexReader(readersForClosing[i]); // might be virtual } if (debugEnabled) { log.trace("MultiReader closed."); } } // Exposed only for testing public List<? extends IndexReader> getSubReaders() { return getSequentialSubReaders(); } @Override public String toString() { return ManagedMultiReader.class.getSimpleName() + " [readersForClosing=" + Arrays.toString(readersForClosing) + ", readerProviders=" + Arrays.toString(readerProviders) + "]"; } }