Skip to content

Commit e5f7eab

Browse files
committed
ParameterizedQuery.and(), or()
1 parent a20a7b6 commit e5f7eab

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

mug-bigquery/src/main/java/com/google/mu/bigquery/ParameterizedQuery.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import static java.util.Collections.emptyMap;
44
import static java.util.Objects.requireNonNull;
5+
import static java.util.stream.Collectors.collectingAndThen;
6+
import static java.util.stream.Collectors.mapping;
57

68
import java.math.BigDecimal;
79
import java.time.Instant;
@@ -16,7 +18,10 @@
1618
import java.util.Map;
1719
import java.util.Objects;
1820
import java.util.Optional;
21+
import java.util.function.BiConsumer;
22+
import java.util.function.Predicate;
1923
import java.util.stream.Collector;
24+
import java.util.stream.Collector.Characteristics;
2025
import java.util.stream.Stream;
2126

2227
import com.google.cloud.bigquery.BigQuery.JobOption;
@@ -117,6 +122,16 @@ private ParameterizedQuery(
117122
*/
118123
public static ParameterizedQuery EMPTY = of("");
119124

125+
/**
126+
* Returns a query using a compile-time constant query with no parameters.
127+
*
128+
* @since 8.2
129+
*/
130+
@TemplateFormatMethod
131+
public static ParameterizedQuery of(@CompileTimeConstant @TemplateString String query) {
132+
return new ParameterizedQuery(query, emptyMap(), emptyMap());
133+
}
134+
120135
/**
121136
* Convenience method when you need to create the {@link ParameterizedQuery} inline, with both the
122137
* query template and the arguments.
@@ -225,6 +240,36 @@ public static Stream<ParameterizedQuery> enumConstants(Class<? extends Enum<?>>
225240
.map(e -> new ParameterizedQuery(e.name(), emptyMap(), emptyMap()));
226241
}
227242

243+
/**
244+
* A collector that joins boolean query snippets using {@code AND} operator. The
245+
* AND'ed sub-queries will be enclosed in pairs of parenthesis to avoid
246+
* ambiguity. If the input is empty, the result will be "TRUE".
247+
*
248+
* <p>Empty ParameterizedQuery elements are ignored and not joined.
249+
*
250+
* @since 8.2
251+
*/
252+
public static Collector<ParameterizedQuery, ?, ParameterizedQuery> and() {
253+
return collectingAndThen(
254+
nonEmptyQueries(mapping(ParameterizedQuery::parenthesized, joining(" AND "))),
255+
query -> query.query.isEmpty() ? of("TRUE") : query);
256+
}
257+
258+
/**
259+
* A collector that joins boolean query snippets using {@code OR} operator. The
260+
* OR'ed sub-queries will be enclosed in pairs of parenthesis to avoid
261+
* ambiguity. If the input is empty, the result will be "FALSE".
262+
*
263+
* <p>Empty ParameterizedQuery elements are ignored and not joined.
264+
*
265+
* @since 8.2
266+
*/
267+
public static Collector<ParameterizedQuery, ?, ParameterizedQuery> or() {
268+
return collectingAndThen(
269+
nonEmptyQueries(mapping(ParameterizedQuery::parenthesized, joining(" OR "))),
270+
query -> query.query.isEmpty() ? of("FALSE") : query);
271+
}
272+
228273
/**
229274
* Returns a collector that joins ParameterizedQuery elements using {@code delimiter}.
230275
*
@@ -391,4 +436,25 @@ public boolean equals(Object obj) {
391436
public String toString() {
392437
return query;
393438
}
439+
440+
private ParameterizedQuery parenthesized() {
441+
return new ParameterizedQuery("(" + query + ")", emptyMap(), emptyMap());
442+
}
443+
444+
private static <R> Collector<ParameterizedQuery, ?, R> nonEmptyQueries(
445+
Collector<ParameterizedQuery, ?, R> downstream) {
446+
return filtering(q -> !q.query.isEmpty(), downstream);
447+
}
448+
449+
// Not in Java 8
450+
private static <T, A, R> Collector<T, A, R> filtering(
451+
Predicate<? super T> filter, Collector<? super T, A, R> collector) {
452+
BiConsumer<A, ? super T> accumulator = collector.accumulator();
453+
return Collector.of(
454+
collector.supplier(),
455+
(a, input) -> {if (filter.test(input)) {accumulator.accept(a, input);}},
456+
collector.combiner(),
457+
collector.finisher(),
458+
collector.characteristics().toArray(new Characteristics[0]));
459+
}
394460
}

mug-bigquery/src/test/java/com/google/mu/bigquery/ParameterizedQueryTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.cloud.bigquery.QueryJobConfiguration;
2020
import com.google.cloud.bigquery.QueryParameterValue;
21+
import com.google.common.collect.ImmutableList;
2122
import com.google.common.testing.EqualsTester;
2223
import com.google.mu.util.StringFormat.Template;
2324

@@ -274,6 +275,80 @@ public void testJoining_parallel() {
274275
assertThat(query).isEqualTo(ParameterizedQuery.of("{v1}, {v2}, {v3}", 1, "2", 3));
275276
}
276277

278+
@Test
279+
public void andCollector_empty() {
280+
ImmutableList<ParameterizedQuery> queries = ImmutableList.of();
281+
assertThat(queries.stream().collect(ParameterizedQuery.and())).isEqualTo(ParameterizedQuery.of("TRUE"));
282+
}
283+
284+
@Test
285+
public void andCollector_singleCondition() {
286+
ImmutableList<ParameterizedQuery> queries = ImmutableList.of(ParameterizedQuery.of("a = 1"));
287+
assertThat(queries.stream().collect(ParameterizedQuery.and())).isEqualTo(ParameterizedQuery.of("(a = 1)"));
288+
}
289+
290+
@Test
291+
public void andCollector_twoConditions() {
292+
ImmutableList<ParameterizedQuery> queries =
293+
ImmutableList.of(ParameterizedQuery.of("a = 1"), ParameterizedQuery.of("b = 2 OR c = 3"));
294+
assertThat(queries.stream().collect(ParameterizedQuery.and()))
295+
.isEqualTo(ParameterizedQuery.of("(a = 1) AND (b = 2 OR c = 3)"));
296+
}
297+
298+
@Test
299+
public void andCollector_threeConditions() {
300+
ImmutableList<ParameterizedQuery> queries =
301+
ImmutableList.of(
302+
ParameterizedQuery.of("a = 1"), ParameterizedQuery.of("b = 2 OR c = 3"), ParameterizedQuery.of("d = 4"));
303+
assertThat(queries.stream().collect(ParameterizedQuery.and()))
304+
.isEqualTo(ParameterizedQuery.of("(a = 1) AND (b = 2 OR c = 3) AND (d = 4)"));
305+
}
306+
307+
@Test
308+
public void andCollector_ignoresEmpty() {
309+
ImmutableList<ParameterizedQuery> queries =
310+
ImmutableList.of(ParameterizedQuery.EMPTY, ParameterizedQuery.of("b = 2 OR c = 3"), ParameterizedQuery.of("d = 4"));
311+
assertThat(queries.stream().collect(ParameterizedQuery.and()))
312+
.isEqualTo(ParameterizedQuery.of("(b = 2 OR c = 3) AND (d = 4)"));
313+
}
314+
315+
@Test
316+
public void orCollector_empty() {
317+
ImmutableList<ParameterizedQuery> queries = ImmutableList.of();
318+
assertThat(queries.stream().collect(ParameterizedQuery.or())).isEqualTo(ParameterizedQuery.of("FALSE"));
319+
}
320+
321+
@Test
322+
public void orCollector_singleCondition() {
323+
ImmutableList<ParameterizedQuery> queries = ImmutableList.of(ParameterizedQuery.of("a = 1"));
324+
assertThat(queries.stream().collect(ParameterizedQuery.or())).isEqualTo(ParameterizedQuery.of("(a = 1)"));
325+
}
326+
327+
@Test
328+
public void orCollector_twoConditions() {
329+
ImmutableList<ParameterizedQuery> queries =
330+
ImmutableList.of(ParameterizedQuery.of("a = 1"), ParameterizedQuery.of("b = 2 AND c = 3"));
331+
assertThat(queries.stream().collect(ParameterizedQuery.or()))
332+
.isEqualTo(ParameterizedQuery.of("(a = 1) OR (b = 2 AND c = 3)"));
333+
}
334+
335+
@Test
336+
public void orCollector_threeConditions() {
337+
ImmutableList<ParameterizedQuery> queries =
338+
ImmutableList.of(
339+
ParameterizedQuery.of("a = 1"), ParameterizedQuery.of("b = 2 AND c = 3"), ParameterizedQuery.of("d = 4"));
340+
assertThat(queries.stream().collect(ParameterizedQuery.or()))
341+
.isEqualTo(ParameterizedQuery.of("(a = 1) OR (b = 2 AND c = 3) OR (d = 4)"));
342+
}
343+
344+
@Test
345+
public void orCollector_ignoresEmpty() {
346+
ImmutableList<ParameterizedQuery> queries =
347+
ImmutableList.of(ParameterizedQuery.EMPTY, ParameterizedQuery.of("b = 2 AND c = 3"), ParameterizedQuery.of("d = 4"));
348+
assertThat(queries.stream().collect(ParameterizedQuery.or()))
349+
.isEqualTo(ParameterizedQuery.of("(b = 2 AND c = 3) OR (d = 4)"));
350+
}
351+
277352
@Test
278353
public void testEquals() {
279354
new EqualsTester()

0 commit comments

Comments
 (0)