From 6d9c7d436fd2872b65f9fce05f877acd6e122c99 Mon Sep 17 00:00:00 2001 From: Almas Abdrazak Date: Tue, 23 Jun 2026 16:34:56 -0700 Subject: [PATCH 1/2] JAVA-6236 add builder API for facet without operator --- .../client/model/search/SearchCollector.java | 17 +++++++ .../model/search/SearchCollectorTest.java | 45 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java b/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java index 6f2c45b4961..571bff38044 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java @@ -54,6 +54,23 @@ static FacetSearchCollector facet(final SearchOperator operator, final Iterable< .append("facets", combineToBson(facets))); } + /** + * Returns a {@link SearchCollector} that groups results by values or ranges in the specified faceted fields and returns the count + * for each of those groups, faceting over the entire collection. + *

+ * Unlike {@link #facet(SearchOperator, Iterable)}, this method omits the search operator, so the facets are computed + * across all documents in the collection.

+ * + * @param facets The non-empty facet definitions. + * @return The requested {@link SearchCollector}. + * @mongodb.atlas.manual atlas-search/facet/ facet collector + */ + @Beta({Reason.CLIENT, Reason.SERVER}) + static FacetSearchCollector facet(final Iterable facets) { + notNull("facets", facets); + return new SearchConstructibleBsonElement("facet", new Document("facets", combineToBson(facets))); + } + /** * Creates a {@link SearchCollector} from a {@link Bson} in situations when there is no builder method that better satisfies your needs. * This method cannot be used to validate the syntax. diff --git a/driver-core/src/test/unit/com/mongodb/client/model/search/SearchCollectorTest.java b/driver-core/src/test/unit/com/mongodb/client/model/search/SearchCollectorTest.java index f8826554990..3cf9b86507a 100644 --- a/driver-core/src/test/unit/com/mongodb/client/model/search/SearchCollectorTest.java +++ b/driver-core/src/test/unit/com/mongodb/client/model/search/SearchCollectorTest.java @@ -62,6 +62,27 @@ void facet() { ); } + @Test + void facetWithoutOperator() { + assertAll( + () -> assertThrows(CodecConfigurationException.class, () -> + // facet names must be unique; `BsonCodec` wraps our `IllegalStateException` into `CodecConfigurationException` + SearchCollector.facet( + asList( + stringFacet("duplicateFacetName", fieldPath("stringFieldName")), + numberFacet("duplicateFacetName", fieldPath("numberFieldName"), asList(10, 20, 30)))) + // we have to render into `BsonDocument` in order to trigger the lazy check + .toBsonDocument() + ), + () -> assertEquals( + documentWithoutOperator() + .toBsonDocument(), + searchCollectorWithoutOperator() + .toBsonDocument() + ) + ); + } + private static SearchCollector docExamplePredefined() { return SearchCollector.facet( exists( @@ -89,4 +110,28 @@ private static Document docExampleCustom() { fieldPath("numberFieldName"), asList(10, 20, 30)))))); } + + private static SearchCollector searchCollectorWithoutOperator() { + return SearchCollector.facet( + asList( + stringFacet( + "stringFacetName", + fieldPath("stringFieldName")), + numberFacet( + "numberFacetName", + fieldPath("numberFieldName"), + asList(10, 20, 30)))); + } + + private static Document documentWithoutOperator() { + return new Document("facet", + new Document("facets", combineToBson(asList( + stringFacet( + "stringFacetName", + fieldPath("stringFieldName")), + numberFacet( + "numberFacetName", + fieldPath("numberFieldName"), + asList(10, 20, 30)))))); + } } From 4a8a080006955b5dff5cfceffe28c3a4be2cc286 Mon Sep 17 00:00:00 2001 From: Almas Abdrazak Date: Wed, 15 Jul 2026 21:50:12 -0700 Subject: [PATCH 2/2] address Valentin's comments --- .../client/model/search/DateSearchFacet.java | 2 +- .../model/search/FacetSearchCollector.java | 2 +- .../model/search/LowerBoundSearchCount.java | 2 +- .../client/model/search/NumberSearchFacet.java | 2 +- .../client/model/search/SearchCollector.java | 15 ++++++--------- .../client/model/search/SearchCount.java | 2 +- .../client/model/search/SearchFacet.java | 2 +- .../client/model/search/SearchOptions.java | 4 ++-- .../client/model/search/StringSearchFacet.java | 2 +- .../client/model/search/TotalSearchCount.java | 2 +- .../scala/model/search/SearchCollector.scala | 18 +++++++++++++++--- .../scala/model/search/SearchCount.scala | 2 +- .../scala/model/search/SearchFacet.scala | 2 +- .../mongodb/scala/model/search/package.scala | 16 ++++++++-------- 14 files changed, 41 insertions(+), 32 deletions(-) diff --git a/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java index 39d8bb2ddf0..958fc5bb2c9 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java +++ b/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java @@ -24,7 +24,7 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface DateSearchFacet extends SearchFacet { /** * Creates a new {@link DateSearchFacet} with the default bucket specified. diff --git a/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java b/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java index 01190216633..af2b3aa741b 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java +++ b/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java @@ -24,6 +24,6 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface FacetSearchCollector extends SearchCollector { } diff --git a/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java b/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java index 15576d4a5b6..c9d1711dac6 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java +++ b/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java @@ -24,7 +24,7 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface LowerBoundSearchCount extends SearchCount { /** * Creates a new {@link LowerBoundSearchCount} that instructs to count documents up to the {@code threshold} exactly, diff --git a/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java index 4587f688097..d2740a6b106 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java +++ b/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java @@ -24,7 +24,7 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface NumberSearchFacet extends SearchFacet { /** * Creates a new {@link NumberSearchFacet} with the default bucket specified. diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java b/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java index 571bff38044..26697059954 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java @@ -38,15 +38,15 @@ @Beta(Reason.CLIENT) public interface SearchCollector extends Bson { /** - * Returns a {@link SearchCollector} that groups results by values or ranges in the specified faceted fields and returns the count - * for each of those groups. + * Returns a {@link SearchCollector} that groups the results of a search based on the specified operator + * by values or ranges in the specified faceted fields, and returns the count for each of those groups. * * @param operator The search operator to use. * @param facets The non-empty facet definitions. * @return The requested {@link SearchCollector}. * @mongodb.atlas.manual atlas-search/facet/ facet collector */ - @Beta({Reason.CLIENT, Reason.SERVER}) + @Beta(Reason.CLIENT) static FacetSearchCollector facet(final SearchOperator operator, final Iterable facets) { notNull("operator", operator); notNull("facets", facets); @@ -55,17 +55,14 @@ static FacetSearchCollector facet(final SearchOperator operator, final Iterable< } /** - * Returns a {@link SearchCollector} that groups results by values or ranges in the specified faceted fields and returns the count - * for each of those groups, faceting over the entire collection. - *

- * Unlike {@link #facet(SearchOperator, Iterable)}, this method omits the search operator, so the facets are computed - * across all documents in the collection.

+ * Returns a {@link SearchCollector} that groups all the input documents + * by values or ranges in the specified faceted fields, and returns the count for each of those groups. * * @param facets The non-empty facet definitions. * @return The requested {@link SearchCollector}. * @mongodb.atlas.manual atlas-search/facet/ facet collector */ - @Beta({Reason.CLIENT, Reason.SERVER}) + @Beta(Reason.CLIENT) static FacetSearchCollector facet(final Iterable facets) { notNull("facets", facets); return new SearchConstructibleBsonElement("facet", new Document("facets", combineToBson(facets))); diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java b/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java index f9a5917582b..eb60fec9de0 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java @@ -34,7 +34,7 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface SearchCount extends Bson { /** * Returns a {@link SearchCount} that instructs to count documents exactly. diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java index 4aac0fef089..2188e06bfb4 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java @@ -44,7 +44,7 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface SearchFacet extends Bson { /** * Returns a {@link SearchFacet} that allows narrowing down search results based on the most frequent diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java b/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java index f5cd0261e8f..22740afde74 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java @@ -54,7 +54,7 @@ public interface SearchOptions extends Bson { * @param option The counting option. * @return A new {@link SearchOptions}. */ - @Beta({Reason.CLIENT, Reason.SERVER}) + @Beta(Reason.CLIENT) SearchOptions count(SearchCount option); /** @@ -64,7 +64,7 @@ public interface SearchOptions extends Bson { * @return A new {@link SearchOptions}. * @mongodb.atlas.manual atlas-search/return-stored-source/ Return stored source fields */ - @Beta({Reason.CLIENT, Reason.SERVER}) + @Beta(Reason.CLIENT) SearchOptions returnStoredSource(boolean returnStoredSource); /** diff --git a/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java index 209eaf9ff47..2d7e21ae63f 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java +++ b/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java @@ -24,7 +24,7 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface StringSearchFacet extends SearchFacet { /** * Creates a new {@link StringSearchFacet} that explicitly limits the number of facet categories. diff --git a/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java b/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java index 2bcbde468f3..173322cf8d1 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java +++ b/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java @@ -24,6 +24,6 @@ * @since 4.7 */ @Sealed -@Beta({Reason.CLIENT, Reason.SERVER}) +@Beta(Reason.CLIENT) public interface TotalSearchCount extends SearchCount { } diff --git a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCollector.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCollector.scala index a651e502b10..b8ad9ade0cb 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCollector.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCollector.scala @@ -34,18 +34,30 @@ import scala.collection.JavaConverters._ object SearchCollector { /** - * Returns a `SearchCollector` that groups results by values or ranges in the specified faceted fields and returns the count - * for each of those groups. + * Returns a `SearchCollector` that groups the results of a search based on the specified operator + * by values or ranges in the specified faceted fields, and returns the count for each of those groups. * * @param operator The search operator to use. * @param facets The non-empty facet definitions. * @return The requested `SearchCollector`. * @see [[https://www.mongodb.com/docs/atlas/atlas-search/facet/ facet collector]] */ - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) def facet(operator: SearchOperator, facets: Iterable[_ <: SearchFacet]): FacetSearchCollector = JSearchCollector.facet(operator, facets.asJava) + /** + * Returns a `SearchCollector` that groups all the input documents + * by values or ranges in the specified faceted fields, and returns the count for each of those groups. + * + * @param facets The non-empty facet definitions. + * @return The requested `SearchCollector`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/facet/ facet collector]] + */ + @Beta(Array(Reason.CLIENT)) + def facet(facets: Iterable[_ <: SearchFacet]): FacetSearchCollector = + JSearchCollector.facet(facets.asJava) + /** * Creates a `SearchCollector` from a `Bson` in situations when there is no builder method that better satisfies your needs. * This method cannot be used to validate the syntax. diff --git a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCount.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCount.scala index ecba0ecce0d..07220825f31 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCount.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchCount.scala @@ -28,7 +28,7 @@ import org.mongodb.scala.model.Projections * @see [[https://www.mongodb.com/docs/atlas/atlas-search/counting/ Counting]] * @since 4.7 */ -@Beta(Array(Reason.CLIENT, Reason.SERVER)) +@Beta(Array(Reason.CLIENT)) object SearchCount { /** diff --git a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchFacet.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchFacet.scala index 3bc27520ea3..75b40c3730c 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchFacet.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchFacet.scala @@ -28,7 +28,7 @@ import collection.JavaConverters._ * @see [[https://www.mongodb.com/docs/atlas/atlas-search/facet/#facet-definition Facet definition]] * @since 4.7 */ -@Beta(Array(Reason.CLIENT, Reason.SERVER)) +@Beta(Array(Reason.CLIENT)) object SearchFacet { /** diff --git a/driver-scala/src/main/scala/org/mongodb/scala/model/search/package.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/search/package.scala index 01ddaffb29f..a28dac4f5e9 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/model/search/package.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/model/search/package.scala @@ -269,7 +269,7 @@ package object search { * @see `SearchCollector.facet(SearchOperator, Iterable)` */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type FacetSearchCollector = com.mongodb.client.model.search.FacetSearchCollector /** @@ -360,21 +360,21 @@ package object search { * @see [[https://www.mongodb.com/docs/atlas/atlas-search/counting/ Counting]] */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type SearchCount = com.mongodb.client.model.search.SearchCount /** * @see `SearchCount.total()` */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type TotalSearchCount = com.mongodb.client.model.search.TotalSearchCount /** * @see `SearchCount.lowerBound()` */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type LowerBoundSearchCount = com.mongodb.client.model.search.LowerBoundSearchCount /** @@ -383,28 +383,28 @@ package object search { * @see [[https://www.mongodb.com/docs/atlas/atlas-search/facet/#facet-definition Facet definition]] */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type SearchFacet = com.mongodb.client.model.search.SearchFacet /** * @see `SearchFacet.stringFacet(String, FieldSearchPath)` */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type StringSearchFacet = com.mongodb.client.model.search.StringSearchFacet /** * @see `SearchFacet.numberFacet(String, FieldSearchPath, Iterable)` */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type NumberSearchFacet = com.mongodb.client.model.search.NumberSearchFacet /** * @see `SearchFacet.dateFacet(String, FieldSearchPath, Iterable)` */ @Sealed - @Beta(Array(Reason.CLIENT, Reason.SERVER)) + @Beta(Array(Reason.CLIENT)) type DateSearchFacet = com.mongodb.client.model.search.DateSearchFacet /**