|
@@ -19,13 +19,11 @@
|
|
|
|
|
|
package org.elasticsearch.search.sort;
|
|
|
|
|
|
-import org.apache.logging.log4j.LogManager;
|
|
|
import org.apache.lucene.search.SortField;
|
|
|
import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.common.ParseField;
|
|
|
import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
|
-import org.elasticsearch.common.logging.DeprecationLogger;
|
|
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
|
|
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
|
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
@@ -36,6 +34,7 @@ import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
|
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
|
|
|
import org.elasticsearch.index.fielddata.plain.SortedNumericDVIndexFieldData;
|
|
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
|
|
+import org.elasticsearch.index.mapper.ObjectMapper;
|
|
|
import org.elasticsearch.index.query.QueryBuilder;
|
|
|
import org.elasticsearch.index.query.QueryRewriteContext;
|
|
|
import org.elasticsearch.index.query.QueryShardContext;
|
|
@@ -47,13 +46,13 @@ import java.io.IOException;
|
|
|
import java.util.Locale;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
+import static org.elasticsearch.index.search.NestedHelper.parentObject;
|
|
|
import static org.elasticsearch.search.sort.NestedSortBuilder.NESTED_FIELD;
|
|
|
|
|
|
/**
|
|
|
* A sort builder to sort based on a document field.
|
|
|
*/
|
|
|
public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
- private static final DeprecationLogger deprecationLogger = new DeprecationLogger(LogManager.getLogger(FieldSortBuilder.class));
|
|
|
|
|
|
public static final String NAME = "field_sort";
|
|
|
public static final ParseField MISSING = new ParseField("missing");
|
|
@@ -80,10 +79,6 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
|
|
|
private SortMode sortMode;
|
|
|
|
|
|
- private QueryBuilder nestedFilter;
|
|
|
-
|
|
|
- private String nestedPath;
|
|
|
-
|
|
|
private NestedSortBuilder nestedSort;
|
|
|
|
|
|
/** Copy constructor. */
|
|
@@ -95,8 +90,6 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
if (template.sortMode != null) {
|
|
|
this.sortMode(template.sortMode());
|
|
|
}
|
|
|
- this.setNestedFilter(template.getNestedFilter());
|
|
|
- this.setNestedPath(template.getNestedPath());
|
|
|
if (template.getNestedSort() != null) {
|
|
|
this.setNestedSort(template.getNestedSort());
|
|
|
}
|
|
@@ -121,8 +114,12 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
*/
|
|
|
public FieldSortBuilder(StreamInput in) throws IOException {
|
|
|
fieldName = in.readString();
|
|
|
- nestedFilter = in.readOptionalNamedWriteable(QueryBuilder.class);
|
|
|
- nestedPath = in.readOptionalString();
|
|
|
+ if (in.getVersion().before(Version.V_8_0_0)) {
|
|
|
+ if (in.readOptionalNamedWriteable(QueryBuilder.class) != null || in.readOptionalString() != null) {
|
|
|
+ throw new IOException("the [sort] options [nested_path] and [nested_filter] are removed in 8.x, " +
|
|
|
+ "please use [nested] instead");
|
|
|
+ }
|
|
|
+ }
|
|
|
missing = in.readGenericValue();
|
|
|
order = in.readOptionalWriteable(SortOrder::readFromStream);
|
|
|
sortMode = in.readOptionalWriteable(SortMode::readFromStream);
|
|
@@ -136,8 +133,10 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
@Override
|
|
|
public void writeTo(StreamOutput out) throws IOException {
|
|
|
out.writeString(fieldName);
|
|
|
- out.writeOptionalNamedWriteable(nestedFilter);
|
|
|
- out.writeOptionalString(nestedPath);
|
|
|
+ if (out.getVersion().before(Version.V_8_0_0)) {
|
|
|
+ out.writeOptionalNamedWriteable(null);
|
|
|
+ out.writeOptionalString(null);
|
|
|
+ }
|
|
|
out.writeGenericValue(missing);
|
|
|
out.writeOptionalWriteable(order);
|
|
|
out.writeOptionalWriteable(sortMode);
|
|
@@ -210,58 +209,6 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
return this.sortMode;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Sets the nested filter that the nested objects should match with in order
|
|
|
- * to be taken into account for sorting.
|
|
|
- *
|
|
|
- * @deprecated set nested sort with {@link #setNestedSort(NestedSortBuilder)} and retrieve with {@link #getNestedSort()}
|
|
|
- */
|
|
|
- @Deprecated
|
|
|
- public FieldSortBuilder setNestedFilter(QueryBuilder nestedFilter) {
|
|
|
- if (this.nestedSort != null) {
|
|
|
- throw new IllegalArgumentException("Setting both nested_path/nested_filter and nested not allowed");
|
|
|
- }
|
|
|
- this.nestedFilter = nestedFilter;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the nested filter that the nested objects should match with in
|
|
|
- * order to be taken into account for sorting.
|
|
|
- *
|
|
|
- * @deprecated set nested sort with {@link #setNestedSort(NestedSortBuilder)} and retrieve with {@link #getNestedSort()}
|
|
|
- */
|
|
|
- @Deprecated
|
|
|
- public QueryBuilder getNestedFilter() {
|
|
|
- return this.nestedFilter;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Sets the nested path if sorting occurs on a field that is inside a nested
|
|
|
- * object. By default when sorting on a field inside a nested object, the
|
|
|
- * nearest upper nested object is selected as nested path.
|
|
|
- *
|
|
|
- * @deprecated set nested sort with {@link #setNestedSort(NestedSortBuilder)} and retrieve with {@link #getNestedSort()}
|
|
|
- */
|
|
|
- @Deprecated
|
|
|
- public FieldSortBuilder setNestedPath(String nestedPath) {
|
|
|
- if (this.nestedSort != null) {
|
|
|
- throw new IllegalArgumentException("Setting both nested_path/nested_filter and nested not allowed");
|
|
|
- }
|
|
|
- this.nestedPath = nestedPath;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the nested path if sorting occurs in a field that is inside a
|
|
|
- * nested object.
|
|
|
- * @deprecated set nested sort with {@link #setNestedSort(NestedSortBuilder)} and retrieve with {@link #getNestedSort()}
|
|
|
- */
|
|
|
- @Deprecated
|
|
|
- public String getNestedPath() {
|
|
|
- return this.nestedPath;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Returns the {@link NestedSortBuilder}
|
|
|
*/
|
|
@@ -276,9 +223,6 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
* order to be taken into account for sorting.
|
|
|
*/
|
|
|
public FieldSortBuilder setNestedSort(final NestedSortBuilder nestedSort) {
|
|
|
- if (this.nestedFilter != null || this.nestedPath != null) {
|
|
|
- throw new IllegalArgumentException("Setting both nested_path/nested_filter and nested not allowed");
|
|
|
- }
|
|
|
this.nestedSort = nestedSort;
|
|
|
return this;
|
|
|
}
|
|
@@ -330,12 +274,6 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
if (sortMode != null) {
|
|
|
builder.field(SORT_MODE.getPreferredName(), sortMode);
|
|
|
}
|
|
|
- if (nestedFilter != null) {
|
|
|
- builder.field(NESTED_FILTER_FIELD.getPreferredName(), nestedFilter, params);
|
|
|
- }
|
|
|
- if (nestedPath != null) {
|
|
|
- builder.field(NESTED_PATH_FIELD.getPreferredName(), nestedPath);
|
|
|
- }
|
|
|
if (nestedSort != null) {
|
|
|
builder.field(NESTED_FIELD.getPreferredName(), nestedSort);
|
|
|
}
|
|
@@ -367,65 +305,85 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
@Override
|
|
|
public SortFieldAndFormat build(QueryShardContext context) throws IOException {
|
|
|
if (DOC_FIELD_NAME.equals(fieldName)) {
|
|
|
- if (order == SortOrder.DESC) {
|
|
|
- return SORT_DOC_REVERSE;
|
|
|
+ return order == SortOrder.DESC ? SORT_DOC_REVERSE : SORT_DOC;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean isUnmapped = false;
|
|
|
+ MappedFieldType fieldType = context.fieldMapper(fieldName);
|
|
|
+ if (fieldType == null) {
|
|
|
+ isUnmapped = true;
|
|
|
+ if (unmappedType != null) {
|
|
|
+ fieldType = context.getMapperService().unmappedFieldType(unmappedType);
|
|
|
} else {
|
|
|
- return SORT_DOC;
|
|
|
- }
|
|
|
- } else {
|
|
|
- boolean isUnmapped = false;
|
|
|
- MappedFieldType fieldType = context.fieldMapper(fieldName);
|
|
|
- if (fieldType == null) {
|
|
|
- isUnmapped = true;
|
|
|
- if (unmappedType != null) {
|
|
|
- fieldType = context.getMapperService().unmappedFieldType(unmappedType);
|
|
|
- } else {
|
|
|
- throw new QueryShardException(context, "No mapping found for [" + fieldName + "] in order to sort on");
|
|
|
- }
|
|
|
+ throw new QueryShardException(context, "No mapping found for [" + fieldName + "] in order to sort on");
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- MultiValueMode localSortMode = null;
|
|
|
- if (sortMode != null) {
|
|
|
- localSortMode = MultiValueMode.fromString(sortMode.toString());
|
|
|
- }
|
|
|
+ MultiValueMode localSortMode = null;
|
|
|
+ if (sortMode != null) {
|
|
|
+ localSortMode = MultiValueMode.fromString(sortMode.toString());
|
|
|
+ }
|
|
|
|
|
|
- boolean reverse = (order == SortOrder.DESC);
|
|
|
- if (localSortMode == null) {
|
|
|
- localSortMode = reverse ? MultiValueMode.MAX : MultiValueMode.MIN;
|
|
|
- }
|
|
|
+ boolean reverse = (order == SortOrder.DESC);
|
|
|
+ if (localSortMode == null) {
|
|
|
+ localSortMode = reverse ? MultiValueMode.MAX : MultiValueMode.MIN;
|
|
|
+ }
|
|
|
|
|
|
- Nested nested = null;
|
|
|
- if (isUnmapped == false) {
|
|
|
- if (nestedSort != null) {
|
|
|
- if (nestedSort.getNestedSort() != null && nestedSort.getMaxChildren() != Integer.MAX_VALUE) {
|
|
|
- throw new QueryShardException(context,
|
|
|
- "max_children is only supported on last level of nested sort");
|
|
|
- }
|
|
|
- // new nested sorts takes priority
|
|
|
- nested = resolveNested(context, nestedSort);
|
|
|
- } else {
|
|
|
- nested = resolveNested(context, nestedPath, nestedFilter);
|
|
|
+ Nested nested = null;
|
|
|
+ if (isUnmapped == false) {
|
|
|
+ if (nestedSort != null) {
|
|
|
+ if (nestedSort.getNestedSort() != null && nestedSort.getMaxChildren() != Integer.MAX_VALUE) {
|
|
|
+ throw new QueryShardException(context,
|
|
|
+ "max_children is only supported on last level of nested sort");
|
|
|
}
|
|
|
+ nested = resolveNested(context, nestedSort);
|
|
|
+ } else {
|
|
|
+ validateMissingNestedPath(context, fieldName);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- IndexFieldData<?> fieldData = context.getForField(fieldType);
|
|
|
- if (fieldData instanceof IndexNumericFieldData == false
|
|
|
- && (sortMode == SortMode.SUM || sortMode == SortMode.AVG || sortMode == SortMode.MEDIAN)) {
|
|
|
- throw new QueryShardException(context, "we only support AVG, MEDIAN and SUM on number based fields");
|
|
|
+ IndexFieldData<?> fieldData = context.getForField(fieldType);
|
|
|
+ if (fieldData instanceof IndexNumericFieldData == false
|
|
|
+ && (sortMode == SortMode.SUM || sortMode == SortMode.AVG || sortMode == SortMode.MEDIAN)) {
|
|
|
+ throw new QueryShardException(context, "we only support AVG, MEDIAN and SUM on number based fields");
|
|
|
+ }
|
|
|
+ final SortField field;
|
|
|
+ if (numericType != null) {
|
|
|
+ if (fieldData instanceof IndexNumericFieldData == false) {
|
|
|
+ throw new QueryShardException(context,
|
|
|
+ "[numeric_type] option cannot be set on a non-numeric field, got " + fieldType.typeName());
|
|
|
}
|
|
|
- final SortField field;
|
|
|
- if (numericType != null) {
|
|
|
- if (fieldData instanceof IndexNumericFieldData == false) {
|
|
|
+ SortedNumericDVIndexFieldData numericFieldData = (SortedNumericDVIndexFieldData) fieldData;
|
|
|
+ NumericType resolvedType = resolveNumericType(numericType);
|
|
|
+ field = numericFieldData.sortField(resolvedType, missing, localSortMode, nested, reverse);
|
|
|
+ } else {
|
|
|
+ field = fieldData.sortField(missing, localSortMode, nested, reverse);
|
|
|
+ }
|
|
|
+ return new SortFieldAndFormat(field, fieldType.docValueFormat(null, null));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Throws an exception if the provided <code>field</code> requires a nested context.
|
|
|
+ */
|
|
|
+ static void validateMissingNestedPath(QueryShardContext context, String field) {
|
|
|
+ ObjectMapper contextMapper = context.nestedScope().getObjectMapper();
|
|
|
+ if (contextMapper != null && contextMapper.nested().isNested() == false) {
|
|
|
+ // already in nested context
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (String parent = parentObject(field); parent != null; parent = parentObject(parent)) {
|
|
|
+ ObjectMapper parentMapper = context.getObjectMapper(parent);
|
|
|
+ if (parentMapper != null && parentMapper.nested().isNested()) {
|
|
|
+ if (contextMapper != null && contextMapper.fullPath().equals(parentMapper.fullPath())) {
|
|
|
+ // we are in a nested context that matches the path of the provided field so the nested path
|
|
|
+ // is not required
|
|
|
+ return ;
|
|
|
+ }
|
|
|
+ if (parentMapper.nested().isIncludeInRoot() == false) {
|
|
|
throw new QueryShardException(context,
|
|
|
- "[numeric_type] option cannot be set on a non-numeric field, got " + fieldType.typeName());
|
|
|
+ "it is mandatory to set the [nested] context on the nested sort field: [" + field + "].");
|
|
|
}
|
|
|
- SortedNumericDVIndexFieldData numericFieldData = (SortedNumericDVIndexFieldData) fieldData;
|
|
|
- NumericType resolvedType = resolveNumericType(numericType);
|
|
|
- field = numericFieldData.sortField(resolvedType, missing, localSortMode, nested, reverse);
|
|
|
- } else {
|
|
|
- field = fieldData.sortField(missing, localSortMode, nested, reverse);
|
|
|
}
|
|
|
- return new SortFieldAndFormat(field, fieldType.docValueFormat(null, null));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -440,8 +398,7 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
}
|
|
|
|
|
|
FieldSortBuilder builder = (FieldSortBuilder) other;
|
|
|
- return (Objects.equals(this.fieldName, builder.fieldName) && Objects.equals(this.nestedFilter, builder.nestedFilter)
|
|
|
- && Objects.equals(this.nestedPath, builder.nestedPath) && Objects.equals(this.missing, builder.missing)
|
|
|
+ return (Objects.equals(this.fieldName, builder.fieldName) && Objects.equals(this.missing, builder.missing)
|
|
|
&& Objects.equals(this.order, builder.order) && Objects.equals(this.sortMode, builder.sortMode)
|
|
|
&& Objects.equals(this.unmappedType, builder.unmappedType) && Objects.equals(this.nestedSort, builder.nestedSort))
|
|
|
&& Objects.equals(this.numericType, builder.numericType);
|
|
@@ -449,7 +406,7 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
|
|
|
@Override
|
|
|
public int hashCode() {
|
|
|
- return Objects.hash(this.fieldName, this.nestedFilter, this.nestedPath, this.nestedSort, this.missing, this.order, this.sortMode,
|
|
|
+ return Objects.hash(this.fieldName, this.nestedSort, this.missing, this.order, this.sortMode,
|
|
|
this.unmappedType, this.numericType);
|
|
|
}
|
|
|
|
|
@@ -475,38 +432,22 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
|
|
|
|
|
static {
|
|
|
PARSER.declareField(FieldSortBuilder::missing, p -> p.objectText(), MISSING, ValueType.VALUE);
|
|
|
- PARSER.declareString((fieldSortBuilder, nestedPath) -> {
|
|
|
- deprecationLogger.deprecated("[nested_path] has been deprecated in favor of the [nested] parameter");
|
|
|
- fieldSortBuilder.setNestedPath(nestedPath);
|
|
|
- }, NESTED_PATH_FIELD);
|
|
|
PARSER.declareString(FieldSortBuilder::unmappedType , UNMAPPED_TYPE);
|
|
|
PARSER.declareString((b, v) -> b.order(SortOrder.fromString(v)) , ORDER_FIELD);
|
|
|
PARSER.declareString((b, v) -> b.sortMode(SortMode.fromString(v)), SORT_MODE);
|
|
|
- PARSER.declareObject(FieldSortBuilder::setNestedFilter, (p, c) -> {
|
|
|
- deprecationLogger.deprecated("[nested_filter] has been deprecated in favour for the [nested] parameter");
|
|
|
- return SortBuilder.parseNestedFilter(p);
|
|
|
- }, NESTED_FILTER_FIELD);
|
|
|
PARSER.declareObject(FieldSortBuilder::setNestedSort, (p, c) -> NestedSortBuilder.fromXContent(p), NESTED_FIELD);
|
|
|
PARSER.declareString((b, v) -> b.setNumericType(v), NUMERIC_TYPE);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public FieldSortBuilder rewrite(QueryRewriteContext ctx) throws IOException {
|
|
|
- if (nestedFilter == null && nestedSort == null) {
|
|
|
+ if (nestedSort == null) {
|
|
|
return this;
|
|
|
}
|
|
|
- if (nestedFilter != null) {
|
|
|
- QueryBuilder rewrite = nestedFilter.rewrite(ctx);
|
|
|
- if (nestedFilter == rewrite) {
|
|
|
- return this;
|
|
|
- }
|
|
|
- return new FieldSortBuilder(this).setNestedFilter(rewrite);
|
|
|
- } else {
|
|
|
- NestedSortBuilder rewrite = nestedSort.rewrite(ctx);
|
|
|
- if (nestedSort == rewrite) {
|
|
|
- return this;
|
|
|
- }
|
|
|
- return new FieldSortBuilder(this).setNestedSort(rewrite);
|
|
|
+ NestedSortBuilder rewrite = nestedSort.rewrite(ctx);
|
|
|
+ if (nestedSort == rewrite) {
|
|
|
+ return this;
|
|
|
}
|
|
|
+ return new FieldSortBuilder(this).setNestedSort(rewrite);
|
|
|
}
|
|
|
}
|