-
Notifications
You must be signed in to change notification settings - Fork 24.3k
/
Layout.java
132 lines (115 loc) · 4.48 KB
/
Layout.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* 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.planner;
import org.elasticsearch.xpack.ql.expression.NameId;
import org.elasticsearch.xpack.ql.expression.NamedExpression;
import org.elasticsearch.xpack.ql.type.DataType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Maintains the mapping from attribute ids to channels (block index).
*
* An attribute can only be mapped to exactly one channel but one channel can be mapped to multiple attributes.
*/
public interface Layout {
/**
* The values stored in the {@link Layout}, a channel id and a {@link DataType}.
*/
record ChannelAndType(int channel, DataType type) {}
/**
* A part of an "inverse" layout, a {@link Set} or {@link NameId}s and a {@link DataType}.
*/
record ChannelSet(Set<NameId> nameIds, DataType type) {}
/**
* @param id the attribute id
* @return the channel to which the specific attribute id is mapped or `null` if the attribute id does not exist in the layout.
*/
ChannelAndType get(NameId id);
/**
* @return the total number of channels in the layout.
*/
int numberOfChannels();
/**
* @return creates a builder to append to this layout.
*/
Layout.Builder builder();
/**
* Build a list whose index is each channel id and who's values are
* all link {@link NameId}s at that position and their {@link DataType}.
*/
List<ChannelSet> inverse();
/**
* Builder class for Layout. The builder ensures that layouts cannot be altered after creation (through references to the underlying
* map).
*/
class Builder {
private final List<ChannelSet> channels;
public Builder() {
channels = new ArrayList<>();
}
Builder(List<ChannelSet> channels) {
this.channels = channels;
}
/**
* Appends a new channel to the layout. The channel is mapped to one or more attribute ids.
*/
public Builder append(ChannelSet set) {
if (set.nameIds.size() < 1) {
throw new IllegalArgumentException("Channel must be mapped to at least one id.");
}
channels.add(set);
return this;
}
/**
* Appends a new channel to the layout. The channel is mapped to a single attribute id.
*/
public Builder append(NamedExpression attribute) {
return append(new ChannelSet(Set.of(attribute.id()), attribute.dataType()));
}
/**
* Appends many new channels to the layout. Each channel is mapped to a single attribute id.
*/
public Builder append(Collection<? extends NamedExpression> attributes) {
for (NamedExpression attribute : attributes) {
append(new ChannelSet(Set.of(attribute.id()), attribute.dataType()));
}
return this;
}
/**
* Build a new {@link Layout}.
*/
public Layout build() {
Map<NameId, ChannelAndType> layout = new HashMap<>();
int numberOfChannels = 0;
for (ChannelSet set : channels) {
int channel = numberOfChannels++;
for (NameId id : set.nameIds) {
ChannelAndType next = new ChannelAndType(channel, set.type);
ChannelAndType prev = layout.put(id, next);
// Do allow multiple name to point to the same channel - see https://github.com/elastic/elasticsearch/pull/100238
// if (prev != null) {
// throw new IllegalArgumentException("Name [" + id + "] is on two channels [" + prev + "] and [" + next + "]");
// }
}
}
return new DefaultLayout(Collections.unmodifiableMap(layout), numberOfChannels);
}
public void replace(NameId id, NameId id1) {
for (ChannelSet channel : this.channels) {
if (channel != null && channel.nameIds.contains(id)) {
channel.nameIds.remove(id);
channel.nameIds.add(id1);
}
}
}
}
}