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

org.elasticsearch.client.ml.datafeed.DatafeedUpdate Maven / Gradle / Ivy

There is a newer version: 8.0.0-alpha2
Show newest version
/*
 * 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 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */
package org.elasticsearch.client.ml.datafeed;

import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.xcontent.ConstructingObjectParser;
import org.elasticsearch.xcontent.ObjectParser;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.xcontent.XContentType;
import org.elasticsearch.xcontent.json.JsonXContent;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * A datafeed update contains partial properties to update a {@link DatafeedConfig}.
 * The main difference between this class and {@link DatafeedConfig} is that here all
 * fields are nullable.
 */
public class DatafeedUpdate implements ToXContentObject {

    public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(
        "datafeed_update",
        true,
        a -> new Builder((String) a[0])
    );

    static {
        PARSER.declareString(ConstructingObjectParser.constructorArg(), DatafeedConfig.ID);

        PARSER.declareString(Builder::setJobId, Job.ID);
        PARSER.declareStringArray(Builder::setIndices, DatafeedConfig.INDEXES);
        PARSER.declareStringArray(Builder::setIndices, DatafeedConfig.INDICES);
        PARSER.declareString(
            (builder, val) -> builder.setQueryDelay(TimeValue.parseTimeValue(val, DatafeedConfig.QUERY_DELAY.getPreferredName())),
            DatafeedConfig.QUERY_DELAY
        );
        PARSER.declareString(
            (builder, val) -> builder.setFrequency(TimeValue.parseTimeValue(val, DatafeedConfig.FREQUENCY.getPreferredName())),
            DatafeedConfig.FREQUENCY
        );
        PARSER.declareField(Builder::setQuery, DatafeedUpdate::parseBytes, DatafeedConfig.QUERY, ObjectParser.ValueType.OBJECT);
        PARSER.declareField(
            Builder::setAggregations,
            DatafeedUpdate::parseBytes,
            DatafeedConfig.AGGREGATIONS,
            ObjectParser.ValueType.OBJECT
        );
        PARSER.declareObject(Builder::setScriptFields, (p, c) -> {
            List parsedScriptFields = new ArrayList<>();
            while (p.nextToken() != XContentParser.Token.END_OBJECT) {
                parsedScriptFields.add(new SearchSourceBuilder.ScriptField(p));
            }
            return parsedScriptFields;
        }, DatafeedConfig.SCRIPT_FIELDS);
        PARSER.declareInt(Builder::setScrollSize, DatafeedConfig.SCROLL_SIZE);
        PARSER.declareObject(Builder::setChunkingConfig, ChunkingConfig.PARSER, DatafeedConfig.CHUNKING_CONFIG);
        PARSER.declareObject(Builder::setDelayedDataCheckConfig, DelayedDataCheckConfig.PARSER, DatafeedConfig.DELAYED_DATA_CHECK_CONFIG);
        PARSER.declareInt(Builder::setMaxEmptySearches, DatafeedConfig.MAX_EMPTY_SEARCHES);
        PARSER.declareObject(
            Builder::setIndicesOptions,
            (p, c) -> IndicesOptions.fromMap(p.map(), new IndicesOptions(IndicesOptions.Option.NONE, IndicesOptions.WildcardStates.NONE)),
            DatafeedConfig.INDICES_OPTIONS
        );
        PARSER.declareObject(Builder::setRuntimeMappings, (p, c) -> p.map(), SearchSourceBuilder.RUNTIME_MAPPINGS_FIELD);
    }

    private static BytesReference parseBytes(XContentParser parser) throws IOException {
        XContentBuilder contentBuilder = JsonXContent.contentBuilder();
        contentBuilder.generator().copyCurrentStructure(parser);
        return BytesReference.bytes(contentBuilder);
    }

    private final String id;
    private final String jobId;
    private final TimeValue queryDelay;
    private final TimeValue frequency;
    private final List indices;
    private final BytesReference query;
    private final BytesReference aggregations;
    private final List scriptFields;
    private final Integer scrollSize;
    private final ChunkingConfig chunkingConfig;
    private final DelayedDataCheckConfig delayedDataCheckConfig;
    private final Integer maxEmptySearches;
    private final IndicesOptions indicesOptions;
    private final Map runtimeMappings;

    private DatafeedUpdate(
        String id,
        String jobId,
        TimeValue queryDelay,
        TimeValue frequency,
        List indices,
        BytesReference query,
        BytesReference aggregations,
        List scriptFields,
        Integer scrollSize,
        ChunkingConfig chunkingConfig,
        DelayedDataCheckConfig delayedDataCheckConfig,
        Integer maxEmptySearches,
        IndicesOptions indicesOptions,
        Map runtimeMappings
    ) {
        this.id = id;
        this.jobId = jobId;
        this.queryDelay = queryDelay;
        this.frequency = frequency;
        this.indices = indices;
        this.query = query;
        this.aggregations = aggregations;
        this.scriptFields = scriptFields;
        this.scrollSize = scrollSize;
        this.chunkingConfig = chunkingConfig;
        this.delayedDataCheckConfig = delayedDataCheckConfig;
        this.maxEmptySearches = maxEmptySearches;
        this.indicesOptions = indicesOptions;
        this.runtimeMappings = runtimeMappings;
    }

