org.elasticsearch.xpack.esql.plan.physical.EsQueryExec Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of x-pack-esql Show documentation
Show all versions of x-pack-esql Show documentation
The plugin that powers ESQL for Elasticsearch
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.esql.plan.physical;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.expression.Order;
import org.elasticsearch.xpack.esql.core.index.EsIndex;
import org.elasticsearch.xpack.esql.core.querydsl.container.Sort;
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
import org.elasticsearch.xpack.esql.core.tree.NodeUtils;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.core.type.EsField;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class EsQueryExec extends LeafExec implements EstimatesRowSize {
public static final EsField DOC_ID_FIELD = new EsField("_doc", DataType.DOC_DATA_TYPE, Map.of(), false);
private final EsIndex index;
private final IndexMode indexMode;
private final QueryBuilder query;
private final Expression limit;
private final List sorts;
private final List attrs;
/**
* Estimate of the number of bytes that'll be loaded per position before
* the stream of pages is consumed.
*/
private final Integer estimatedRowSize;
public record FieldSort(FieldAttribute field, Order.OrderDirection direction, Order.NullsPosition nulls) {
public FieldSortBuilder fieldSortBuilder() {
FieldSortBuilder builder = new FieldSortBuilder(field.name());
builder.order(Sort.Direction.from(direction).asOrder());
builder.missing(Sort.Missing.from(nulls).searchOrder());
builder.unmappedType(field.dataType().esType());
return builder;
}
}
public EsQueryExec(Source source, EsIndex index, IndexMode indexMode, List attributes, QueryBuilder query) {
this(source, index, indexMode, attributes, query, null, null, null);
}
public EsQueryExec(
Source source,
EsIndex index,
IndexMode indexMode,
List attrs,
QueryBuilder query,
Expression limit,
List sorts,
Integer estimatedRowSize
) {
super(source);
this.index = index;
this.indexMode = indexMode;
this.query = query;
this.attrs = attrs;
this.limit = limit;
this.sorts = sorts;
this.estimatedRowSize = estimatedRowSize;
}
public static boolean isSourceAttribute(Attribute attr) {
return DOC_ID_FIELD.getName().equals(attr.name());
}
@Override
protected NodeInfo info() {
return NodeInfo.create(this, EsQueryExec::new, index, indexMode, attrs, query, limit, sorts, estimatedRowSize);
}
public EsIndex index() {
return index;
}
public IndexMode indexMode() {
return indexMode;
}
public QueryBuilder query() {
return query;
}
@Override
public List output() {
return attrs;
}
public Expression limit() {
return limit;
}
public List sorts() {
return sorts;
}
public List attrs() {
return attrs;
}
/**
* Estimate of the number of bytes that'll be loaded per position before
* the stream of pages is consumed.
*/
public Integer estimatedRowSize() {
return estimatedRowSize;
}
@Override
public PhysicalPlan estimateRowSize(State state) {
int size;
if (sorts == null || sorts.isEmpty()) {
// track doc ids
state.add(false, Integer.BYTES);
size = state.consumeAllFields(false);
} else {
// track doc ids and segment ids
state.add(false, Integer.BYTES * 2);
size = state.consumeAllFields(true);
}
return Objects.equals(this.estimatedRowSize, size)
? this
: new EsQueryExec(source(), index, indexMode, attrs, query, limit, sorts, size);
}
public EsQueryExec withLimit(Expression limit) {
return Objects.equals(this.limit, limit)
? this
: new EsQueryExec(source(), index, indexMode, attrs, query, limit, sorts, estimatedRowSize);
}
public boolean canPushSorts() {
return indexMode != IndexMode.TIME_SERIES;
}
public EsQueryExec withSorts(List sorts) {
if (indexMode == IndexMode.TIME_SERIES) {
assert false : "time-series index mode doesn't support sorts";
throw new UnsupportedOperationException("time-series index mode doesn't support sorts");
}
return Objects.equals(this.sorts, sorts)
? this
: new EsQueryExec(source(), index, indexMode, attrs, query, limit, sorts, estimatedRowSize);
}
@Override
public int hashCode() {
return Objects.hash(index, indexMode, attrs, query, limit, sorts);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
EsQueryExec other = (EsQueryExec) obj;
return Objects.equals(index, other.index)
&& Objects.equals(indexMode, other.indexMode)
&& Objects.equals(attrs, other.attrs)
&& Objects.equals(query, other.query)
&& Objects.equals(limit, other.limit)
&& Objects.equals(sorts, other.sorts)
&& Objects.equals(estimatedRowSize, other.estimatedRowSize);
}
@Override
public String nodeString() {
return nodeName()
+ "["
+ index
+ "], "
+ "indexMode["
+ indexMode
+ "], "
+ "query["
+ (query != null ? Strings.toString(query, false, true) : "")
+ "]"
+ NodeUtils.limitedToString(attrs)
+ ", limit["
+ (limit != null ? limit.toString() : "")
+ "], sort["
+ (sorts != null ? sorts.toString() : "")
+ "] estimatedRowSize["
+ estimatedRowSize
+ "]";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy