1 | /* | |
2 | * Copyright OpenSearch Contributors | |
3 | * SPDX-License-Identifier: Apache-2.0 | |
4 | */ | |
5 | ||
6 | ||
7 | package org.opensearch.sql.planner.physical; | |
8 | ||
9 | import com.google.common.annotations.VisibleForTesting; | |
10 | import com.google.common.collect.ImmutableList; | |
11 | import com.google.common.collect.Streams; | |
12 | import java.util.AbstractMap; | |
13 | import java.util.Collections; | |
14 | import java.util.Comparator; | |
15 | import java.util.HashMap; | |
16 | import java.util.Iterator; | |
17 | import java.util.LinkedHashMap; | |
18 | import java.util.List; | |
19 | import java.util.Map; | |
20 | import java.util.stream.Collectors; | |
21 | import lombok.EqualsAndHashCode; | |
22 | import lombok.Getter; | |
23 | import lombok.RequiredArgsConstructor; | |
24 | import lombok.ToString; | |
25 | import org.opensearch.sql.ast.tree.RareTopN.CommandType; | |
26 | import org.opensearch.sql.data.model.ExprTupleValue; | |
27 | import org.opensearch.sql.data.model.ExprValue; | |
28 | import org.opensearch.sql.expression.Expression; | |
29 | import org.opensearch.sql.storage.bindingtuple.BindingTuple; | |
30 | ||
31 | /** | |
32 | * Group the all the input {@link BindingTuple} by {@link RareTopNOperator#groupByExprList}, | |
33 | * Calculate the rare result by using the {@link RareTopNOperator#fieldExprList}. | |
34 | */ | |
35 | @ToString | |
36 | @EqualsAndHashCode(callSuper = false) | |
37 | public class RareTopNOperator extends PhysicalPlan { | |
38 | ||
39 | @Getter | |
40 | private final PhysicalPlan input; | |
41 | @Getter | |
42 | private final CommandType commandType; | |
43 | @Getter | |
44 | private final Integer noOfResults; | |
45 | @Getter | |
46 | private final List<Expression> fieldExprList; | |
47 | @Getter | |
48 | private final List<Expression> groupByExprList; | |
49 | ||
50 | @EqualsAndHashCode.Exclude | |
51 | private final Group group; | |
52 | @EqualsAndHashCode.Exclude | |
53 | private Iterator<ExprValue> iterator; | |
54 | ||
55 | private static final Integer DEFAULT_NO_OF_RESULTS = 10; | |
56 | ||
57 | ||
58 | public RareTopNOperator(PhysicalPlan input, CommandType commandType, | |
59 | List<Expression> fieldExprList, List<Expression> groupByExprList) { | |
60 | this(input, commandType, DEFAULT_NO_OF_RESULTS, fieldExprList, groupByExprList); | |
61 | } | |
62 | ||
63 | /** | |
64 | * RareTopNOperator Constructor. | |
65 | * | |
66 | * @param input Input {@link PhysicalPlan} | |
67 | * @param commandType Enum for Rare/TopN command. | |
68 | * @param noOfResults Number of results | |
69 | * @param fieldExprList List of {@link Expression} | |
70 | * @param groupByExprList List of group by {@link Expression} | |
71 | */ | |
72 | public RareTopNOperator(PhysicalPlan input, CommandType commandType, int noOfResults, | |
73 | List<Expression> fieldExprList, | |
74 | List<Expression> groupByExprList) { | |
75 | this.input = input; | |
76 | this.commandType = commandType; | |
77 | this.noOfResults = noOfResults; | |
78 | this.fieldExprList = fieldExprList; | |
79 | this.groupByExprList = groupByExprList; | |
80 | this.group = new Group(); | |
81 | } | |
82 | ||
83 | @Override | |
84 | public <R, C> R accept(PhysicalPlanNodeVisitor<R, C> visitor, C context) { | |
85 |
1
1. accept : replaced return value with null for org/opensearch/sql/planner/physical/RareTopNOperator::accept → KILLED |
return visitor.visitRareTopN(this, context); |
86 | } | |
87 | ||
88 | @Override | |
89 | public List<PhysicalPlan> getChild() { | |
90 |
1
1. getChild : replaced return value with Collections.emptyList for org/opensearch/sql/planner/physical/RareTopNOperator::getChild → KILLED |
return Collections.singletonList(input); |
91 | } | |
92 | ||
93 | @Override | |
94 | public boolean hasNext() { | |
95 |
2
1. hasNext : replaced boolean return with false for org/opensearch/sql/planner/physical/RareTopNOperator::hasNext → KILLED 2. hasNext : replaced boolean return with true for org/opensearch/sql/planner/physical/RareTopNOperator::hasNext → KILLED |
return iterator.hasNext(); |
96 | } | |
97 | ||
98 | @Override | |
99 | public ExprValue next() { | |
100 |
1
1. next : replaced return value with null for org/opensearch/sql/planner/physical/RareTopNOperator::next → KILLED |
return iterator.next(); |
101 | } | |
102 | ||
103 | @Override | |
104 | public void open() { | |
105 |
1
1. open : removed call to org/opensearch/sql/planner/physical/PhysicalPlan::open → SURVIVED |
super.open(); |
106 |
1
1. open : negated conditional → KILLED |
while (input.hasNext()) { |
107 |
1
1. open : removed call to org/opensearch/sql/planner/physical/RareTopNOperator$Group::push → KILLED |
group.push(input.next()); |
108 | } | |
109 | iterator = group.result().iterator(); | |
110 | } | |
111 | ||
112 | @VisibleForTesting | |
113 | @RequiredArgsConstructor | |
114 | public class Group { | |
115 | ||
116 | private final Map<Key, Map<Key, Integer>> groupListMap = new HashMap<>(); | |
117 | ||
118 | /** | |
119 | * Push the BindingTuple to Group. | |
120 | */ | |
121 | public void push(ExprValue inputValue) { | |
122 | Key groupKey = new Key(inputValue, groupByExprList); | |
123 | Key fieldKey = new Key(inputValue, fieldExprList); | |
124 | groupListMap.computeIfAbsent(groupKey, k -> { | |
125 | Map<Key, Integer> map = new HashMap<>(); | |
126 | map.put(fieldKey, 1); | |
127 |
1
1. lambda$push$0 : replaced return value with Collections.emptyMap for org/opensearch/sql/planner/physical/RareTopNOperator$Group::lambda$push$0 → KILLED |
return map; |
128 | }); | |
129 | groupListMap.computeIfPresent(groupKey, (key, map) -> { | |
130 |
1
1. lambda$push$1 : replaced Integer return value with 0 for org/opensearch/sql/planner/physical/RareTopNOperator$Group::lambda$push$1 → SURVIVED |
map.computeIfAbsent(fieldKey, f -> 1); |
131 | map.computeIfPresent(fieldKey, (field, count) -> { | |
132 |
2
1. lambda$push$2 : replaced Integer return value with 0 for org/opensearch/sql/planner/physical/RareTopNOperator$Group::lambda$push$2 → SURVIVED 2. lambda$push$2 : Replaced integer addition with subtraction → KILLED |
return count + 1; |
133 | }); | |
134 |
1
1. lambda$push$3 : replaced return value with Collections.emptyMap for org/opensearch/sql/planner/physical/RareTopNOperator$Group::lambda$push$3 → KILLED |
return map; |
135 | }); | |
136 | } | |
137 | ||
138 | /** | |
139 | * Get the list of {@link BindingTuple} for each group. | |
140 | */ | |
141 | public List<ExprValue> result() { | |
142 | ImmutableList.Builder<ExprValue> resultBuilder = new ImmutableList.Builder<>(); | |
143 | ||
144 |
1
1. result : removed call to java/util/Map::forEach → KILLED |
groupListMap.forEach((groups, fieldMap) -> { |
145 | Map<String, ExprValue> map = new LinkedHashMap<>(); | |
146 | List<Key> result = find(fieldMap); | |
147 |
1
1. lambda$result$5 : removed call to java/util/List::forEach → KILLED |
result.forEach(field -> { |
148 |
1
1. lambda$result$4 : removed call to java/util/Map::putAll → KILLED |
map.putAll(groups.keyMap(groupByExprList)); |
149 |
1
1. lambda$result$4 : removed call to java/util/Map::putAll → KILLED |
map.putAll(field.keyMap(fieldExprList)); |
150 | resultBuilder.add(ExprTupleValue.fromExprValueMap(map)); | |
151 | }); | |
152 | }); | |
153 | ||
154 |
1
1. result : replaced return value with Collections.emptyList for org/opensearch/sql/planner/physical/RareTopNOperator$Group::result → KILLED |
return resultBuilder.build(); |
155 | } | |
156 | ||
157 | /** | |
158 | * Get a list of result. | |
159 | */ | |
160 | public List<Key> find(Map<Key, Integer> map) { | |
161 | Comparator<Map.Entry<Key, Integer>> valueComparator; | |
162 |
1
1. find : negated conditional → KILLED |
if (CommandType.TOP.equals(commandType)) { |
163 | valueComparator = Map.Entry.comparingByValue(Comparator.reverseOrder()); | |
164 | } else { | |
165 | valueComparator = Map.Entry.comparingByValue(); | |
166 | } | |
167 | ||
168 |
1
1. find : replaced return value with Collections.emptyList for org/opensearch/sql/planner/physical/RareTopNOperator$Group::find → KILLED |
return map.entrySet().stream().sorted(valueComparator).limit(noOfResults) |
169 | .map(Map.Entry::getKey).collect(Collectors.toList()); | |
170 | } | |
171 | } | |
172 | ||
173 | /** | |
174 | * Key. | |
175 | */ | |
176 | @EqualsAndHashCode | |
177 | @VisibleForTesting | |
178 | public class Key { | |
179 | ||
180 | private final List<ExprValue> valueList; | |
181 | ||
182 | /** | |
183 | * Key constructor. | |
184 | */ | |
185 | public Key(ExprValue value, List<Expression> exprList) { | |
186 | this.valueList = exprList.stream() | |
187 |
1
1. lambda$new$0 : replaced return value with null for org/opensearch/sql/planner/physical/RareTopNOperator$Key::lambda$new$0 → KILLED |
.map(expr -> expr.valueOf(value.bindingTuples())).collect(Collectors.toList()); |
188 | } | |
189 | ||
190 | /** | |
191 | * Return the Map of key and key value. | |
192 | */ | |
193 | public Map<String, ExprValue> keyMap(List<Expression> exprList) { | |
194 | ||
195 |
1
1. keyMap : replaced return value with Collections.emptyMap for org/opensearch/sql/planner/physical/RareTopNOperator$Key::keyMap → KILLED |
return Streams.zip( |
196 | exprList.stream().map( | |
197 |
1
1. lambda$keyMap$1 : replaced return value with "" for org/opensearch/sql/planner/physical/RareTopNOperator$Key::lambda$keyMap$1 → KILLED |
expression -> expression.toString()), |
198 | valueList.stream(), | |
199 | AbstractMap.SimpleEntry::new | |
200 |
2
1. lambda$keyMap$2 : replaced return value with "" for org/opensearch/sql/planner/physical/RareTopNOperator$Key::lambda$keyMap$2 → KILLED 2. lambda$keyMap$3 : replaced return value with null for org/opensearch/sql/planner/physical/RareTopNOperator$Key::lambda$keyMap$3 → KILLED |
).collect(Collectors.toMap(key -> key.getKey(), key -> key.getValue())); |
201 | } | |
202 | } | |
203 | ||
204 | } | |
Mutations | ||
85 |
1.1 |
|
90 |
1.1 |
|
95 |
1.1 2.2 |
|
100 |
1.1 |
|
105 |
1.1 |
|
106 |
1.1 |
|
107 |
1.1 |
|
127 |
1.1 |
|
130 |
1.1 |
|
132 |
1.1 2.2 |
|
134 |
1.1 |
|
144 |
1.1 |
|
147 |
1.1 |
|
148 |
1.1 |
|
149 |
1.1 |
|
154 |
1.1 |
|
162 |
1.1 |
|
168 |
1.1 |
|
187 |
1.1 |
|
195 |
1.1 |
|
197 |
1.1 |
|
200 |
1.1 2.2 |