    /**
     * Get the id of the datafeed to update
     */
    public String getId() {
        return id;
    }

    public Map getRuntimeMappings() {
        return runtimeMappings;
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject();
        builder.field(DatafeedConfig.ID.getPreferredName(), id);
        addOptionalField(builder, Job.ID, jobId);
        if (queryDelay != null) {
            builder.field(DatafeedConfig.QUERY_DELAY.getPreferredName(), queryDelay.getStringRep());
        }
        if (frequency != null) {
            builder.field(DatafeedConfig.FREQUENCY.getPreferredName(), frequency.getStringRep());
        }
        addOptionalField(builder, DatafeedConfig.INDICES, indices);
        if (query != null) {
            builder.field(DatafeedConfig.QUERY.getPreferredName(), asMap(query));
        }
        if (aggregations != null) {
            builder.field(DatafeedConfig.AGGREGATIONS.getPreferredName(), asMap(aggregations));
        }
        if (scriptFields != null) {
            builder.startObject(DatafeedConfig.SCRIPT_FIELDS.getPreferredName());
            for (SearchSourceBuilder.ScriptField scriptField : scriptFields) {
                scriptField.toXContent(builder, params);
            }
            builder.endObject();
        }
        if (delayedDataCheckConfig != null) {
            builder.field(DatafeedConfig.DELAYED_DATA_CHECK_CONFIG.getPreferredName(), delayedDataCheckConfig);
        }
        addOptionalField(builder, DatafeedConfig.SCROLL_SIZE, scrollSize);
        addOptionalField(builder, DatafeedConfig.CHUNKING_CONFIG, chunkingConfig);
        addOptionalField(builder, DatafeedConfig.MAX_EMPTY_SEARCHES, maxEmptySearches);
        if (indicesOptions != null) {
            builder.startObject(DatafeedConfig.INDICES_OPTIONS.getPreferredName());
            indicesOptions.toXContent(builder, params);
            builder.endObject();
        }
        addOptionalField(builder, SearchSourceBuilder.RUNTIME_MAPPINGS_FIELD, runtimeMappings);
        builder.endObject();
        return builder;
    }

    private void addOptionalField(XContentBuilder builder, ParseField field, Object value) throws IOException {
        if (value != null) {
            builder.field(field.getPreferredName(), value);
        }
    }

    public String getJobId() {
        return jobId;
    }

    public TimeValue getQueryDelay() {
        return queryDelay;
    }

    public TimeValue getFrequency() {
        return frequency;
    }

    public List getIndices() {
        return indices;
    }

    public Integer getScrollSize() {
        return scrollSize;
    }

    public BytesReference getQuery() {
        return query;
    }

    public BytesReference getAggregations() {
        return aggregations;
    }

    public List getScriptFields() {
        return scriptFields == null ? Collections.emptyList() : scriptFields;
    }

    public ChunkingConfig getChunkingConfig() {
        return chunkingConfig;
    }

    public DelayedDataCheckConfig getDelayedDataCheckConfig() {
        return delayedDataCheckConfig;
    }

    public Integer getMaxEmptySearches() {
        return maxEmptySearches;
    }

    public IndicesOptions getIndicesOptions() {
        return indicesOptions;
    }

    private static Map asMap(BytesReference bytesReference) {
        return bytesReference == null ? null : XContentHelper.convertToMap(bytesReference, true, XContentType.JSON).v2();
    }

