Skip to content

Commit 78b27e8

Browse files
authored
Add builders for window functions (#747)
JAVA-3866
1 parent 9014c64 commit 78b27e8

File tree

21 files changed

+3083
-12
lines changed

21 files changed

+3083
-12
lines changed

bson/src/main/org/bson/BsonNumber.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.bson.types.Decimal128;
2020

2121
/**
22-
* Base class for the three numeric BSON types. This class mirrors the functionality provided by {@code java.lang.Number}.
22+
* Base class for the numeric BSON types. This class mirrors the functionality provided by {@code java.lang.Number}.
2323
*
2424
* @since 3.0
2525
*/

config/codenarc/codenarc.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@
128128
<rule-config name='NestedBlockDepth'>
129129
<property name='doNotApplyToFilesMatching' value='.*Specification.groovy'/>
130130
</rule-config>
131+
<rule-config name='ClassSize'>
132+
<property name='doNotApplyToFilesMatching' value='.*Specification.groovy'/>
133+
</rule-config>
131134
<exclude name='CrapMetric'/>
132135
<exclude name='AbcMetric'/>
133136
<exclude name='MethodSize'/>
@@ -148,4 +151,3 @@
148151
</ruleset-ref>
149152

150153
</ruleset>
151-

docs/reference/content/builders/aggregation.md

+29-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ title = "Aggregates"
1010

1111
## Aggregates
1212

13-
The [`Aggregates`]({{< apiref "mongodb-driver-core" "com/mongodb/client/model/Aggregates" >}}) class provides static factory methods that build [aggregation
14-
pipeline operators]({{< docsref "reference/operator/aggregation/" >}}). Each method returns an instance of the
13+
The [`Aggregates`]({{< apiref "mongodb-driver-core" "com/mongodb/client/model/Aggregates" >}}) class provides static factory methods
14+
that build [aggregation pipeline stages]({{< docsref "meta/aggregation-quick-reference/#stages" >}}).
15+
Each method returns an instance of the
1516
[`Bson`]({{< relref "bson/documents.md#bson" >}}) type, which can in turn be passed to the `aggregate` method of `MongoCollection`.
1617

1718
For brevity, you may choose to import the methods of the `Aggregates` class statically:
@@ -427,9 +428,34 @@ This stage returns a document that looks like this:
427428
428429
```
429430
431+
### SetWindowFields
432+
433+
{{% note class="important" %}}
434+
Support for `$setWindowFields` is in beta. Backwards-breaking changes may be made before the final release.
435+
{{% /note %}}
436+
437+
The [`$setWindowFields`](https://dochub.mongodb.org/core/window-functions-set-window-fields) pipeline stage
438+
allows using window operators. This stage partitions the input documents similarly to the [`$group`](#group) pipeline stage,
439+
optionally sorts them, computes fields in the documents by computing window functions over windows specified per function
440+
(a window is a subset of a partition), and outputs the documents. The important difference from the `$group` pipeline stage is that
441+
documents belonging to the same partition or window are not folded into a single document.
442+
443+
The driver includes the [`WindowedComputations`]({{< apiref "mongodb-driver-core" "com/mongodb/client/model/WindowedComputations" >}})
444+
class with factory methods for supported window operators.
445+
446+
This example computes the accumulated rainfall and the average temperature over the past month per each locality
447+
from more fine-grained measurements presented via the `rainfall` and `temperature` fields:
448+
449+
```java
450+
Window pastMonth = Windows.timeRange(-1, Windows.Bound.CURRENT, MongoTimeUnit.MONTH);
451+
setWindowFields("$localityId", Sorts.ascending("measurementDateTime"),
452+
WindowedComputations.sum("monthlyRainfall", "$rainfall", pastMonth),
453+
WindowedComputations.avg("monthlyAvgTemp", "$temperature", pastMonth));
454+
```
455+
430456
### Creating a Pipeline
431457
432-
The above pipeline operators are typically combined into a list and passed to the `aggregate` method of a `MongoCollection`. For instance:
458+
The above pipeline stages are typically combined into a list and passed to the `aggregate` method of a `MongoCollection`. For instance:
433459
434460
```java
435461
collection.aggregate(Arrays.asList(match(eq("author", "Dave")),

docs/reference/content/driver-scala/builders/aggregation.md

+31-5
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ title = "Aggregation"
1010

1111
## Aggregation
1212

13-
The [`Aggregates`]({{< apiref "mongo-scala-driver" "org/mongodb/scala/model/Aggregates$" >}}) class provides static factory methods that build [aggregation
14-
pipeline operators]({{< docsref "reference/operator/aggregation/" >}}). Each method returns an instance of the
13+
The [`Aggregates`]({{< apiref "mongo-scala-driver" "org/mongodb/scala/model/Aggregates$" >}}) class provides static factory methods
14+
that build [aggregation pipeline stages]({{< docsref "meta/aggregation-quick-reference/#stages" >}}).
15+
Each method returns an instance of the
1516
[`Bson`]({{< relref "bson/documents.md#bson" >}}) type, which can in turn be passed to the `aggregate` method of `MongoCollection`.
1617

1718
For brevity, you may choose to import the methods of the `Aggregates` class statically:
@@ -129,9 +130,9 @@ expression and outputs to the next stage a document for each distinct grouping.
129130
expression on which to group, and zero or more
130131
[accumulators]({{< docsref "reference/operator/aggregation/group/#accumulator-operator" >}}) which are evaluated for each
131132
grouping. To simplify the expression of accumulators, the driver includes an
132-
[`Accumulators`]({{< apiref "mongo-scala-driver" "org/mongodb/scala/model/Aggregates$" >}}) class with static factory methods for each of the supported
133-
accumulators. In the example below, it's assumed that the `sum` and `avg` methods of the `Accumulators` class have been statically
134-
imported.
133+
[`Accumulators`]({{< apiref "mongo-scala-driver" "org/mongodb/scala/model/Accumulators$" >}}) singleton object with factory methods
134+
for each of the supported accumulators.
135+
In the example below, it's assumed that the `sum` and `avg` methods of the `Accumulators` class have been statically imported.
135136

136137
This example groups documents by the value of the `customerId` field, and for each group accumulates the sum and average of the values of
137138
the `quantity` field into the `totalQuantity` and `averageQuantity` fields, respectively.
@@ -175,6 +176,31 @@ This example writes the pipeline to the `authors` collection:
175176
out("authors")
176177
```
177178

179+
### SetWindowFields
180+
181+
{{% note class="important" %}}
182+
Support for `$setWindowFields` is in beta. Backwards-breaking changes may be made before the final release.
183+
{{% /note %}}
184+
185+
The [`$setWindowFields`](https://dochub.mongodb.org/core/window-functions-set-window-fields) pipeline stage
186+
allows using window operators. This stage partitions the input documents similarly to the [`$group`](#group) pipeline stage,
187+
optionally sorts them, computes fields in the documents by computing window functions over windows specified per function
188+
(a window is a subset of a partition), and outputs the documents. The important difference from the `$group` pipeline stage is that
189+
documents belonging to the same partition or window are not folded into a single document.
190+
191+
The driver includes the [`WindowedComputations`]({{< apiref "mongo-scala-driver" "org/mongodb/scala/model/WindowedComputations$" >}})
192+
singleton object with factory methods for supported window operators.
193+
194+
This example computes the accumulated rainfall and the average temperature over the past month per each locality
195+
from more fine-grained measurements presented via the `rainfall` and `temperature` fields:
196+
197+
```scala
198+
val pastMonth: Window = Windows.timeRange(-1, Windows.Bound.CURRENT, MongoTimeUnit.MONTH)
199+
setWindowFields("$localityId", Sorts.ascending("measurementDateTime"),
200+
WindowedComputations.sum("monthlyRainfall", "$rainfall", pastMonth),
201+
WindowedComputations.avg("monthlyAvgTemp", "$temperature", pastMonth))
202+
```
203+
178204
### Creating a Pipeline
179205

180206
The above pipeline operators are typically combined into a list and passed to the `aggregate` method of a `MongoCollection`. For instance:

docs/reference/content/whats-new.md

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ New features of the 4.3 Java driver release include:
2828
[Netty](https://netty.io/) [`io.netty.handler.ssl.SslContext`]({{< nettyapiref "io/netty/handler/ssl/SslContext.html" >}}),
2929
which may be used as a convenient way to utilize [OpenSSL](https://www.openssl.org/) as an alternative
3030
to the TLS/SSL protocol implementation in a JDK.
31+
* Added [builders]({{< apiref "mongodb-driver-core" "com/mongodb/client/model/Aggregates.html#setWindowFields(TExpression,org.bson.conversions.Bson,java.util.List)" >}})
32+
for the new [`$setWindowFields`](https://dochub.mongodb.org/core/window-functions-set-window-fields)
33+
pipeline stage of an aggregation pipeline.
3134

3235
# What's new in 4.2
3336

driver-core/src/main/com/mongodb/client/model/Aggregates.java

+122
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb.client.model;
1818

1919
import com.mongodb.MongoNamespace;
20+
import com.mongodb.annotations.Beta;
2021
import com.mongodb.lang.Nullable;
2122
import org.bson.BsonBoolean;
2223
import org.bson.BsonDocument;
@@ -28,6 +29,7 @@
2829
import org.bson.conversions.Bson;
2930

3031
import java.util.List;
32+
import java.util.Objects;
3133

3234
import static java.util.Arrays.asList;
3335
import static org.bson.assertions.Assertions.notNull;
@@ -594,6 +596,60 @@ public static Bson sample(final int size) {
594596
return new BsonDocument("$sample", new BsonDocument("size", new BsonInt32(size)));
595597
}
596598

599+
/**
600+
* Creates a {@code $setWindowFields} pipeline stage, which allows using window operators.
601+
* This stage partitions the input documents similarly to the {@link #group(Object, List) $group} pipeline stage,
602+
* optionally sorts them, computes fields in the documents by computing window functions over {@linkplain Window windows} specified per
603+
* function, and outputs the documents. The important difference from the {@code $group} pipeline stage is that
604+
* documents belonging to the same partition or window are not folded into a single document.
605+
*
606+
* @param partitionBy Optional partitioning of data specified like {@code id} in {@link #group(Object, List)}.
607+
* If {@code null}, then all documents belong to the same partition.
608+
* @param sortBy Fields to sort by. The syntax is identical to {@code sort} in {@link #sort(Bson)} (see {@link Sorts}).
609+
* Sorting is required by certain functions and may be required by some windows (see {@link Windows} for more details).
610+
* Sorting is used only for the purpose of computing window functions and does not guarantee ordering
611+
* of the output documents.
612+
* @param output A nonempty array of {@linkplain WindowedComputation windowed computations}.
613+
* @param <TExpression> The {@code partitionBy} expression type.
614+
* @return The {@code $setWindowFields} pipeline stage.
615+
* @mongodb.driver.dochub core/window-functions-set-window-fields $setWindowFields
616+
* @mongodb.server.release 5.0
617+
* @since 4.3
618+
*/
619+
@Beta
620+
public static <TExpression> Bson setWindowFields(@Nullable final TExpression partitionBy, @Nullable final Bson sortBy,
621+
final WindowedComputation... output) {
622+
notNull("output", output);
623+
return setWindowFields(partitionBy, sortBy, asList(output));
624+
}
625+
626+
/**
627+
* Creates a {@code $setWindowFields} pipeline stage, which allows using window operators.
628+
* This stage partitions the input documents similarly to the {@link #group(Object, List) $group} pipeline stage,
629+
* optionally sorts them, computes fields in the documents by computing window functions over {@linkplain Window windows} specified per
630+
* function, and outputs the documents. The important difference from the {@code $group} pipeline stage is that
631+
* documents belonging to the same partition or window are not folded into a single document.
632+
*
633+
* @param partitionBy Optional partitioning of data specified like {@code id} in {@link #group(Object, List)}.
634+
* If {@code null}, then all documents belong to the same partition.
635+
* @param sortBy Fields to sort by. The syntax is identical to {@code sort} in {@link #sort(Bson)} (see {@link Sorts}).
636+
* Sorting is required by certain functions and may be required by some windows (see {@link Windows} for more details).
637+
* Sorting is used only for the purpose of computing window functions and does not guarantee ordering
638+
* of the output documents.
639+
* @param output A nonempty list of {@linkplain WindowedComputation windowed computations}.
640+
* @param <TExpression> The {@code partitionBy} expression type.
641+
* @return The {@code $setWindowFields} pipeline stage.
642+
* @mongodb.driver.dochub core/window-functions-set-window-fields $setWindowFields
643+
* @mongodb.server.release 5.0
644+
* @since 4.3
645+
*/
646+
@Beta
647+
public static <TExpression> Bson setWindowFields(@Nullable final TExpression partitionBy, @Nullable final Bson sortBy,
648+
final List<WindowedComputation> output) {
649+
notNull("output", output);
650+
return new SetWindowFieldsStage<>(partitionBy, sortBy, output);
651+
}
652+
597653
static void writeBucketOutput(final CodecRegistry codecRegistry, final BsonDocumentWriter writer,
598654
@Nullable final List<BsonField> output) {
599655
if (output != null) {
@@ -1490,6 +1546,72 @@ public String toString() {
14901546
}
14911547
}
14921548

1549+
private static final class SetWindowFieldsStage<TExpression> implements Bson {
1550+
@Nullable
1551+
private final TExpression partitionBy;
1552+
@Nullable
1553+
private final Bson sortBy;
1554+
private final List<WindowedComputation> output;
1555+
1556+
SetWindowFieldsStage(@Nullable final TExpression partitionBy, @Nullable final Bson sortBy, final List<WindowedComputation> output) {
1557+
this.partitionBy = partitionBy;
1558+
this.sortBy = sortBy;
1559+
this.output = output;
1560+
}
1561+
1562+
@Override
1563+
public <TDocument> BsonDocument toBsonDocument(final Class<TDocument> tDocumentClass, final CodecRegistry codecRegistry) {
1564+
BsonDocumentWriter writer = new BsonDocumentWriter(new BsonDocument());
1565+
writer.writeStartDocument();
1566+
writer.writeStartDocument("$setWindowFields");
1567+
if (partitionBy != null) {
1568+
writer.writeName("partitionBy");
1569+
BuildersHelper.encodeValue(writer, partitionBy, codecRegistry);
1570+
}
1571+
if (sortBy != null) {
1572+
writer.writeName("sortBy");
1573+
BuildersHelper.encodeValue(writer, sortBy, codecRegistry);
1574+
}
1575+
writer.writeStartDocument("output");
1576+
for (WindowedComputation windowedComputation : output) {
1577+
BsonField field = windowedComputation.toBsonField();
1578+
writer.writeName(field.getName());
1579+
BuildersHelper.encodeValue(writer, field.getValue(), codecRegistry);
1580+
}
1581+
writer.writeEndDocument(); // end output
1582+
writer.writeEndDocument(); // end $setWindowFields
1583+
writer.writeEndDocument();
1584+
return writer.getDocument();
1585+
}
1586+
1587+
@Override
1588+
public boolean equals(final Object o) {
1589+
if (this == o) {
1590+
return true;
1591+
}
1592+
if (o == null || getClass() != o.getClass()) {
1593+
return false;
1594+
}
1595+
final SetWindowFieldsStage<?> that = (SetWindowFieldsStage<?>) o;
1596+
return Objects.equals(partitionBy, that.partitionBy) && Objects.equals(sortBy, that.sortBy) && output.equals(that.output);
1597+
}
1598+
1599+
@Override
1600+
public int hashCode() {
1601+
return Objects.hash(partitionBy, sortBy, output);
1602+
}
1603+
1604+
@Override
1605+
public String toString() {
1606+
return "Stage{"
1607+
+ "name='$setWindowFields'"
1608+
+ ", partitionBy=" + partitionBy
1609+
+ ", sortBy=" + sortBy
1610+
+ ", output=" + output
1611+
+ '}';
1612+
}
1613+
}
1614+
14931615
private Aggregates() {
14941616
}
14951617
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.mongodb.client.model;
17+
18+
import com.mongodb.annotations.Beta;
19+
20+
/**
21+
* Units for specifying time-based bounds for {@linkplain Window windows} and output units for some time-based
22+
* {@linkplain WindowedComputation windowed computations}.
23+
*
24+
* @mongodb.server.release 5.0
25+
* @since 4.3
26+
*/
27+
@Beta
28+
public enum MongoTimeUnit {
29+
YEAR("year", false),
30+
QUARTER("quarter", false),
31+
MONTH("month", false),
32+
WEEK("week", true),
33+
DAY("day", true),
34+
HOUR("hour", true),
35+
MINUTE("minute", true),
36+
SECOND("second", true),
37+
MILLISECOND("millisecond", true);
38+
39+
private final String value;
40+
private final boolean fixed;
41+
42+
MongoTimeUnit(final String value, final boolean fixed) {
43+
this.value = value;
44+
this.fixed = fixed;
45+
}
46+
47+
String value() {
48+
return value;
49+
}
50+
51+
/**
52+
* Returns {@code true} iff the unit represents a fixed duration.
53+
* E.g., a minute is a fixed duration equal to 60_000 milliseconds, while the duration of a month varies.
54+
*/
55+
boolean fixed() {
56+
return fixed;
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.mongodb.client.model;
17+
18+
import com.mongodb.annotations.Beta;
19+
import org.bson.conversions.Bson;
20+
21+
import java.util.List;
22+
23+
/**
24+
* A subset of documents within a partition in the {@link Aggregates#setWindowFields(Object, Bson, List) $setWindowFields} pipeline stage
25+
* of an aggregation pipeline (see {@code partitionBy} in {@link Aggregates#setWindowFields(Object, Bson, List)}).
26+
*
27+
* @see Windows
28+
* @since 4.3
29+
*/
30+
@Beta
31+
public interface Window extends Bson {
32+
}

0 commit comments

Comments
 (0)