org.apache.solr.legacy.LegacyNumericTokenStream.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.legacy.LegacyNumericTokenStream.java

Source

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

import java.util.Objects;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.apache.lucene.util.Attribute;
import org.apache.lucene.util.AttributeFactory;
import org.apache.lucene.util.AttributeImpl;
import org.apache.lucene.util.AttributeReflector;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.NumericUtils;

/**
 * <b>Expert:</b> This class provides a {@link TokenStream}
 * for indexing numeric values that can be used by {@link
 * org.apache.solr.legacy.LegacyNumericRangeQuery}.
 *
 * <p>Note that for simple usage, {@link org.apache.solr.legacy.LegacyIntField}, {@link
 * org.apache.solr.legacy.LegacyLongField}, {@link org.apache.solr.legacy.LegacyFloatField} or {@link org.apache.solr.legacy.LegacyDoubleField} is
 * recommended.  These fields disable norms and
 * term freqs, as they are not usually needed during
 * searching.  If you need to change these settings, you
 * should use this class.
 *
 * <p>Here's an example usage, for an <code>int</code> field:
 *
 * <pre class="prettyprint">
 *  FieldType fieldType = new FieldType(TextField.TYPE_NOT_STORED);
 *  fieldType.setOmitNorms(true);
 *  fieldType.setIndexOptions(IndexOptions.DOCS_ONLY);
 *  Field field = new Field(name, new LegacyNumericTokenStream(precisionStep).setIntValue(value), fieldType);
 *  document.add(field);
 * </pre>
 *
 * <p>For optimal performance, re-use the TokenStream and Field instance
 * for more than one document:
 *
 * <pre class="prettyprint">
 *  LegacyNumericTokenStream stream = new LegacyNumericTokenStream(precisionStep);
 *  FieldType fieldType = new FieldType(TextField.TYPE_NOT_STORED);
 *  fieldType.setOmitNorms(true);
 *  fieldType.setIndexOptions(IndexOptions.DOCS_ONLY);
 *  Field field = new Field(name, stream, fieldType);
 *  Document document = new Document();
 *  document.add(field);
 *
 *  for(all documents) {
 *    stream.setIntValue(value)
 *    writer.addDocument(document);
 *  }
 * </pre>
 *
 * <p>This stream is not intended to be used in analyzers;
 * it's more for iterating the different precisions during
 * indexing a specific numeric value.</p>
    
 * <p><b>NOTE</b>: as token streams are only consumed once
 * the document is added to the index, if you index more
 * than one numeric field, use a separate <code>LegacyNumericTokenStream</code>
 * instance for each.</p>
 *
 * <p>See {@link org.apache.solr.legacy.LegacyNumericRangeQuery} for more details on the
 * <a
 * href="LegacyNumericRangeQuery.html#precisionStepDesc"><code>precisionStep</code></a>
 * parameter as well as how numeric fields work under the hood.</p>
 *
 * @deprecated Please switch to {@link org.apache.lucene.index.PointValues} instead
 *
 * @since 2.9
 */
@Deprecated
public final class LegacyNumericTokenStream extends TokenStream {

    /** The full precision token gets this token type assigned. */
    public static final String TOKEN_TYPE_FULL_PREC = "fullPrecNumeric";

    /** The lower precision tokens gets this token type assigned. */
    public static final String TOKEN_TYPE_LOWER_PREC = "lowerPrecNumeric";

    /** <b>Expert:</b> Use this attribute to get the details of the currently generated token.
     * @lucene.experimental
     * @since 4.0
     */
    public interface LegacyNumericTermAttribute extends Attribute {
        /** Returns current shift value, undefined before first token */
        int getShift();

        /** Returns current token's raw value as {@code long} with all {@link #getShift} applied, undefined before first token */
        long getRawValue();

        /** Returns value size in bits (32 for {@code float}, {@code int}; 64 for {@code double}, {@code long}) */
        int getValueSize();

        /** <em>Don't call this method!</em>
          * @lucene.internal */
        void init(long value, int valSize, int precisionStep, int shift);

        /** <em>Don't call this method!</em>
          * @lucene.internal */
        void setShift(int shift);

        /** <em>Don't call this method!</em>
          * @lucene.internal */
        int incShift();
    }

