All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.search.MultiValueMode Maven / Gradle / Ivy

There is a newer version: 8.14.1
Show newest version
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.elasticsearch.search;

import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.RandomAccessOrds;
import org.apache.lucene.index.SortedDocValues;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.index.fielddata.FieldData;
import org.elasticsearch.index.fielddata.NumericDoubleValues;
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;

import java.io.IOException;
import java.util.Locale;

/**
 * Defines what values to pick in the case a document contains multiple values for a particular field.
 */
public enum MultiValueMode implements Writeable {

    /**
     * Pick the sum of all the values.
     */
    SUM {
        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            if (count > 0) {
                long total = 0;
                for (int index = 0; index < count; ++index) {
                    total += values.valueAt(index);
                }
                return total;
            } else {
                return missingValue;
            }
        }

        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                long totalValue = 0;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    for (int index = 0; index < count; ++index) {
                        totalValue += values.valueAt(index);
                    }
                    totalCount += count;
                }
                return totalCount > 0 ? totalValue : missingValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            if (count > 0) {
                double total = 0;
                for (int index = 0; index < count; ++index) {
                    total += values.valueAt(index);
                }
                return total;
            } else {
                return missingValue;
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                double totalValue = 0;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    for (int index = 0; index < count; ++index) {
                        totalValue += values.valueAt(index);
                    }
                    totalCount += count;
                }
                return totalCount > 0 ? totalValue : missingValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(UnsortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            if (count > 0) {
                double total = 0;
                for (int index = 0; index < count; ++index) {
                    total += values.valueAt(index);
                }
                return total;
            } else {
                return missingValue;
            }
        }
    },

    /**
     * Pick the average of all the values.
     */
    AVG {
        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            if (count > 0) {
                long total = 0;
                for (int index = 0; index < count; ++index) {
                    total += values.valueAt(index);
                }
                return count > 1 ? Math.round((double)total/(double)count) : total;
            } else {
                return missingValue;
            }
        }

        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                long totalValue = 0;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    for (int index = 0; index < count; ++index) {
                        totalValue += values.valueAt(index);
                    }
                    totalCount += count;
                }
                if (totalCount < 1) {
                    return missingValue;
                }
                return totalCount > 1 ? Math.round((double)totalValue/(double)totalCount) : totalValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            if (count > 0) {
                double total = 0;
                for (int index = 0; index < count; ++index) {
                    total += values.valueAt(index);
                }
                return total/count;
            } else {
                return missingValue;
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                double totalValue = 0;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    for (int index = 0; index < count; ++index) {
                        totalValue += values.valueAt(index);
                    }
                    totalCount += count;
                }
                if (totalCount < 1) {
                    return missingValue;
                }
                return totalValue/totalCount;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(UnsortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            if (count > 0) {
                double total = 0;
                for (int index = 0; index < count; ++index) {
                    total += values.valueAt(index);
                }
                return total/count;
            } else {
                return missingValue;
            }
        }
    },

    /**
     * Pick the median of the values.
     */
    MEDIAN {
        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, int doc) {
            values.setDocument(doc);
            int count = values.count();
            if (count > 0) {
                if (count % 2 == 0) {
                    count /= 2;
                    return Math.round((values.valueAt(count - 1) + values.valueAt(count))/2.0);
                } else {
                    count /= 2;
                    return values.valueAt(count);
                }
            } else {
                return missingValue;
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            int count = values.count();
            if (count > 0) {
                if (count % 2 == 0) {
                    count /= 2;
                    return (values.valueAt(count - 1) + values.valueAt(count))/2.0;
                } else {
                    count /= 2;
                    return values.valueAt(count);
                }
            } else {
                return missingValue;
            }
        }
    },

    /**
     * Pick the lowest value.
     */
    MIN {
        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            return count > 0 ? values.valueAt(0) : missingValue;
        }

        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                long minValue = Long.MAX_VALUE;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    if (count > 0) {
                        minValue = Math.min(minValue, values.valueAt(0));
                    }
                    totalCount += count;
                }
                return totalCount > 0 ? minValue : missingValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            int count = values.count();
            return count > 0 ? values.valueAt(0) : missingValue;
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                double minValue = Double.MAX_VALUE;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    if (count > 0) {
                        minValue = Math.min(minValue, values.valueAt(0));
                    }
                    totalCount += count;
                }
                return totalCount > 0 ? minValue : missingValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected BytesRef pick(SortedBinaryDocValues values, BytesRef missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            return count > 0 ? values.valueAt(0) : missingValue;
        }

        @Override
        protected BytesRef pick(BinaryDocValues values, BytesRefBuilder builder, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                BytesRefBuilder value = null;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    final BytesRef innerValue = values.get(doc);
                    if (innerValue != null) {
                        if (value == null) {
                            builder.copyBytes(innerValue);
                            value = builder;
                        } else {
                            final BytesRef min = value.get().compareTo(innerValue) <= 0 ? value.get() : innerValue;
                            if (min == innerValue) {
                                value.copyBytes(min);
                            }
                        }
                    }
                }
                return value == null ? null : value.get();
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected int pick(RandomAccessOrds values, int doc) {
            values.setDocument(doc);
            return values.cardinality() > 0 ? (int)values.ordAt(0) : -1;
        }

        @Override
        protected int pick(SortedDocValues values, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int ord = -1;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    final int innerOrd = values.getOrd(doc);
                    if (innerOrd != -1) {
                        ord = ord == -1  ? innerOrd : Math.min(ord, innerOrd);
                    }
                }
                return ord;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(UnsortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            int count = values.count();
            double min = Double.MAX_VALUE;
            for (int index = 0; index < count; ++index) {
                min = Math.min(values.valueAt(index), min);
            }
            return count > 0 ? min : missingValue;
        }
    },

    /**
     * Pick the highest value.
     */
    MAX {
        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            return count > 0 ? values.valueAt(count - 1) : missingValue;
        }

        @Override
        protected long pick(SortedNumericDocValues values, long missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                long maxValue = Long.MIN_VALUE;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    if (count > 0) {
                        maxValue = Math.max(maxValue, values.valueAt(count - 1));
                    }
                    totalCount += count;
                }
                return totalCount > 0 ? maxValue : missingValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            return count > 0 ? values.valueAt(count - 1) : missingValue;
        }

        @Override
        protected double pick(SortedNumericDoubleValues values, double missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int totalCount = 0;
                double maxValue = Double.MIN_VALUE;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    values.setDocument(doc);
                    final int count = values.count();
                    if (count > 0) {
                        maxValue = Math.max(maxValue, values.valueAt(count - 1));
                    }
                    totalCount += count;
                }
                return totalCount > 0 ? maxValue : missingValue;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected BytesRef pick(SortedBinaryDocValues values, BytesRef missingValue, int doc) {
            values.setDocument(doc);
            final int count = values.count();
            return count > 0 ? values.valueAt(count - 1) : missingValue;
        }

        @Override
        protected BytesRef pick(BinaryDocValues values, BytesRefBuilder builder, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                BytesRefBuilder value = null;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    final BytesRef innerValue = values.get(doc);
                    if (innerValue != null) {
                        if (value == null) {
                            builder.copyBytes(innerValue);
                            value = builder;
                        } else {
                            final BytesRef max = value.get().compareTo(innerValue) > 0 ? value.get() : innerValue;
                            if (max == innerValue) {
                                value.copyBytes(max);
                            }
                        }
                    }
                }
                return value == null ? null : value.get();
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected int pick(RandomAccessOrds values, int doc) {
            values.setDocument(doc);
            final int count = values.cardinality();
            return count > 0 ? (int)values.ordAt(count - 1) : -1;
        }

        @Override
        protected int pick(SortedDocValues values, DocIdSetIterator docItr, int startDoc, int endDoc) {
            try {
                int ord = -1;
                for (int doc = startDoc; doc < endDoc; doc = docItr.nextDoc()) {
                    final int innerOrd = values.getOrd(doc);
                    if (innerOrd != -1) {
                        ord = Math.max(ord, innerOrd);
                    }
                }
                return ord;
            } catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }

        @Override
        protected double pick(UnsortedNumericDoubleValues values, double missingValue, int doc) {
            values.setDocument(doc);
            int count = values.count();
            double max = Double.MIN_VALUE;
            for (int index = 0; index < count; ++index) {
                max = Math.max(values.valueAt(index), max);
            }
            return count > 0 ? max : missingValue;
        }
    };

    /**
     * A case insensitive version of {@link #valueOf(String)}
     *
     * @throws IllegalArgumentException if the given string doesn't match a sort mode or is null.
     */
    public static MultiValueMode fromString(String sortMode) {
        try {
            return valueOf(sortMode.toUpperCase(Locale.ROOT));
        } catch (Exception e) {
            throw new IllegalArgumentException("Illegal sort mode: " + sortMode);
        }
    }

    /**
     * Return a {@link NumericDocValues} instance that can be used to sort documents
     * with this mode and the provided values. When a document has no value,
     * missingValue is returned.
     *
     * Allowed Modes: SUM, AVG, MEDIAN, MIN, MAX
     */
    public NumericDocValues select(final SortedNumericDocValues values, final long missingValue) {
        final NumericDocValues singleton = DocValues.unwrapSingleton(values);
        if (singleton != null) {
            final Bits docsWithField = DocValues.unwrapSingletonBits(values);
            if (docsWithField == null || missingValue == 0) {
                return singleton;
            } else {
                return new NumericDocValues() {
                    @Override
                    public long get(int docID) {
                        final long value = singleton.get(docID);
                        if (value == 0 && docsWithField.get(docID) == false) {
                            return missingValue;
                        }
                        return value;
                    }
                };
            }
        } else {
            return new NumericDocValues() {
                @Override
                public long get(int docID) {
                    return pick(values, missingValue, docID);
                }
            };
        }
    }

    protected long pick(SortedNumericDocValues values, long missingValue, int doc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link NumericDocValues} instance that can be used to sort root documents
     * with this mode, the provided values and filters for root/inner documents.
     *
     * For every root document, the values of its inner documents will be aggregated.
     * If none of the inner documents has a value, then missingValue is returned.
     *
     * Allowed Modes: SUM, AVG, MIN, MAX
     *
     * NOTE: Calling the returned instance on docs that are not root docs is illegal
     *       The returned instance can only be evaluate the current and upcoming docs
     */
    public NumericDocValues select(final SortedNumericDocValues values, final long missingValue, final BitSet rootDocs, final DocIdSetIterator innerDocs, int maxDoc) throws IOException {
        if (rootDocs == null || innerDocs == null) {
            return select(DocValues.emptySortedNumeric(maxDoc), missingValue);
        }

        return new NumericDocValues() {

            int lastSeenRootDoc = 0;
            long lastEmittedValue = missingValue;

            @Override
            public long get(int rootDoc) {
                assert rootDocs.get(rootDoc) : "can only sort root documents";
                assert rootDoc >= lastSeenRootDoc : "can only evaluate current and upcoming root docs";
                if (rootDoc == lastSeenRootDoc) {
                    return lastEmittedValue;
                }
                try {
                    final int prevRootDoc = rootDocs.prevSetBit(rootDoc - 1);
                    final int firstNestedDoc;
                    if (innerDocs.docID() > prevRootDoc) {
                        firstNestedDoc = innerDocs.docID();
                    } else {
                        firstNestedDoc = innerDocs.advance(prevRootDoc + 1);
                    }

                    lastSeenRootDoc = rootDoc;
                    lastEmittedValue = pick(values, missingValue, innerDocs, firstNestedDoc, rootDoc);
                    return lastEmittedValue;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    protected long pick(SortedNumericDocValues values, long missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link NumericDoubleValues} instance that can be used to sort documents
     * with this mode and the provided values. When a document has no value,
     * missingValue is returned.
     *
     * Allowed Modes: SUM, AVG, MEDIAN, MIN, MAX
     */
    public NumericDoubleValues select(final SortedNumericDoubleValues values, final double missingValue) {
        final NumericDoubleValues singleton = FieldData.unwrapSingleton(values);
        if (singleton != null) {
            final Bits docsWithField = FieldData.unwrapSingletonBits(values);
            if (docsWithField == null || missingValue == 0) {
                return singleton;
            } else {
                return new NumericDoubleValues() {
                    @Override
                    public double get(int docID) {
                        final double value = singleton.get(docID);
                        if (value == 0 && docsWithField.get(docID) == false) {
                            return missingValue;
                        }
                        return value;
                    }
                };
            }
        } else {
            return new NumericDoubleValues() {
                @Override
                public double get(int docID) {
                    return pick(values, missingValue, docID);
                }
            };
        }
    }

    protected double pick(SortedNumericDoubleValues values, double missingValue, int doc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link NumericDoubleValues} instance that can be used to sort root documents
     * with this mode, the provided values and filters for root/inner documents.
     *
     * For every root document, the values of its inner documents will be aggregated.
     * If none of the inner documents has a value, then missingValue is returned.
     *
     * Allowed Modes: SUM, AVG, MIN, MAX
     *
     * NOTE: Calling the returned instance on docs that are not root docs is illegal
     *       The returned instance can only be evaluate the current and upcoming docs
     */
    public NumericDoubleValues select(final SortedNumericDoubleValues values, final double missingValue, final BitSet rootDocs, final DocIdSetIterator innerDocs, int maxDoc) throws IOException {
        if (rootDocs == null || innerDocs == null) {
            return select(FieldData.emptySortedNumericDoubles(maxDoc), missingValue);
        }

        return new NumericDoubleValues() {

            int lastSeenRootDoc = 0;
            double lastEmittedValue = missingValue;

            @Override
            public double get(int rootDoc) {
                assert rootDocs.get(rootDoc) : "can only sort root documents";
                assert rootDoc >= lastSeenRootDoc : "can only evaluate current and upcoming root docs";
                if (rootDoc == lastSeenRootDoc) {
                    return lastEmittedValue;
                }
                try {
                    final int prevRootDoc = rootDocs.prevSetBit(rootDoc - 1);
                    final int firstNestedDoc;
                    if (innerDocs.docID() > prevRootDoc) {
                        firstNestedDoc = innerDocs.docID();
                    } else {
                        firstNestedDoc = innerDocs.advance(prevRootDoc + 1);
                    }

                    lastSeenRootDoc = rootDoc;
                    lastEmittedValue = pick(values, missingValue, innerDocs, firstNestedDoc, rootDoc);
                    return lastEmittedValue;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    protected double pick(SortedNumericDoubleValues values, double missingValue, DocIdSetIterator docItr, int startDoc, int endDoc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link BinaryDocValues} instance that can be used to sort documents
     * with this mode and the provided values. When a document has no value,
     * missingValue is returned.
     *
     * Allowed Modes: MIN, MAX
     */
    public BinaryDocValues select(final SortedBinaryDocValues values, final BytesRef missingValue) {
        final BinaryDocValues singleton = FieldData.unwrapSingleton(values);
        if (singleton != null) {
            final Bits docsWithField = FieldData.unwrapSingletonBits(values);
            if (docsWithField == null) {
                return singleton;
            } else {
                return new BinaryDocValues() {
                    @Override
                    public BytesRef get(int docID) {
                        final BytesRef value = singleton.get(docID);
                        if (value.length == 0 && docsWithField.get(docID) == false) {
                            return missingValue;
                        }
                        return value;
                    }
                };
            }
        } else {
            return new BinaryDocValues() {
                @Override
                public BytesRef get(int docID) {
                    return pick(values, missingValue, docID);
                }
            };
        }
    }

    protected BytesRef pick(SortedBinaryDocValues values, BytesRef missingValue, int doc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link BinaryDocValues} instance that can be used to sort root documents
     * with this mode, the provided values and filters for root/inner documents.
     *
     * For every root document, the values of its inner documents will be aggregated.
     * If none of the inner documents has a value, then missingValue is returned.
     *
     * Allowed Modes: MIN, MAX
     *
     * NOTE: Calling the returned instance on docs that are not root docs is illegal
     *       The returned instance can only be evaluate the current and upcoming docs
     */
    public BinaryDocValues select(final SortedBinaryDocValues values, final BytesRef missingValue, final BitSet rootDocs, final DocIdSetIterator innerDocs, int maxDoc) throws IOException {
        if (rootDocs == null || innerDocs == null) {
            return select(FieldData.emptySortedBinary(maxDoc), missingValue);
        }
        final BinaryDocValues selectedValues = select(values, null);

        return new BinaryDocValues() {

            final BytesRefBuilder builder = new BytesRefBuilder();

            int lastSeenRootDoc = 0;
            BytesRef lastEmittedValue = missingValue;

            @Override
            public BytesRef get(int rootDoc) {
                assert rootDocs.get(rootDoc) : "can only sort root documents";
                assert rootDoc >= lastSeenRootDoc : "can only evaluate current and upcoming root docs";
                if (rootDoc == lastSeenRootDoc) {
                    return lastEmittedValue;
                }

                try {
                    final int prevRootDoc = rootDocs.prevSetBit(rootDoc - 1);
                    final int firstNestedDoc;
                    if (innerDocs.docID() > prevRootDoc) {
                        firstNestedDoc = innerDocs.docID();
                    } else {
                        firstNestedDoc = innerDocs.advance(prevRootDoc + 1);
                    }

                    lastSeenRootDoc = rootDoc;
                    lastEmittedValue = pick(selectedValues, builder, innerDocs, firstNestedDoc, rootDoc);
                    if (lastEmittedValue == null) {
                        lastEmittedValue = missingValue;
                    }
                    return lastEmittedValue;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    protected BytesRef pick(BinaryDocValues values, BytesRefBuilder builder, DocIdSetIterator docItr, int startDoc, int endDoc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link SortedDocValues} instance that can be used to sort documents
     * with this mode and the provided values.
     *
     * Allowed Modes: MIN, MAX
     */
    public SortedDocValues select(final RandomAccessOrds values) {
        if (values.getValueCount() >= Integer.MAX_VALUE) {
            throw new UnsupportedOperationException("fields containing more than " + (Integer.MAX_VALUE-1) + " unique terms are unsupported");
        }

        final SortedDocValues singleton = DocValues.unwrapSingleton(values);
        if (singleton != null) {
            return singleton;
        } else {
            return new SortedDocValues() {
                @Override
                public int getOrd(int docID) {
                    return pick(values, docID);
                }

                @Override
                public BytesRef lookupOrd(int ord) {
                    return values.lookupOrd(ord);
                }

                @Override
                public int getValueCount() {
                    return (int)values.getValueCount();
                }
            };
        }
    }

    protected int pick(RandomAccessOrds values, int doc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link SortedDocValues} instance that can be used to sort root documents
     * with this mode, the provided values and filters for root/inner documents.
     *
     * For every root document, the values of its inner documents will be aggregated.
     *
     * Allowed Modes: MIN, MAX
     *
     * NOTE: Calling the returned instance on docs that are not root docs is illegal
     *       The returned instance can only be evaluate the current and upcoming docs
     */
    public SortedDocValues select(final RandomAccessOrds values, final BitSet rootDocs, final DocIdSetIterator innerDocs) throws IOException {
        if (rootDocs == null || innerDocs == null) {
            return select(DocValues.emptySortedSet());
        }
        final SortedDocValues selectedValues = select(values);

        return new SortedDocValues() {

            int lastSeenRootDoc = 0;
            int lastEmittedOrd = -1;

            @Override
            public BytesRef lookupOrd(int ord) {
                return selectedValues.lookupOrd(ord);
            }

            @Override
            public int getValueCount() {
                return selectedValues.getValueCount();
            }

            @Override
            public int getOrd(int rootDoc) {
                assert rootDocs.get(rootDoc) : "can only sort root documents";
                assert rootDoc >= lastSeenRootDoc : "can only evaluate current and upcoming root docs";
                if (rootDoc == lastSeenRootDoc) {
                    return lastEmittedOrd;
                }

                try {
                    final int prevRootDoc = rootDocs.prevSetBit(rootDoc - 1);
                    final int firstNestedDoc;
                    if (innerDocs.docID() > prevRootDoc) {
                        firstNestedDoc = innerDocs.docID();
                    } else {
                        firstNestedDoc = innerDocs.advance(prevRootDoc + 1);
                    }

                    lastSeenRootDoc = rootDoc;
                    return lastEmittedOrd = pick(selectedValues, innerDocs, firstNestedDoc, rootDoc);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    protected int pick(SortedDocValues values, DocIdSetIterator docItr, int startDoc, int endDoc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Return a {@link NumericDoubleValues} instance that can be used to sort documents
     * with this mode and the provided values. When a document has no value,
     * missingValue is returned.
     *
     * Allowed Modes: SUM, AVG, MIN, MAX
     */
    public NumericDoubleValues select(final UnsortedNumericDoubleValues values, final double missingValue) {
        return new NumericDoubleValues() {
            @Override
            public double get(int docID) {
                return pick(values, missingValue, docID);
            }
        };
    }

    protected double pick(UnsortedNumericDoubleValues values, final double missingValue, int doc) {
        throw new IllegalArgumentException("Unsupported sort mode: " + this);
    }

    /**
     * Interface allowing custom value generators to be used in MultiValueMode.
     */
    public interface UnsortedNumericDoubleValues {
        int count();
        void setDocument(int docId);
        double valueAt(int index);
    }

    @Override
    public void writeTo(StreamOutput out) throws IOException {
        out.writeVInt(this.ordinal());
    }

    public static MultiValueMode readMultiValueModeFrom(StreamInput in) throws IOException {
        int ordinal = in.readVInt();
        if (ordinal < 0 || ordinal >= values().length) {
            throw new IOException("Unknown MultiValueMode ordinal [" + ordinal + "]");
        }
        return values()[ordinal];
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy