Skip to content

Commit 4f662bd

Browse files
author
Hendrik Muhs
authored
Add data frame feature (#38934) (#39029)
The data frame plugin allows users to create feature indexes by pivoting a source index. In a nutshell this can be understood as reindex supporting aggregations or similar to the so called entity centric indexing. Full history is provided in: feature/data-frame-transforms
1 parent 8895bef commit 4f662bd

File tree

93 files changed

+8671
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+8671
-5
lines changed

client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1697,7 +1697,7 @@ public void testCRUDIndexTemplateWithTypes() throws Exception {
16971697
assertTrue(template2.mappings().containsKey("custom_doc_type"));
16981698

16991699
List<String> names = randomBoolean()
1700-
? Arrays.asList("*-1", "template-2")
1700+
? Arrays.asList("*plate-1", "template-2")
17011701
: Arrays.asList("template-*");
17021702
GetIndexTemplatesRequest getBothRequest = new GetIndexTemplatesRequest(names);
17031703
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse getBoth = execute(
@@ -1780,7 +1780,7 @@ public void testCRUDIndexTemplate() throws Exception {
17801780

17811781

17821782
List<String> names = randomBoolean()
1783-
? Arrays.asList("*-1", "template-2")
1783+
? Arrays.asList("*plate-1", "template-2")
17841784
: Arrays.asList("template-*");
17851785
GetIndexTemplatesRequest getBothRequest = new GetIndexTemplatesRequest(names);
17861786
GetIndexTemplatesResponse getBoth = execute(
@@ -1834,7 +1834,7 @@ public void testIndexTemplatesExist() throws Exception {
18341834

18351835
{
18361836
final List<String> templateNames = randomBoolean()
1837-
? Arrays.asList("*-1", "template-2")
1837+
? Arrays.asList("*plate-1", "template-2")
18381838
: Arrays.asList("template-*");
18391839

18401840
final IndexTemplatesExistRequest bothRequest = new IndexTemplatesExistRequest(templateNames);

docs/reference/rest-api/info.asciidoc

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ Example response:
6868
"available" : true,
6969
"enabled" : true
7070
},
71+
"data_frame" : {
72+
"description" : "Data Frame for the Elastic Stack",
73+
"available" : true,
74+
"enabled" : true
75+
},
7176
"graph" : {
7277
"description" : "Graph Data Exploration for the Elastic Stack",
7378
"available" : true,

x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java

+9
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,15 @@ public static boolean isMachineLearningAllowedForOperationMode(final OperationMo
553553
return isPlatinumOrTrialOperationMode(operationMode);
554554
}
555555

556+
/**
557+
* Data Frame is always available as long as there is a valid license
558+
*
559+
* @return true if the license is active
560+
*/
561+
public synchronized boolean isDataFrameAllowed() {
562+
return status.active;
563+
}
564+
556565
/**
557566
* Rollup is always available as long as there is a valid license
558567
*

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public final class ClientHelper {
5050
public static final String DEPRECATION_ORIGIN = "deprecation";
5151
public static final String PERSISTENT_TASK_ORIGIN = "persistent_tasks";
5252
public static final String ROLLUP_ORIGIN = "rollup";
53+
public static final String DATA_FRAME_ORIGIN = "data_frame";
5354

5455
private ClientHelper() {}
5556

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.elasticsearch.xpack.core.beats.BeatsFeatureSetUsage;
4343
import org.elasticsearch.xpack.core.ccr.AutoFollowMetadata;
4444
import org.elasticsearch.xpack.core.ccr.CCRFeatureSet;
45+
import org.elasticsearch.xpack.core.dataframe.DataFrameFeatureSetUsage;
4546
import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction;
4647
import org.elasticsearch.xpack.core.graph.GraphFeatureSetUsage;
4748
import org.elasticsearch.xpack.core.graph.action.GraphExploreAction;
@@ -439,8 +440,9 @@ public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
439440
new NamedWriteableRegistry.Entry(LifecycleAction.class, DeleteAction.NAME, DeleteAction::new),
440441
new NamedWriteableRegistry.Entry(LifecycleAction.class, FreezeAction.NAME, FreezeAction::new),
441442
new NamedWriteableRegistry.Entry(LifecycleAction.class, SetPriorityAction.NAME, SetPriorityAction::new),
442-
new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new)
443-
);
443+
new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new),
444+
// Data Frame
445+
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.DATA_FRAME, DataFrameFeatureSetUsage::new));
444446
}
445447

446448
@Override

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public final class XPackField {
3535
public static final String INDEX_LIFECYCLE = "ilm";
3636
/** Name constant for the CCR feature. */
3737
public static final String CCR = "ccr";
38+
/** Name constant for the data frame feature. */
39+
public static final String DATA_FRAME = "data_frame";
3840

3941
private XPackField() {}
4042

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ private XPackSettings() {
4444
*/
4545
public static final Setting<Boolean> CCR_ENABLED_SETTING = Setting.boolSetting("xpack.ccr.enabled", true, Property.NodeScope);
4646

47+
/** Setting for enabling or disabling data frame. Defaults to true. */
48+
public static final Setting<Boolean> DATA_FRAME_ENABLED = Setting.boolSetting("xpack.data_frame.enabled", true,
49+
Setting.Property.NodeScope);
50+
4751
/** Setting for enabling or disabling security. Defaults to true. */
4852
public static final Setting<Boolean> SECURITY_ENABLED = Setting.boolSetting("xpack.security.enabled", true, Setting.Property.NodeScope);
4953

@@ -209,6 +213,7 @@ public static List<Setting<?>> getAllSettings() {
209213
settings.add(ROLLUP_ENABLED);
210214
settings.add(PASSWORD_HASHING_ALGORITHM);
211215
settings.add(INDEX_LIFECYCLE_ENABLED);
216+
settings.add(DATA_FRAME_ENABLED);
212217
return Collections.unmodifiableList(settings);
213218
}
214219

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.dataframe;
8+
9+
import org.elasticsearch.cluster.metadata.MetaData;
10+
import org.elasticsearch.common.io.stream.StreamInput;
11+
import org.elasticsearch.common.io.stream.StreamOutput;
12+
import org.elasticsearch.common.xcontent.XContentBuilder;
13+
import org.elasticsearch.xpack.core.XPackFeatureSet.Usage;
14+
import org.elasticsearch.xpack.core.XPackField;
15+
import org.elasticsearch.xpack.core.dataframe.transform.DataFrameIndexerTransformStats;
16+
17+
import java.io.IOException;
18+
import java.util.Map;
19+
import java.util.Map.Entry;
20+
import java.util.Objects;
21+
22+
public class DataFrameFeatureSetUsage extends Usage {
23+
24+
private final Map<String, Long> transformCountByState;
25+
private final DataFrameIndexerTransformStats accumulatedStats;
26+
27+
public DataFrameFeatureSetUsage(StreamInput in) throws IOException {
28+
super(in);
29+
this.transformCountByState = in.readMap(StreamInput::readString, StreamInput::readLong);
30+
this.accumulatedStats = new DataFrameIndexerTransformStats(in);
31+
}
32+
33+
public DataFrameFeatureSetUsage(boolean available, boolean enabled, Map<String, Long> transformCountByState,
34+
DataFrameIndexerTransformStats accumulatedStats) {
35+
super(XPackField.DATA_FRAME, available, enabled);
36+
this.transformCountByState = Objects.requireNonNull(transformCountByState);
37+
this.accumulatedStats = Objects.requireNonNull(accumulatedStats);
38+
}
39+
40+
@Override
41+
public void writeTo(StreamOutput out) throws IOException {
42+
super.writeTo(out);
43+
out.writeMap(transformCountByState, StreamOutput::writeString, StreamOutput::writeLong);
44+
accumulatedStats.writeTo(out);
45+
}
46+
47+
@Override
48+
protected void innerXContent(XContentBuilder builder, Params params) throws IOException {
49+
super.innerXContent(builder, params);
50+
if (transformCountByState.isEmpty() == false) {
51+
builder.startObject(DataFrameField.TRANSFORMS.getPreferredName());
52+
long all = 0L;
53+
for (Entry<String, Long> entry : transformCountByState.entrySet()) {
54+
builder.field(entry.getKey(), entry.getValue());
55+
all+=entry.getValue();
56+
}
57+
builder.field(MetaData.ALL, all);
58+
builder.endObject();
59+
60+
// if there are no transforms, do not show any stats
61+
builder.field(DataFrameField.STATS_FIELD.getPreferredName(), accumulatedStats);
62+
}
63+
}
64+
65+
@Override
66+
public int hashCode() {
67+
return Objects.hash(enabled, available, transformCountByState, accumulatedStats);
68+
}
69+
70+
@Override
71+
public boolean equals(Object obj) {
72+
if (obj == null) {
73+
return false;
74+
}
75+
if (getClass() != obj.getClass()) {
76+
return false;
77+
}
78+
DataFrameFeatureSetUsage other = (DataFrameFeatureSetUsage) obj;
79+
return Objects.equals(name, other.name) && available == other.available && enabled == other.enabled
80+
&& Objects.equals(transformCountByState, other.transformCountByState)
81+
&& Objects.equals(accumulatedStats, other.accumulatedStats);
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.dataframe;
8+
9+
import org.elasticsearch.common.ParseField;
10+
11+
/*
12+
* Utility class to hold common fields and strings for data frame.
13+
*/
14+
public final class DataFrameField {
15+
16+
// common parse fields
17+
public static final ParseField AGGREGATIONS = new ParseField("aggregations");
18+
public static final ParseField AGGS = new ParseField("aggs");
19+
public static final ParseField ID = new ParseField("id");
20+
public static final ParseField TRANSFORMS = new ParseField("transforms");
21+
public static final ParseField COUNT = new ParseField("count");
22+
public static final ParseField GROUP_BY = new ParseField("group_by");
23+
public static final ParseField TIMEOUT = new ParseField("timeout");
24+
public static final ParseField WAIT_FOR_COMPLETION = new ParseField("wait_for_completion");
25+
public static final ParseField STATS_FIELD = new ParseField("stats");
26+
27+
// common strings
28+
public static final String TASK_NAME = "data_frame/transforms";
29+
public static final String REST_BASE_PATH = "/_data_frame/";
30+
public static final String REST_BASE_PATH_TRANSFORMS_BY_ID = REST_BASE_PATH + "transforms/{id}/";
31+
32+
// note: this is used to match tasks
33+
public static final String PERSISTENT_TASK_DESCRIPTION_PREFIX = "data_frame_";
34+
35+
// strings for meta information
36+
public static final String META_FIELDNAME = "_data_frame";
37+
public static final String CREATION_DATE_MILLIS = "creation_date_in_millis";
38+
public static final String VERSION = "version";
39+
public static final String CREATED = "created";
40+
public static final String CREATED_BY = "created_by";
41+
public static final String TRANSFORM = "transform";
42+
public static final String DATA_FRAME_SIGNATURE = "data-frame-transform";
43+
44+
private DataFrameField() {
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.dataframe;
8+
9+
import java.text.MessageFormat;
10+
import java.util.Locale;
11+
12+
public class DataFrameMessages {
13+
14+
public static final String REST_STOP_TRANSFORM_WAIT_FOR_COMPLETION_TIMEOUT =
15+
"Timed out after [{0}] while waiting for data frame transform [{1}] to stop";
16+
public static final String REST_STOP_TRANSFORM_WAIT_FOR_COMPLETION_INTERRUPT =
17+
"Interrupted while waiting for data frame transform [{0}] to stop";
18+
public static final String REST_PUT_DATA_FRAME_TRANSFORM_EXISTS = "Transform with id [{0}] already exists";
19+
public static final String REST_DATA_FRAME_UNKNOWN_TRANSFORM = "Transform with id [{0}] could not be found";
20+
public static final String REST_PUT_DATA_FRAME_FAILED_TO_VALIDATE_DATA_FRAME_CONFIGURATION =
21+
"Failed to validate data frame configuration";
22+
public static final String REST_PUT_DATA_FRAME_FAILED_PERSIST_TRANSFORM_CONFIGURATION = "Failed to persist data frame configuration";
23+
public static final String REST_PUT_DATA_FRAME_FAILED_TO_DEDUCE_TARGET_MAPPINGS = "Failed to deduce target mappings";
24+
public static final String REST_PUT_DATA_FRAME_FAILED_TO_CREATE_TARGET_INDEX = "Failed to create target index";
25+
public static final String REST_PUT_DATA_FRAME_FAILED_TO_START_PERSISTENT_TASK =
26+
"Failed to start persistent task, configuration has been cleaned up: [{0}]";
27+
public static final String REST_DATA_FRAME_FAILED_TO_SERIALIZE_TRANSFORM = "Failed to serialise transform [{0}]";
28+
29+
public static final String FAILED_TO_CREATE_DESTINATION_INDEX = "Could not create destination index [{0}] for transform[{1}]";
30+
public static final String FAILED_TO_LOAD_TRANSFORM_CONFIGURATION =
31+
"Failed to load data frame transform configuration for transform [{0}]";
32+
public static final String FAILED_TO_PARSE_TRANSFORM_CONFIGURATION =
33+
"Failed to parse transform configuration for data frame transform [{0}]";
34+
public static final String DATA_FRAME_TRANSFORM_CONFIGURATION_NO_TRANSFORM =
35+
"Data frame transform configuration must specify exactly 1 function";
36+
public static final String DATA_FRAME_TRANSFORM_CONFIGURATION_PIVOT_NO_GROUP_BY =
37+
"Data frame pivot transform configuration must specify at least 1 group_by";
38+
public static final String DATA_FRAME_TRANSFORM_CONFIGURATION_PIVOT_NO_AGGREGATION =
39+
"Data frame pivot transform configuration must specify at least 1 aggregation";
40+
public static final String DATA_FRAME_TRANSFORM_PIVOT_FAILED_TO_CREATE_COMPOSITE_AGGREGATION =
41+
"Failed to create composite aggregation from pivot function";
42+
public static final String DATA_FRAME_TRANSFORM_CONFIGURATION_INVALID =
43+
"Data frame transform configuration [{0}] has invalid elements";
44+
45+
public static final String LOG_DATA_FRAME_TRANSFORM_CONFIGURATION_BAD_QUERY =
46+
"Failed to parse query for data frame transform";
47+
public static final String LOG_DATA_FRAME_TRANSFORM_CONFIGURATION_BAD_GROUP_BY =
48+
"Failed to parse group_by for data frame pivot transform";
49+
public static final String LOG_DATA_FRAME_TRANSFORM_CONFIGURATION_BAD_AGGREGATION =
50+
"Failed to parse aggregation for data frame pivot transform";
51+
52+
private DataFrameMessages() {
53+
}
54+
55+
/**
56+
* Returns the message parameter
57+
*
58+
* @param message Should be one of the statics defined in this class
59+
*/
60+
public static String getMessage(String message) {
61+
return message;
62+
}
63+
64+
/**
65+
* Format the message with the supplied arguments
66+
*
67+
* @param message Should be one of the statics defined in this class
68+
* @param args MessageFormat arguments. See {@linkplain MessageFormat#format(Object)}]
69+
*/
70+
public static String getMessage(String message, Object... args) {
71+
return new MessageFormat(message, Locale.ROOT).format(args);
72+
}
73+
}

0 commit comments

Comments
 (0)