    /**
     * The lists of indices and types are compared for equality but they are not
     * sorted first so this test could fail simply because the indices and types
     * lists are in different orders.
     *
     * Also note this could be a heavy operation when a query or aggregations
     * are set as we need to convert the bytes references into maps to correctly
     * compare them.
     */
    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }

        if (other == null || getClass() != other.getClass()) {
            return false;
        }

        DatafeedUpdate that = (DatafeedUpdate) other;

        return Objects.equals(this.id, that.id)
            && Objects.equals(this.jobId, that.jobId)
            && Objects.equals(this.frequency, that.frequency)
            && Objects.equals(this.queryDelay, that.queryDelay)
            && Objects.equals(this.indices, that.indices)
            && Objects.equals(asMap(this.query), asMap(that.query))
            && Objects.equals(this.scrollSize, that.scrollSize)
            && Objects.equals(asMap(this.aggregations), asMap(that.aggregations))
            && Objects.equals(this.delayedDataCheckConfig, that.delayedDataCheckConfig)
            && Objects.equals(this.scriptFields, that.scriptFields)
            && Objects.equals(this.chunkingConfig, that.chunkingConfig)
            && Objects.equals(this.maxEmptySearches, that.maxEmptySearches)
            && Objects.equals(this.indicesOptions, that.indicesOptions)
            && Objects.equals(this.runtimeMappings, that.runtimeMappings);
    }

    /**
     * Note this could be a heavy operation when a query or aggregations
     * are set as we need to convert the bytes references into maps to
     * compute a stable hash code.
     */
    @Override
    public int hashCode() {
        return Objects.hash(
            id,
            jobId,
            frequency,
            queryDelay,
            indices,
            asMap(query),
            scrollSize,
            asMap(aggregations),
            scriptFields,
            chunkingConfig,
            delayedDataCheckConfig,
            maxEmptySearches,
            indicesOptions,
            runtimeMappings
        );
    }

    public static Builder builder(String id) {
        return new Builder(id);
    }

    public static class Builder {

        private String id;
        private String jobId;
        private TimeValue queryDelay;
        private TimeValue frequency;
        private List indices;
        private BytesReference query;
        private BytesReference aggregations;
        private List scriptFields;
        private Integer scrollSize;
        private ChunkingConfig chunkingConfig;
        private DelayedDataCheckConfig delayedDataCheckConfig;
        private Integer maxEmptySearches;
        private IndicesOptions indicesOptions;
        private Map runtimeMappings;

        public Builder(String id) {
            this.id = Objects.requireNonNull(id, DatafeedConfig.ID.getPreferredName());
        }

        public Builder(DatafeedUpdate config) {
            this.id = config.id;
            this.jobId = config.jobId;
            this.queryDelay = config.queryDelay;
            this.frequency = config.frequency;
            this.indices = config.indices;
            this.query = config.query;
            this.aggregations = config.aggregations;
            this.scriptFields = config.scriptFields;
            this.scrollSize = config.scrollSize;
            this.chunkingConfig = config.chunkingConfig;
            this.delayedDataCheckConfig = config.delayedDataCheckConfig;
            this.maxEmptySearches = config.maxEmptySearches;
            this.indicesOptions = config.indicesOptions;
            this.runtimeMappings = config.runtimeMappings != null ? new HashMap<>(config.runtimeMappings) : null;
        }

        @Deprecated
        public Builder setJobId(String jobId) {
            this.jobId = jobId;
            return this;
        }

        public Builder setIndices(List indices) {
            this.indices = indices;
            return this;
        }

        public Builder setIndices(String... indices) {
            return setIndices(Arrays.asList(indices));
        }

        public Builder setQueryDelay(TimeValue queryDelay) {
            this.queryDelay = queryDelay;
            return this;
        }

        public Builder setFrequency(TimeValue frequency) {
            this.frequency = frequency;
            return this;
        }

        private Builder setQuery(BytesReference query) {
            this.query = query;
            return this;
        }

        public Builder setQuery(String queryAsJson) {
            this.query = queryAsJson == null ? null : new BytesArray(queryAsJson);
            return this;
        }

        public Builder setQuery(QueryBuilder query) throws IOException {
            this.query = query == null ? null : xContentToBytes(query);
            return this;
        }

        private Builder setAggregations(BytesReference aggregations) {
            this.aggregations = aggregations;
            return this;
        }

        public Builder setAggregations(String aggsAsJson) {
            this.aggregations = aggsAsJson == null ? null : new BytesArray(aggsAsJson);
            return this;
        }

        public Builder setAggregations(AggregatorFactories.Builder aggregations) throws IOException {
            this.aggregations = aggregations == null ? null : xContentToBytes(aggregations);
            return this;
        }

        public Builder setScriptFields(List scriptFields) {
            List sorted = new ArrayList<>(scriptFields);
            sorted.sort(Comparator.comparing(SearchSourceBuilder.ScriptField::fieldName));
            this.scriptFields = sorted;
            return this;
        }

        public Builder setScrollSize(int scrollSize) {
            this.scrollSize = scrollSize;
            return this;
        }

        public Builder setChunkingConfig(ChunkingConfig chunkingConfig) {
            this.chunkingConfig = chunkingConfig;
            return this;
        }

        public Builder setDelayedDataCheckConfig(DelayedDataCheckConfig delayedDataCheckConfig) {
            this.delayedDataCheckConfig = delayedDataCheckConfig;
            return this;
        }

        public Builder setMaxEmptySearches(int maxEmptySearches) {
            this.maxEmptySearches = maxEmptySearches;
            return this;
        }

        public Builder setIndicesOptions(IndicesOptions indicesOptions) {
            this.indicesOptions = indicesOptions;
            return this;
        }

        public Builder setRuntimeMappings(Map runtimeMappings) {
            this.runtimeMappings = runtimeMappings;
            return this;
        }

        public DatafeedUpdate build() {
            return new DatafeedUpdate(
                id,
                jobId,
                queryDelay,
                frequency,
                indices,
                query,
                aggregations,
                scriptFields,
                scrollSize,
                chunkingConfig,
                delayedDataCheckConfig,
                maxEmptySearches,
                indicesOptions,
                runtimeMappings
            );
        }

        private static BytesReference xContentToBytes(ToXContentObject object) throws IOException {
            try (XContentBuilder builder = JsonXContent.contentBuilder()) {
                object.toXContent(builder, ToXContentObject.EMPTY_PARAMS);
                return BytesReference.bytes(builder);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy