/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.solr.index; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.lucene.codecs.DocValuesProducer; import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.index.CodecReader; import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.FieldInfo; import org.apache.lucene.index.FieldInfos; import org.apache.lucene.index.FilterCodecReader; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.MergePolicy; import org.apache.lucene.index.NumericDocValues; import org.apache.lucene.index.OneMergeWrappingMergePolicy; import org.apache.lucene.index.SegmentCommitInfo; import org.apache.lucene.index.SortedDocValues; import org.apache.lucene.index.SortedNumericDocValues; import org.apache.lucene.index.SortedSetDocValues; import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.SchemaField; import org.apache.solr.uninverting.UninvertingReader; /** * A merge policy that can detect schema changes and write docvalues into merging segments when a field has docvalues enabled * Using UninvertingReader. * * This merge policy will delegate to the wrapped merge policy for selecting merge segments * */ public class UninvertDocValuesMergePolicyFactory extends WrapperMergePolicyFactory { final private boolean skipIntegrityCheck; /** * Whether or not the wrapped docValues producer should check consistency */ public boolean getSkipIntegrityCheck() { return skipIntegrityCheck; } public UninvertDocValuesMergePolicyFactory(SolrResourceLoader resourceLoader, MergePolicyFactoryArgs args, IndexSchema schema) { super(resourceLoader, args, schema); final Boolean sic = (Boolean)args.remove("skipIntegrityCheck"); if (sic != null) { this.skipIntegrityCheck = sic.booleanValue(); } else { this.skipIntegrityCheck = false; } if (!args.keys().isEmpty()) { throw new IllegalArgumentException("Arguments were "+args+" but "+getClass().getSimpleName()+" takes no arguments."); } } @Override protected MergePolicy getMergePolicyInstance(MergePolicy wrappedMP) { return new OneMergeWrappingMergePolicy(wrappedMP, (merge) -> new UninvertDocValuesOneMerge(merge.segments)); } private UninvertingReader.Type getUninversionType(FieldInfo fi) { SchemaField sf = schema.getFieldOrNull(fi.name); if (null != sf && sf.hasDocValues() && fi.getDocValuesType() == DocValuesType.NONE && fi.getIndexOptions() != IndexOptions.NONE) { return sf.getType().getUninversionType(sf); } else { return null; } } private class UninvertDocValuesOneMerge extends MergePolicy.OneMerge { public UninvertDocValuesOneMerge(List<SegmentCommitInfo> segments) { super(segments); } @Override public CodecReader wrapForMerge(CodecReader reader) throws IOException { // Wrap the reader with an uninverting reader if any of the fields have no docvalues but the // Schema says there should be Map<String,UninvertingReader.Type> uninversionMap = null; for(FieldInfo fi: reader.getFieldInfos()) { final UninvertingReader.Type type = getUninversionType(fi); if (type != null) { if (uninversionMap == null) { uninversionMap = new HashMap<>(); } uninversionMap.put(fi.name, type); } } if(uninversionMap == null) { return reader; // Default to normal reader if nothing to uninvert } else { return new UninvertingFilterCodecReader(reader, uninversionMap); } } } /** * Delegates to an Uninverting for fields with docvalues * * This is going to blow up FieldCache, look into an alternative implementation that uninverts without * fieldcache */ private class UninvertingFilterCodecReader extends FilterCodecReader { private final UninvertingReader uninvertingReader; private final DocValuesProducer docValuesProducer; public UninvertingFilterCodecReader(CodecReader in, Map<String,UninvertingReader.Type> uninversionMap) { super(in); this.uninvertingReader = new UninvertingReader(in, uninversionMap); this.docValuesProducer = new DocValuesProducer() { @Override public NumericDocValues getNumeric(FieldInfo field) throws IOException { return uninvertingReader.getNumericDocValues(field.name); } @Override public BinaryDocValues getBinary(FieldInfo field) throws IOException { return uninvertingReader.getBinaryDocValues(field.name); } @Override public SortedDocValues getSorted(FieldInfo field) throws IOException { return uninvertingReader.getSortedDocValues(field.name); } @Override public SortedNumericDocValues getSortedNumeric(FieldInfo field) throws IOException { return uninvertingReader.getSortedNumericDocValues(field.name); } @Override public SortedSetDocValues getSortedSet(FieldInfo field) throws IOException { return uninvertingReader.getSortedSetDocValues(field.name); } @Override public void checkIntegrity() throws IOException { if (!skipIntegrityCheck) { uninvertingReader.checkIntegrity(); } } @Override public void close() throws IOException { } @Override public long ramBytesUsed() { return 0; } }; } @Override protected void doClose() throws IOException { docValuesProducer.close(); uninvertingReader.close(); super.doClose(); } @Override public DocValuesProducer getDocValuesReader() { return docValuesProducer; } @Override public FieldInfos getFieldInfos() { return uninvertingReader.getFieldInfos(); } @Override public CacheHelper getCoreCacheHelper() { return in.getCoreCacheHelper(); } @Override public CacheHelper getReaderCacheHelper() { return in.getReaderCacheHelper(); } } }