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

org.opensearch.index.IndexSortConfig Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

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

/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.index;

import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.SortedNumericSortField;
import org.apache.lucene.search.SortedSetSortField;
import org.opensearch.common.annotation.PublicApi;
import org.opensearch.common.settings.Setting;
import org.opensearch.common.settings.Settings;
import org.opensearch.index.fielddata.IndexFieldData;
import org.opensearch.index.mapper.MappedFieldType;
import org.opensearch.search.MultiValueMode;
import org.opensearch.search.lookup.SearchLookup;
import org.opensearch.search.sort.SortOrder;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Holds all the information that is used to build the sort order of an index.
 * 

* The index sort settings are final and can be defined only at index creation. * These settings are divided in four lists that are merged during the initialization of this class: *

    *
  • `index.sort.field`: the field or a list of field to use for the sort
  • *
  • `index.sort.order` the {@link SortOrder} to use for the field or a list of {@link SortOrder} * for each field defined in `index.sort.field`. *
  • *
  • `index.sort.mode`: the {@link MultiValueMode} to use for the field or a list of orders * for each field defined in `index.sort.field`. *
  • *
  • `index.sort.missing`: the missing value to use for the field or a list of missing values * for each field defined in `index.sort.field` *
  • *
* * * @opensearch.api */ @PublicApi(since = "1.0.0") public final class IndexSortConfig { /** * The list of field names */ public static final Setting> INDEX_SORT_FIELD_SETTING = Setting.listSetting( "index.sort.field", Collections.emptyList(), Function.identity(), Setting.Property.IndexScope, Setting.Property.Final ); /** * The {@link SortOrder} for each specified sort field (ie. asc or desc). */ public static final Setting> INDEX_SORT_ORDER_SETTING = Setting.listSetting( "index.sort.order", Collections.emptyList(), IndexSortConfig::parseOrderMode, Setting.Property.IndexScope, Setting.Property.Final ); /** * The {@link MultiValueMode} for each specified sort field (ie. max or min). */ public static final Setting> INDEX_SORT_MODE_SETTING = Setting.listSetting( "index.sort.mode", Collections.emptyList(), IndexSortConfig::parseMultiValueMode, Setting.Property.IndexScope, Setting.Property.Final ); /** * The missing value for each specified sort field (ie. _first or _last) */ public static final Setting> INDEX_SORT_MISSING_SETTING = Setting.listSetting( "index.sort.missing", Collections.emptyList(), IndexSortConfig::validateMissingValue, Setting.Property.IndexScope, Setting.Property.Final ); private static String validateMissingValue(String missing) { if ("_last".equals(missing) == false && "_first".equals(missing) == false) { throw new IllegalArgumentException("Illegal missing value:[" + missing + "], " + "must be one of [_last, _first]"); } return missing; } private static SortOrder parseOrderMode(String value) { try { return SortOrder.fromString(value); } catch (Exception e) { throw new IllegalArgumentException("Illegal sort order:" + value); } } private static MultiValueMode parseMultiValueMode(String value) { MultiValueMode mode = MultiValueMode.fromString(value); if (mode != MultiValueMode.MAX && mode != MultiValueMode.MIN) { throw new IllegalArgumentException( "Illegal index sort mode:[" + mode + "], " + "must be one of [" + MultiValueMode.MAX + ", " + MultiValueMode.MIN + "]" ); } return mode; } // visible for tests final FieldSortSpec[] sortSpecs; public IndexSortConfig(IndexSettings indexSettings) { final Settings settings = indexSettings.getSettings(); List fields = INDEX_SORT_FIELD_SETTING.get(settings); this.sortSpecs = fields.stream().map((name) -> new FieldSortSpec(name)).toArray(FieldSortSpec[]::new); if (INDEX_SORT_ORDER_SETTING.exists(settings)) { List orders = INDEX_SORT_ORDER_SETTING.get(settings); if (orders.size() != sortSpecs.length) { throw new IllegalArgumentException( "index.sort.field:" + fields + " index.sort.order:" + orders.toString() + ", size mismatch" ); } for (int i = 0; i < sortSpecs.length; i++) { sortSpecs[i].order = orders.get(i); } } if (INDEX_SORT_MODE_SETTING.exists(settings)) { List modes = INDEX_SORT_MODE_SETTING.get(settings); if (modes.size() != sortSpecs.length) { throw new IllegalArgumentException("index.sort.field:" + fields + " index.sort.mode:" + modes + ", size mismatch"); } for (int i = 0; i < sortSpecs.length; i++) { sortSpecs[i].mode = modes.get(i); } } if (INDEX_SORT_MISSING_SETTING.exists(settings)) { List missingValues = INDEX_SORT_MISSING_SETTING.get(settings); if (missingValues.size() != sortSpecs.length) { throw new IllegalArgumentException( "index.sort.field:" + fields + " index.sort.missing:" + missingValues + ", size mismatch" ); } for (int i = 0; i < sortSpecs.length; i++) { sortSpecs[i].missingValue = missingValues.get(i); } } } /** * Returns true if the index should be sorted */ public boolean hasIndexSort() { return sortSpecs.length > 0; } public boolean hasPrimarySortOnField(String field) { return sortSpecs.length > 0 && sortSpecs[0].field.equals(field); } /** * Builds the {@link Sort} order from the settings for this index * or returns null if this index has no sort. */ public Sort buildIndexSort( boolean shouldWidenIndexSortType, Function fieldTypeLookup, BiFunction, IndexFieldData> fieldDataLookup ) { if (hasIndexSort() == false) { return null; } final SortField[] sortFields = new SortField[sortSpecs.length]; for (int i = 0; i < sortSpecs.length; i++) { FieldSortSpec sortSpec = sortSpecs[i]; final MappedFieldType ft = fieldTypeLookup.apply(sortSpec.field); if (ft == null) { throw new IllegalArgumentException("unknown index sort field:[" + sortSpec.field + "]"); } boolean reverse = sortSpec.order == null ? false : (sortSpec.order == SortOrder.DESC); MultiValueMode mode = sortSpec.mode; if (mode == null) { mode = reverse ? MultiValueMode.MAX : MultiValueMode.MIN; } IndexFieldData fieldData; try { fieldData = fieldDataLookup.apply(ft, () -> { throw new UnsupportedOperationException("index sorting not supported on runtime field [" + ft.name() + "]"); }); } catch (Exception e) { throw new IllegalArgumentException("docvalues not found for index sort field:[" + sortSpec.field + "]", e); } if (fieldData == null) { throw new IllegalArgumentException("docvalues not found for index sort field:[" + sortSpec.field + "]"); } if (shouldWidenIndexSortType == true) { sortFields[i] = fieldData.wideSortField(sortSpec.missingValue, mode, null, reverse); } else { sortFields[i] = fieldData.sortField(sortSpec.missingValue, mode, null, reverse); } validateIndexSortField(sortFields[i]); } return new Sort(sortFields); } private void validateIndexSortField(SortField sortField) { SortField.Type type = getSortFieldType(sortField); if (ALLOWED_INDEX_SORT_TYPES.contains(type) == false) { throw new IllegalArgumentException("invalid index sort field:[" + sortField.getField() + "]"); } } /** * Field sort specification * * @opensearch.internal */ static class FieldSortSpec { final String field; SortOrder order; MultiValueMode mode; String missingValue; FieldSortSpec(String field) { this.field = field; } } /** We only allow index sorting on these types */ private static final EnumSet ALLOWED_INDEX_SORT_TYPES = EnumSet.of( SortField.Type.STRING, SortField.Type.LONG, SortField.Type.INT, SortField.Type.DOUBLE, SortField.Type.FLOAT ); public static SortField.Type getSortFieldType(SortField sortField) { if (sortField instanceof SortedSetSortField) { return SortField.Type.STRING; } else if (sortField instanceof SortedNumericSortField) { return ((SortedNumericSortField) sortField).getNumericType(); } else { return sortField.getType(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy