Skip to content

Commit c70c2d2

Browse files
authored
Add query building API for the $fill aggregation pipeline stage (#965)
JAVA-4394
1 parent bb4f8f2 commit c70c2d2

21 files changed

+1155
-46
lines changed

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

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.mongodb.client.model;
1818

1919
import com.mongodb.MongoNamespace;
20+
import com.mongodb.client.model.fill.FillComputation;
21+
import com.mongodb.client.model.fill.FillOptions;
2022
import com.mongodb.client.model.search.SearchOperator;
2123
import com.mongodb.client.model.search.SearchCollector;
2224
import com.mongodb.client.model.search.SearchOptions;
@@ -26,6 +28,7 @@
2628
import org.bson.BsonDocumentWriter;
2729
import org.bson.BsonInt32;
2830
import org.bson.BsonString;
31+
import org.bson.BsonType;
2932
import org.bson.BsonValue;
3033
import org.bson.codecs.configuration.CodecRegistry;
3134
import org.bson.conversions.Bson;
@@ -35,10 +38,12 @@
3538
import java.util.Objects;
3639

3740
import static com.mongodb.assertions.Assertions.assertTrue;
41+
import static com.mongodb.assertions.Assertions.isTrueArgument;
42+
import static com.mongodb.assertions.Assertions.notNull;
3843
import static com.mongodb.client.model.search.SearchOptions.searchOptions;
3944
import static com.mongodb.internal.Iterables.concat;
45+
import static com.mongodb.internal.client.model.Util.sizeAtLeast;
4046
import static java.util.Arrays.asList;
41-
import static org.bson.assertions.Assertions.notNull;
4247

4348
/**
4449
* Builders for aggregation pipeline stages.
@@ -656,6 +661,60 @@ public static <TExpression> Bson setWindowFields(@Nullable final TExpression par
656661
return new SetWindowFieldsStage<>(partitionBy, sortBy, output);
657662
}
658663

664+
/**
665+
* Creates a {@code $fill} pipeline stage, which assigns values to fields when they are {@link BsonType#NULL Null} or missing.
666+
*
667+
* @param options The fill options.
668+
* @param output The {@link FillComputation}.
669+
* @param moreOutput More {@link FillComputation}s.
670+
* @return The requested pipeline stage.
671+
* @mongodb.driver.manual reference/operator/aggregation/fill/ $fill
672+
* @mongodb.server.release 5.3
673+
* @since 4.7
674+
*/
675+
public static Bson fill(final FillOptions options, final FillComputation output, final FillComputation... moreOutput) {
676+
return fill(options, concat(notNull("output", output), moreOutput));
677+
}
678+
679+
/**
680+
* Creates a {@code $fill} pipeline stage, which assigns values to fields when they are {@link BsonType#NULL Null} or missing.
681+
*
682+
* @param options The fill options.
683+
* @param output The non-empty {@link FillComputation}s.
684+
* @return The requested pipeline stage.
685+
* @mongodb.driver.manual reference/operator/aggregation/fill/ $fill
686+
* @mongodb.server.release 5.3
687+
* @since 4.7
688+
*/
689+
public static Bson fill(final FillOptions options, final Iterable<? extends FillComputation> output) {
690+
notNull("options", options);
691+
notNull("output", output);
692+
isTrueArgument("output must not be empty", sizeAtLeast(output, 1));
693+
return new Bson() {
694+
@Override
695+
public <TDocument> BsonDocument toBsonDocument(final Class<TDocument> documentClass, final CodecRegistry codecRegistry) {
696+
BsonDocument fillSpecificationDoc = new BsonDocument();
697+
fillSpecificationDoc.putAll(options.toBsonDocument(documentClass, codecRegistry));
698+
BsonDocument outputDoc = new BsonDocument();
699+
for (final FillComputation computation : output) {
700+
BsonDocument computationDoc = computation.toBsonDocument(documentClass, codecRegistry);
701+
assertTrue(computationDoc.size() == 1);
702+
outputDoc.putAll(computationDoc);
703+
}
704+
fillSpecificationDoc.append("output", outputDoc);
705+
return new BsonDocument("$fill", fillSpecificationDoc);
706+
}
707+
708+
@Override
709+
public String toString() {
710+
return "Stage{name='$fill'"
711+
+ ", options=" + options
712+
+ ", output=" + output
713+
+ '}';
714+
}
715+
};
716+
}
717+
659718
/**
660719
* Creates a {@code $search} pipeline stage supported by MongoDB Atlas.
661720
* You may use the {@code $meta: "searchScore"} expression, e.g., via {@link Projections#metaSearchScore(String)},

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,43 @@ public static WindowedComputation denseRank(final String path) {
784784
return simpleParameterWindowFunction(path, "$denseRank", null, null);
785785
}
786786

787+
/**
788+
* Builds a computation of the last observed non-{@link BsonType#NULL Null} evaluation result of the {@code expression}.
789+
*
790+
* @param path The output field path.
791+
* @param expression The expression.
792+
* @param <TExpression> The expression type.
793+
* @return The constructed windowed computation.
794+
* @mongodb.driver.manual reference/operator/aggregation/locf/ $locf
795+
* @since 4.7
796+
* @mongodb.server.release 5.2
797+
*/
798+
public static <TExpression> WindowedComputation locf(final String path, final TExpression expression) {
799+
notNull("path", path);
800+
notNull("expression", expression);
801+
return simpleParameterWindowFunction(path, "$locf", expression, null);
802+
}
803+
804+
/**
805+
* Builds a computation of a value that is equal to the evaluation result of the {@code expression} when it is non-{@link BsonType#NULL Null},
806+
* or to the linear interpolation of surrounding evaluation results of the {@code expression} when the result is {@link BsonType#NULL Null}.
807+
* <p>
808+
* {@linkplain Aggregates#setWindowFields(Object, Bson, Iterable) Sorting} is required.</p>
809+
*
810+
* @param path The output field path.
811+
* @param expression The expression.
812+
* @param <TExpression> The expression type.
813+
* @return The constructed windowed computation.
814+
* @mongodb.driver.manual reference/operator/aggregation/linearFill/ $linearFill
815+
* @since 4.7
816+
* @mongodb.server.release 5.3
817+
*/
818+
public static <TExpression> WindowedComputation linearFill(final String path, final TExpression expression) {
819+
notNull("path", path);
820+
notNull("expression", expression);
821+
return simpleParameterWindowFunction(path, "$linearFill", expression, null);
822+
}
823+
787824
private static WindowedComputation simpleParameterWindowFunction(final String path, final String functionName,
788825
@Nullable final Object expression,
789826
@Nullable final Window window) {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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.fill;
17+
18+
import com.mongodb.annotations.Evolving;
19+
import com.mongodb.client.model.Aggregates;
20+
import com.mongodb.client.model.WindowedComputations;
21+
import org.bson.Document;
22+
import org.bson.conversions.Bson;
23+
24+
import static com.mongodb.assertions.Assertions.notNull;
25+
26+
/**
27+
* The core part of the {@code $fill} pipeline stage of an aggregation pipeline.
28+
* A pair of an expression/method and a path to a field to be filled with evaluation results of the expression/method.
29+
*
30+
* @see Aggregates#fill(FillOptions, Iterable)
31+
* @see Aggregates#fill(FillOptions, FillComputation, FillComputation...)
32+
* @mongodb.server.release 5.3
33+
* @since 4.7
34+
*/
35+
@Evolving
36+
public interface FillComputation extends Bson {
37+
/**
38+
* Returns a {@link FillComputation} that uses the specified {@code expression}.
39+
*
40+
* @param field The field to fill.
41+
* @param expression The expression.
42+
* @param <TExpression> The {@code expression} type.
43+
* @return The requested {@link FillComputation}.
44+
* @mongodb.driver.manual core/document/#dot-notation Dot notation
45+
*/
46+
static <TExpression> ValueFillComputation value(final String field, TExpression expression) {
47+
return new FillConstructibleBsonElement(notNull("field", field),
48+
new Document("value", (notNull("expression", expression))));
49+
}
50+
51+
/**
52+
* Returns a {@link FillComputation} that uses the {@link WindowedComputations#locf(String, Object) locf} method.
53+
*
54+
* @param field The field to fill.
55+
* @return The requested {@link FillComputation}.
56+
* @mongodb.driver.manual core/document/#dot-notation Dot notation
57+
*/
58+
static LocfFillComputation locf(final String field) {
59+
return new FillConstructibleBsonElement(notNull("field", field),
60+
new Document("method", "locf"));
61+
}
62+
63+
/**
64+
* Returns a {@link FillComputation} that uses the {@link WindowedComputations#linearFill(String, Object) linear} method.
65+
* <p>
66+
* {@linkplain FillOptions#sortBy(Bson) Sorting} is required.</p>
67+
*
68+
* @param field The field to fill.
69+
* @return The requested {@link FillComputation}.
70+
* @mongodb.driver.manual core/document/#dot-notation Dot notation
71+
*/
72+
static LinearFillComputation linear(final String field) {
73+
return new FillConstructibleBsonElement(notNull("field", field),
74+
new Document("method", "linear"));
75+
}
76+
77+
/**
78+
* Creates a {@link FillComputation} from a {@link Bson} in situations when there is no builder method
79+
* that better satisfies your needs.
80+
* This method cannot be used to validate the syntax.
81+
* <p>
82+
* <i>Example</i><br>
83+
* The following code creates two functionally equivalent {@link FillComputation}s,
84+
* though they may not be {@linkplain Object#equals(Object) equal}.
85+
* <pre>{@code
86+
* FillComputation field1 = FillComputation.locf("fieldName");
87+
* FillComputation field2 = FillComputation.of(new Document("fieldName", new Document("method", "locf")));
88+
* }</pre>
89+
*
90+
* @param fill A {@link Bson} representing the required {@link FillComputation}.
91+
* @return The requested {@link FillComputation}.
92+
*/
93+
static FillComputation of(final Bson fill) {
94+
return new FillConstructibleBsonElement(notNull("fill", fill));
95+
}
96+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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.fill;
17+
18+
import com.mongodb.internal.client.model.AbstractConstructibleBson;
19+
import org.bson.Document;
20+
import org.bson.conversions.Bson;
21+
22+
import static com.mongodb.assertions.Assertions.notNull;
23+
import static com.mongodb.internal.client.model.Util.sizeAtLeast;
24+
25+
final class FillConstructibleBson extends AbstractConstructibleBson<FillConstructibleBson> implements FillOptions {
26+
static final FillConstructibleBson EMPTY_IMMUTABLE = new FillConstructibleBson(AbstractConstructibleBson.EMPTY_IMMUTABLE);
27+
28+
FillConstructibleBson(final Bson base) {
29+
super(base);
30+
}
31+
32+
private FillConstructibleBson(final Bson base, final Document appended) {
33+
super(base, appended);
34+
}
35+
36+
@Override
37+
protected FillConstructibleBson newSelf(final Bson base, final Document appended) {
38+
return new FillConstructibleBson(base, appended);
39+
}
40+
41+
@Override
42+
public <TExpression> FillOptions partitionBy(final TExpression expression) {
43+
notNull("expression", expression);
44+
return newMutated(doc -> {
45+
doc.remove("partitionByFields");
46+
doc.append("partitionBy", expression);
47+
});
48+
}
49+
50+
@Override
51+
public FillOptions partitionByFields(final Iterable<String> fields) {
52+
notNull("fields", fields);
53+
return newMutated(doc -> {
54+
doc.remove("partitionBy");
55+
if (sizeAtLeast(fields, 1)) {
56+
doc.append("partitionByFields", fields);
57+
} else {
58+
doc.remove("partitionByFields");
59+
}
60+
});
61+
}
62+
63+
@Override
64+
public FillOptions sortBy(final Bson sortBy) {
65+
return newAppended("sortBy", notNull("sortBy", sortBy));
66+
}
67+
68+
@Override
69+
public FillOptions option(final String name, final Object value) {
70+
return newAppended(notNull("name", name), notNull("value", value));
71+
}
72+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.fill;
17+
18+
import com.mongodb.internal.client.model.AbstractConstructibleBsonElement;
19+
import org.bson.conversions.Bson;
20+
21+
final class FillConstructibleBsonElement extends AbstractConstructibleBsonElement<FillConstructibleBsonElement> implements
22+
ValueFillComputation, LocfFillComputation, LinearFillComputation {
23+
FillConstructibleBsonElement(final String name, final Bson value) {
24+
super(name, value);
25+
}
26+
27+
FillConstructibleBsonElement(final Bson baseElement) {
28+
super(baseElement);
29+
}
30+
31+
private FillConstructibleBsonElement(final Bson baseElement, final Bson appendedElementValue) {
32+
super(baseElement, appendedElementValue);
33+
}
34+
35+
@Override
36+
protected FillConstructibleBsonElement newSelf(final Bson baseElement, final Bson appendedElementValue) {
37+
return new FillConstructibleBsonElement(baseElement, appendedElementValue);
38+
}
39+
}

0 commit comments

Comments
 (0)