Skip to content

Commit c02f604

Browse files
authored
Merge branch 'main' into removetv1
2 parents 7e2f715 + d1f2980 commit c02f604

File tree

34 files changed

+2140
-8
lines changed

34 files changed

+2140
-8
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"esql.delete_view": {
3+
"documentation": {
4+
"url": "https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-view-delete",
5+
"description": "Delete a non-materialized VIEW for ESQL."
6+
},
7+
"stability": "experimental",
8+
"visibility": "feature_flag",
9+
"feature_flag": "esql_views",
10+
"headers": {
11+
"accept": [
12+
"application/json"
13+
],
14+
"content_type": [
15+
"application/json"
16+
]
17+
},
18+
"url": {
19+
"paths": [
20+
{
21+
"path": "/_query/view/{name}",
22+
"methods": [
23+
"DELETE"
24+
],
25+
"parts": {
26+
"name": {
27+
"type": "string",
28+
"description": "The name of the view to delete"
29+
}
30+
}
31+
}
32+
]
33+
}
34+
}
35+
}

rest-api-spec/src/main/resources/rest-api-spec/api/esql.get_query.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"description": "Get a specific running ES|QL query information"
66
},
77
"stability": "experimental",
8-
"visibility": "public",
8+
"visibility": "feature_flag",
9+
"feature_flag": "esql_views",
910
"headers": {
1011
"accept": [
1112
"application/json"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"esql.get_view": {
3+
"documentation": {
4+
"url": "https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-view-get",
5+
"description": "Get a non-materialized VIEW for ESQL."
6+
},
7+
"stability": "experimental",
8+
"visibility": "feature_flag",
9+
"feature_flag": "esql_views",
10+
"headers": {
11+
"accept": [
12+
"application/json"
13+
],
14+
"content_type": [
15+
"application/json"
16+
]
17+
},
18+
"url": {
19+
"paths": [
20+
{
21+
"path": "/_query/view/{name}",
22+
"methods": [
23+
"GET"
24+
],
25+
"parts": {
26+
"name": {
27+
"type": "list",
28+
"description": "A comma-separated list of view names"
29+
}
30+
}
31+
},
32+
{
33+
"path": "/_query/view",
34+
"methods": [
35+
"GET"
36+
]
37+
}
38+
]
39+
}
40+
}
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"esql.put_view": {
3+
"documentation": {
4+
"url": "https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-view-put",
5+
"description": "Creates a non-materialized VIEW for ESQL."
6+
},
7+
"stability": "experimental",
8+
"visibility": "feature_flag",
9+
"feature_flag": "esql_views",
10+
"headers": {
11+
"accept": [
12+
"application/json"
13+
],
14+
"content_type": [
15+
"application/json"
16+
]
17+
},
18+
"url": {
19+
"paths": [
20+
{
21+
"path": "/_query/view/{name}",
22+
"methods": [
23+
"PUT"
24+
],
25+
"parts": {
26+
"name": {
27+
"type": "string",
28+
"description": "The name of the view to create or update"
29+
}
30+
}
31+
}
32+
]
33+
},
34+
"body": {
35+
"description": "Use the `query` element to define the ES|QL query to use as a non-materialized VIEW.",
36+
"required": true
37+
}
38+
}
39+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
package org.elasticsearch.cluster.metadata;
10+
11+
import org.elasticsearch.common.Strings;
12+
import org.elasticsearch.common.io.stream.StreamInput;
13+
import org.elasticsearch.common.io.stream.StreamOutput;
14+
import org.elasticsearch.common.io.stream.Writeable;
15+
import org.elasticsearch.xcontent.ConstructingObjectParser;
16+
import org.elasticsearch.xcontent.ParseField;
17+
import org.elasticsearch.xcontent.ToXContentObject;
18+
import org.elasticsearch.xcontent.XContentBuilder;
19+
import org.elasticsearch.xcontent.XContentParser;
20+
21+
import java.io.IOException;
22+
import java.util.Objects;
23+
24+
/**
25+
* Represents a single view definition, which is simply a name and a query string.
26+
*/
27+
public final class View implements Writeable, ToXContentObject {
28+
private static final ParseField NAME = new ParseField("name");
29+
private static final ParseField QUERY = new ParseField("query");
30+
31+
// Parser that includes the name field (eg. serializing/deserializing the full object)
32+
static final ConstructingObjectParser<View, Void> PARSER = new ConstructingObjectParser<>(
33+
"view",
34+
false,
35+
(args, ctx) -> new View((String) args[0], (String) args[1])
36+
);
37+
38+
static {
39+
PARSER.declareString(ConstructingObjectParser.constructorArg(), NAME);
40+
PARSER.declareString(ConstructingObjectParser.constructorArg(), QUERY);
41+
}
42+
43+
// Parser that excludes the name field (eg. when the name is provided externally, in the URL path)
44+
public static ConstructingObjectParser<View, Void> parser(String name) {
45+
ConstructingObjectParser<View, Void> parser = new ConstructingObjectParser<>(
46+
"view",
47+
false,
48+
(args, ctx) -> new View(name, (String) args[0])
49+
);
50+
parser.declareString(ConstructingObjectParser.constructorArg(), QUERY);
51+
return parser;
52+
}
53+
54+
private final String name;
55+
private final String query;
56+
57+
public View(String name, String query) {
58+
this.name = Objects.requireNonNull(name, "view name must not be null");
59+
this.query = Objects.requireNonNull(query, "view query must not be null");
60+
}
61+
62+
public View(StreamInput in) throws IOException {
63+
this.name = in.readString();
64+
this.query = in.readString();
65+
}
66+
67+
public static View fromXContent(XContentParser parser) throws IOException {
68+
return PARSER.parse(parser, null);
69+
}
70+
71+
@Override
72+
public void writeTo(StreamOutput out) throws IOException {
73+
out.writeString(name);
74+
out.writeString(query);
75+
}
76+
77+
public String name() {
78+
return name;
79+
}
80+
81+
public String query() {
82+
return query;
83+
}
84+
85+
@Override
86+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
87+
builder.startObject();
88+
builder.field(NAME.getPreferredName(), name);
89+
builder.field(QUERY.getPreferredName(), query);
90+
builder.endObject();
91+
return builder;
92+
}
93+
94+
@Override
95+
public boolean equals(Object o) {
96+
if (this == o) return true;
97+
if (o == null || getClass() != o.getClass()) return false;
98+
View other = (View) o;
99+
return Objects.equals(name, other.name) && Objects.equals(query, other.query);
100+
}
101+
102+
@Override
103+
public int hashCode() {
104+
return Objects.hash(name, query);
105+
}
106+
107+
public String toString() {
108+
return Strings.toString(this);
109+
}
110+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
package org.elasticsearch.cluster.metadata;
10+
11+
import org.elasticsearch.TransportVersion;
12+
import org.elasticsearch.cluster.AbstractNamedDiffable;
13+
import org.elasticsearch.cluster.NamedDiff;
14+
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
15+
import org.elasticsearch.common.io.stream.StreamInput;
16+
import org.elasticsearch.common.io.stream.StreamOutput;
17+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
18+
import org.elasticsearch.core.Nullable;
19+
import org.elasticsearch.xcontent.ConstructingObjectParser;
20+
import org.elasticsearch.xcontent.ParseField;
21+
import org.elasticsearch.xcontent.ToXContent;
22+
import org.elasticsearch.xcontent.XContentParser;
23+
24+
import java.io.IOException;
25+
import java.util.Collections;
26+
import java.util.EnumSet;
27+
import java.util.HashMap;
28+
import java.util.Iterator;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.Objects;
32+
33+
/**
34+
* Encapsulates view definitions as custom metadata inside ProjectMetadata within cluster state.
35+
*/
36+
public final class ViewMetadata extends AbstractNamedDiffable<Metadata.ProjectCustom> implements Metadata.ProjectCustom {
37+
public static final String TYPE = "esql_view";
38+
public static final List<NamedWriteableRegistry.Entry> ENTRIES = List.of(
39+
new NamedWriteableRegistry.Entry(Metadata.ProjectCustom.class, TYPE, ViewMetadata::readFromStream),
40+
new NamedWriteableRegistry.Entry(NamedDiff.class, TYPE, in -> ViewMetadata.readDiffFrom(Metadata.ProjectCustom.class, TYPE, in))
41+
);
42+
public static final ViewMetadata EMPTY = new ViewMetadata(Collections.emptyMap());
43+
44+
private static final TransportVersion ESQL_VIEWS = TransportVersion.fromName("esql_views");
45+
private static final ParseField VIEWS = new ParseField("views");
46+
47+
private final Map<String, View> views;
48+
49+
@SuppressWarnings("unchecked")
50+
private static final ConstructingObjectParser<ViewMetadata, Void> PARSER = new ConstructingObjectParser<>(
51+
"view_metadata",
52+
true,
53+
(args, ctx) -> new ViewMetadata((Map<String, View>) args[0])
54+
);
55+
56+
static {
57+
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> {
58+
Map<String, View> views = new HashMap<>();
59+
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
60+
String name = p.currentName();
61+
views.put(name, View.fromXContent(p));
62+
}
63+
return views;
64+
}, VIEWS);
65+
}
66+
67+
public static ViewMetadata fromXContent(XContentParser parser) throws IOException {
68+
return PARSER.parse(parser, null);
69+
}
70+
71+
public static ViewMetadata readFromStream(StreamInput in) throws IOException {
72+
return new ViewMetadata(in.readMap(View::new));
73+
}
74+
75+
public ViewMetadata(Map<String, View> views) {
76+
this.views = Collections.unmodifiableMap(views);
77+
}
78+
79+
public Map<String, View> views() {
80+
return views;
81+
}
82+
83+
@Nullable
84+
public View getView(String name) {
85+
return views.get(name);
86+
}
87+
88+
@Override
89+
public EnumSet<Metadata.XContentContext> context() {
90+
return Metadata.ALL_CONTEXTS;
91+
}
92+
93+
@Override
94+
public TransportVersion getMinimalSupportedVersion() {
95+
return ESQL_VIEWS;
96+
}
97+
98+
@Override
99+
public String getWriteableName() {
100+
return TYPE;
101+
}
102+
103+
@Override
104+
public void writeTo(StreamOutput out) throws IOException {
105+
out.writeMap(this.views, StreamOutput::writeWriteable);
106+
}
107+
108+
@Override
109+
public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params ignored) {
110+
return ChunkedToXContentHelper.xContentObjectFields(VIEWS.getPreferredName(), views);
111+
}
112+
113+
@Override
114+
public boolean equals(Object o) {
115+
if (this == o) return true;
116+
if (o == null || getClass() != o.getClass()) return false;
117+
ViewMetadata that = (ViewMetadata) o;
118+
return views.equals(that.views);
119+
}
120+
121+
@Override
122+
public int hashCode() {
123+
return Objects.hash(views);
124+
}
125+
126+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9239000
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
esql_base_conversion,9238000
1+
esql_views,9239000

0 commit comments

Comments
 (0)