forked from elastic/elasticsearch
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce Layout class (elastic#433)
* Introduce `Layout` with `Layout.Builder` that encapsulates the layout data. The builder class ensures that the underlying maps are copied during layout creation. This avoids hard-to-spot issues from accidentally modifying layouts shared by another operator factory. * Aligns the layout interface with the common access patterns to layouts: Looking up channels from attribute ids and appending new channels.
- Loading branch information
Lukas Wegmann
committed
Dec 8, 2022
1 parent
614647a
commit e0349e8
Showing
2 changed files
with
175 additions
and
69 deletions.
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/Layout.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* | ||
* 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 java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
/** | ||
* 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 class Layout { | ||
|
||
private final Map<NameId, Integer> layout; | ||
private final int numberOfChannels; | ||
|
||
private Layout(Map<NameId, Integer> layout, int numberOfChannels) { | ||
this.layout = layout; | ||
this.numberOfChannels = numberOfChannels; | ||
} | ||
|
||
/** | ||
* @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. | ||
*/ | ||
public Integer getChannel(NameId id) { | ||
return layout.get(id); | ||
} | ||
|
||
/** | ||
* @return the total number of ids in the layout. | ||
*/ | ||
public int numberOfIds() { | ||
return layout.size(); | ||
} | ||
|
||
/** | ||
* @return the total number of channels in the layout. | ||
*/ | ||
public int numberOfChannels() { | ||
return numberOfChannels; | ||
} | ||
|
||
/** | ||
* @return creates a builder to append to this layout. | ||
*/ | ||
public Layout.Builder builder() { | ||
return new Layout.Builder(this); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "BlockLayout{" + "layout=" + layout + ", lastChannel=" + numberOfChannels + '}'; | ||
} | ||
|
||
/** | ||
* Builder class for Layout. The builder ensures that layouts cannot be altered after creation (through references to the underlying | ||
* map). | ||
*/ | ||
public static class Builder { | ||
|
||
private final List<Set<NameId>> channels; | ||
|
||
public Builder() { | ||
this.channels = new ArrayList<>(); | ||
} | ||
|
||
private Builder(Layout layout) { | ||
channels = IntStream.range(0, layout.numberOfChannels).<Set<NameId>>mapToObj(i -> new HashSet<>()).collect(Collectors.toList()); | ||
for (Map.Entry<NameId, Integer> entry : layout.layout.entrySet()) { | ||
channels.get(entry.getValue()).add(entry.getKey()); | ||
} | ||
} | ||
|
||
/** | ||
* Appends a new channel to the layout. The channel is mapped to a single attribute id. | ||
* @param id the attribute id | ||
*/ | ||
public void appendChannel(NameId id) { | ||
channels.add(Set.of(id)); | ||
} | ||
|
||
/** | ||
* Appends a new channel to the layout. The channel is mapped to one or more attribute ids. | ||
* @param ids the attribute ids | ||
*/ | ||
public void appendChannel(Set<NameId> ids) { | ||
if (ids.size() < 1) { | ||
throw new IllegalArgumentException("Channel must be mapped to at least one id."); | ||
} | ||
channels.add(ids); | ||
} | ||
|
||
public Layout build() { | ||
Map<NameId, Integer> layout = new HashMap<>(); | ||
int numberOfChannels = 0; | ||
for (Set<NameId> ids : this.channels) { | ||
int channel = numberOfChannels++; | ||
for (NameId id : ids) { | ||
layout.put(id, channel); | ||
} | ||
} | ||
return new Layout(Collections.unmodifiableMap(layout), numberOfChannels); | ||
} | ||
} | ||
} |
Oops, something went wrong.