    // just a wrapper to prevent adding CTA
    private static final class NumericAttributeFactory extends AttributeFactory {
        private final AttributeFactory delegate;

        NumericAttributeFactory(AttributeFactory delegate) {
            this.delegate = delegate;
        }

        @Override
        public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
            if (CharTermAttribute.class.isAssignableFrom(attClass))
                throw new IllegalArgumentException("LegacyNumericTokenStream does not support CharTermAttribute.");
            return delegate.createAttributeInstance(attClass);
        }
    }

    /** Implementation of {@link org.apache.solr.legacy.LegacyNumericTokenStream.LegacyNumericTermAttribute}.
     * @lucene.internal
     * @since 4.0
     */
    public static final class LegacyNumericTermAttributeImpl extends AttributeImpl
            implements LegacyNumericTermAttribute, TermToBytesRefAttribute {
        private long value = 0L;
        private int valueSize = 0, shift = 0, precisionStep = 0;
        private BytesRefBuilder bytes = new BytesRefBuilder();

        /** 
         * Creates, but does not yet initialize this attribute instance
         * @see #init(long, int, int, int)
         */
        public LegacyNumericTermAttributeImpl() {
        }

        @Override
        public BytesRef getBytesRef() {
            assert valueSize == 64 || valueSize == 32;
            if (shift >= valueSize) {
                bytes.clear();
            } else if (valueSize == 64) {
                LegacyNumericUtils.longToPrefixCoded(value, shift, bytes);
            } else {
                LegacyNumericUtils.intToPrefixCoded((int) value, shift, bytes);
            }
            return bytes.get();
        }

        @Override
        public int getShift() {
            return shift;
        }

        @Override
        public void setShift(int shift) {
            this.shift = shift;
        }

        @Override
        public int incShift() {
            return (shift += precisionStep);
        }

        @Override
        public long getRawValue() {
            return value & ~((1L << shift) - 1L);
        }

        @Override
        public int getValueSize() {
            return valueSize;
        }

        @Override
        public void init(long value, int valueSize, int precisionStep, int shift) {
            this.value = value;
            this.valueSize = valueSize;
            this.precisionStep = precisionStep;
            this.shift = shift;
        }

        @Override
        public void clear() {
            // this attribute has no contents to clear!
            // we keep it untouched as it's fully controlled by outer class.
        }

        @Override
        public void reflectWith(AttributeReflector reflector) {
            reflector.reflect(TermToBytesRefAttribute.class, "bytes", getBytesRef());
            reflector.reflect(LegacyNumericTermAttribute.class, "shift", shift);
            reflector.reflect(LegacyNumericTermAttribute.class, "rawValue", getRawValue());
            reflector.reflect(LegacyNumericTermAttribute.class, "valueSize", valueSize);
        }

        @Override
        public void copyTo(AttributeImpl target) {
            final LegacyNumericTermAttribute a = (LegacyNumericTermAttribute) target;
            a.init(value, valueSize, precisionStep, shift);
        }

        @Override
        public LegacyNumericTermAttributeImpl clone() {
            LegacyNumericTermAttributeImpl t = (LegacyNumericTermAttributeImpl) super.clone();
            // Do a deep clone
            t.bytes = new BytesRefBuilder();
            t.bytes.copyBytes(getBytesRef());
            return t;
        }

        @Override
        public int hashCode() {
            return Objects.hash(precisionStep, shift, value, valueSize);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            LegacyNumericTermAttributeImpl other = (LegacyNumericTermAttributeImpl) obj;
            if (precisionStep != other.precisionStep)
                return false;
            if (shift != other.shift)
                return false;
            if (value != other.value)
                return false;
            if (valueSize != other.valueSize)
                return false;
            return true;
        }
    }

    /**
     * Creates a token stream for numeric values using the default <code>precisionStep</code>
     * {@link org.apache.solr.legacy.LegacyNumericUtils#PRECISION_STEP_DEFAULT} (16). The stream is not yet initialized,
     * before using set a value using the various set<em>???</em>Value() methods.
     */
    public LegacyNumericTokenStream() {
        this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY, LegacyNumericUtils.PRECISION_STEP_DEFAULT);
    }

    /**
     * Creates a token stream for numeric values with the specified
     * <code>precisionStep</code>. The stream is not yet initialized,
     * before using set a value using the various set<em>???</em>Value() methods.
     */
    public LegacyNumericTokenStream(final int precisionStep) {
        this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY, precisionStep);
    }

    /**
     * Expert: Creates a token stream for numeric values with the specified
     * <code>precisionStep</code> using the given
     * {@link org.apache.lucene.util.AttributeFactory}.
     * The stream is not yet initialized,
     * before using set a value using the various set<em>???</em>Value() methods.
     */
    public LegacyNumericTokenStream(AttributeFactory factory, final int precisionStep) {
        super(new NumericAttributeFactory(factory));
        if (precisionStep < 1)
            throw new IllegalArgumentException("precisionStep must be >=1");
        this.precisionStep = precisionStep;
        numericAtt.setShift(-precisionStep);
    }

    /**
     * Initializes the token stream with the supplied <code>long</code> value.
     * @param value the value, for which this TokenStream should enumerate tokens.
     * @return this instance, because of this you can use it the following way:
     * <code>new Field(name, new LegacyNumericTokenStream(precisionStep).setLongValue(value))</code>
     */
    public LegacyNumericTokenStream setLongValue(final long value) {
        numericAtt.init(value, valSize = 64, precisionStep, -precisionStep);
        return this;
    }

    /**
     * Initializes the token stream with the supplied <code>int</code> value.
     * @param value the value, for which this TokenStream should enumerate tokens.
     * @return this instance, because of this you can use it the following way:
     * <code>new Field(name, new LegacyNumericTokenStream(precisionStep).setIntValue(value))</code>
     */
    public LegacyNumericTokenStream setIntValue(final int value) {
        numericAtt.init(value, valSize = 32, precisionStep, -precisionStep);
        return this;
    }

    /**
     * Initializes the token stream with the supplied <code>double</code> value.
     * @param value the value, for which this TokenStream should enumerate tokens.
     * @return this instance, because of this you can use it the following way:
     * <code>new Field(name, new LegacyNumericTokenStream(precisionStep).setDoubleValue(value))</code>
     */
    public LegacyNumericTokenStream setDoubleValue(final double value) {
        numericAtt.init(NumericUtils.doubleToSortableLong(value), valSize = 64, precisionStep, -precisionStep);
        return this;
    }

    /**
     * Initializes the token stream with the supplied <code>float</code> value.
     * @param value the value, for which this TokenStream should enumerate tokens.
     * @return this instance, because of this you can use it the following way:
     * <code>new Field(name, new LegacyNumericTokenStream(precisionStep).setFloatValue(value))</code>
     */
    public LegacyNumericTokenStream setFloatValue(final float value) {
        numericAtt.init(NumericUtils.floatToSortableInt(value), valSize = 32, precisionStep, -precisionStep);
        return this;
    }

    @Override
    public void reset() {
        if (valSize == 0)
            throw new IllegalStateException("call set???Value() before usage");
        numericAtt.setShift(-precisionStep);
    }

    @Override
    public boolean incrementToken() {
        if (valSize == 0)
            throw new IllegalStateException("call set???Value() before usage");

        // this will only clear all other attributes in this TokenStream
        clearAttributes();

        final int shift = numericAtt.incShift();
        typeAtt.setType((shift == 0) ? TOKEN_TYPE_FULL_PREC : TOKEN_TYPE_LOWER_PREC);
        posIncrAtt.setPositionIncrement((shift == 0) ? 1 : 0);
        return (shift < valSize);
    }

    /** Returns the precision step. */
    public int getPrecisionStep() {
        return precisionStep;
    }

    @Override
    public String toString() {
        // We override default because it can throw cryptic "illegal shift value":
        return getClass().getSimpleName() + "(precisionStep=" + precisionStep + " valueSize="
                + numericAtt.getValueSize() + " shift=" + numericAtt.getShift() + ")";
    }

    // members
    private final LegacyNumericTermAttribute numericAtt = addAttribute(LegacyNumericTermAttribute.class);
    private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class);
    private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);

    private int valSize = 0; // valSize==0 means not initialized
    private final int precisionStep;
}