diff --git a/driver-core/src/main/com/mongodb/client/model/search/EqualsSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/EqualsSearchOperator.java new file mode 100644 index 0000000000..b3aa4c278e --- /dev/null +++ b/driver-core/src/main/com/mongodb/client/model/search/EqualsSearchOperator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mongodb.client.model.search; + +import com.mongodb.annotations.Beta; +import com.mongodb.annotations.Reason; +import com.mongodb.annotations.Sealed; + +import java.util.UUID; + +import java.time.Instant; + +import org.bson.types.ObjectId; + +/** + * @see SearchOperator#equals(FieldSearchPath, boolean) + * @see SearchOperator#equals(FieldSearchPath, ObjectId) + * @see SearchOperator#equals(FieldSearchPath, Number) + * @see SearchOperator#equals(FieldSearchPath, Instant) + * @see SearchOperator#equals(FieldSearchPath, String) + * @see SearchOperator#equals(FieldSearchPath, UUID) + * @see SearchOperator#equalsNull(FieldSearchPath) + * @since 5.3 + */ +@Sealed +@Beta(Reason.CLIENT) +public interface EqualsSearchOperator extends SearchOperator { + @Override + EqualsSearchOperator score(SearchScore modifier); +} diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchConstructibleBsonElement.java b/driver-core/src/main/com/mongodb/client/model/search/SearchConstructibleBsonElement.java index 6db68a7d6c..6c78d7e16d 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchConstructibleBsonElement.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchConstructibleBsonElement.java @@ -31,8 +31,9 @@ final class SearchConstructibleBsonElement extends AbstractConstructibleBsonElement implements MustCompoundSearchOperator, MustNotCompoundSearchOperator, ShouldCompoundSearchOperator, FilterCompoundSearchOperator, ExistsSearchOperator, TextSearchOperator, AutocompleteSearchOperator, - NumberNearSearchOperator, DateNearSearchOperator, GeoNearSearchOperator, RegexSearchOperator, QueryStringSearchOperator, WildcardSearchOperator, - MoreLikeThisSearchOperator, + NumberNearSearchOperator, DateNearSearchOperator, GeoNearSearchOperator, + EqualsSearchOperator, MoreLikeThisSearchOperator, + RegexSearchOperator, QueryStringSearchOperator, WildcardSearchOperator, ValueBoostSearchScore, PathBoostSearchScore, ConstantSearchScore, FunctionSearchScore, GaussSearchScoreExpression, PathSearchScoreExpression, FacetSearchCollector, diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java index dada0331c4..e457454db0 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java +++ b/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java @@ -20,6 +20,11 @@ import com.mongodb.annotations.Sealed; import com.mongodb.client.model.Aggregates; import com.mongodb.client.model.geojson.Point; + +import java.util.UUID; + +import org.bson.BsonBinary; +import org.bson.BsonNull; import org.bson.BsonDocument; import org.bson.BsonType; import org.bson.Document; @@ -29,6 +34,8 @@ import java.time.Instant; import java.util.Iterator; +import org.bson.types.ObjectId; + import static com.mongodb.assertions.Assertions.isTrueArgument; import static com.mongodb.internal.Iterables.concat; import static com.mongodb.internal.client.model.Util.combineToBsonValue; @@ -293,6 +300,96 @@ static GeoNearSearchOperator near(final Point origin, final Number pivot, final .append("pivot", notNull("pivot", pivot))); } + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The boolean value to query for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equals(final FieldSearchPath path, final boolean value) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", value)); + } + + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The object id value to query for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equals(final FieldSearchPath path, final ObjectId value) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", notNull("value", value))); + } + + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The number value to query for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equals(final FieldSearchPath path, final Number value) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", notNull("value", value))); + } + + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The instant date value to query for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equals(final FieldSearchPath path, final Instant value) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", notNull("value", value))); + } + + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The string value to query for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equals(final FieldSearchPath path, final String value) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", notNull("value", value))); + } + + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The uuid value to query for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equals(final FieldSearchPath path, final UUID value) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", notNull("value", new BsonBinary(value)))); + } + + /** + * Returns a {@link SearchOperator} that searches for documents where a field matches null. + * + * @param path The indexed field to be searched. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/equals/ equals operator + */ + static EqualsSearchOperator equalsNull(final FieldSearchPath path) { + return new SearchConstructibleBsonElement("equals", new Document("path", notNull("path", path).toValue()) + .append("value", BsonNull.VALUE)); + } + /** * Returns a {@link SearchOperator} that returns documents similar to input document. * diff --git a/driver-core/src/test/functional/com/mongodb/client/model/search/AggregatesSearchIntegrationTest.java b/driver-core/src/test/functional/com/mongodb/client/model/search/AggregatesSearchIntegrationTest.java index a8e0ad314b..d90f4a0575 100644 --- a/driver-core/src/test/functional/com/mongodb/client/model/search/AggregatesSearchIntegrationTest.java +++ b/driver-core/src/test/functional/com/mongodb/client/model/search/AggregatesSearchIntegrationTest.java @@ -80,6 +80,7 @@ import static com.mongodb.client.model.search.SearchOperator.autocomplete; import static com.mongodb.client.model.search.SearchOperator.compound; import static com.mongodb.client.model.search.SearchOperator.dateRange; +import static com.mongodb.client.model.search.SearchOperator.equalsNull; import static com.mongodb.client.model.search.SearchOperator.exists; import static com.mongodb.client.model.search.SearchOperator.moreLikeThis; import static com.mongodb.client.model.search.SearchOperator.near; @@ -621,7 +622,9 @@ private static Stream searchAndSearchMetaArgs() { moreLikeThis(new BsonDocument("like", new BsonDocument("fieldName10", new BsonString("term6")))), wildcard(asList("term10", "term11"), asList(wildcardPath("wildc*rd"), fieldPath("title").multi( - "keyword"))) + "keyword"))), + SearchOperator.equals(fieldPath("fieldName11"), "term7"), + equalsNull(fieldPath("fieldName12")) )) .minimumShouldMatch(1) .mustNot(singleton( diff --git a/driver-core/src/test/unit/com/mongodb/client/model/search/SearchOperatorTest.java b/driver-core/src/test/unit/com/mongodb/client/model/search/SearchOperatorTest.java index ef481ea03c..06a825c612 100644 --- a/driver-core/src/test/unit/com/mongodb/client/model/search/SearchOperatorTest.java +++ b/driver-core/src/test/unit/com/mongodb/client/model/search/SearchOperatorTest.java @@ -18,14 +18,22 @@ import com.mongodb.MongoClientSettings; import com.mongodb.client.model.geojson.Point; import com.mongodb.client.model.geojson.Position; + +import java.util.UUID; + import org.bson.BsonArray; +import org.bson.BsonBinary; +import org.bson.BsonBoolean; import org.bson.BsonDateTime; import org.bson.BsonDocument; import org.bson.BsonDouble; import org.bson.BsonInt32; import org.bson.BsonInt64; +import org.bson.BsonNull; +import org.bson.BsonObjectId; import org.bson.BsonString; import org.bson.Document; +import org.bson.types.ObjectId; import org.junit.jupiter.api.Test; import java.time.Duration; @@ -581,6 +589,106 @@ void near() { ); } + @Test + void equals() { + ObjectId objectId = new ObjectId(); + UUID uuid = UUID.randomUUID(); + assertAll( + () -> assertThrows(IllegalArgumentException.class, () -> + // path must not be null + SearchOperator.equals(null, "term") + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonBoolean(true)) + ), + SearchOperator.equals( + fieldPath("fieldName"), + true) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonObjectId(objectId)) + ), + SearchOperator.equals( + fieldPath("fieldName"), + objectId) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonInt32(1)) + ), + SearchOperator.equals( + fieldPath("fieldName"), + 1) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonInt64(Long.MAX_VALUE)) + ), + SearchOperator.equals( + fieldPath("fieldName"), + Long.MAX_VALUE) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonDouble(Double.MAX_VALUE)) + ), + SearchOperator.equals( + fieldPath("fieldName"), + Double.MAX_VALUE) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonDateTime(Instant.EPOCH.toEpochMilli())) + ), + SearchOperator.equals( + fieldPath("fieldName"), + Instant.EPOCH) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonString("term")) + ), + SearchOperator.equals( + fieldPath("fieldName"), + "term") + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", new BsonBinary(uuid)) + ), + SearchOperator.equals( + fieldPath("fieldName"), + uuid) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("equals", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("value", BsonNull.VALUE) + ), + SearchOperator.equalsNull(fieldPath("fieldName")) + .toBsonDocument() + ) + ); + } + @Test void moreLikeThis() { assertAll( diff --git a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchOperator.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchOperator.scala index fc8f065b55..500724d4d3 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchOperator.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/model/search/SearchOperator.scala @@ -21,7 +21,10 @@ import org.mongodb.scala.bson.BsonDocument import org.mongodb.scala.bson.conversions.Bson import org.mongodb.scala.model.geojson.Point +import org.bson.types.ObjectId; + import java.time.{ Duration, Instant } +import java.util.UUID import collection.JavaConverters._ /** @@ -229,6 +232,83 @@ object SearchOperator { def near(origin: Point, pivot: Number, paths: Iterable[_ <: FieldSearchPath]): GeoNearSearchOperator = JSearchOperator.near(origin, pivot, paths.asJava) + /** + * Returns a `SearchOperator` that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The boolean value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equals(path: FieldSearchPath, value: Boolean): EqualsSearchOperator = + JSearchOperator.equals(path, value) + + /** + * Returns a `SearchOperator` that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The object id value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equals(path: FieldSearchPath, value: ObjectId): EqualsSearchOperator = + JSearchOperator.equals(path, value) + + /** + * Returns a `SearchOperator` that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The number value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equals(path: FieldSearchPath, value: Number): EqualsSearchOperator = + JSearchOperator.equals(path, value) + + /** + * Returns a `SearchOperator` that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The instant date value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equals(path: FieldSearchPath, value: Instant): EqualsSearchOperator = + JSearchOperator.equals(path, value) + + /** + * Returns a `SearchOperator` that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The string value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equals(path: FieldSearchPath, value: String): EqualsSearchOperator = + JSearchOperator.equals(path, value) + + /** + * Returns a `SearchOperator` that searches for documents where a field matches the specified value. + * + * @param path The indexed field to be searched. + * @param value The uuid value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equals(path: FieldSearchPath, value: UUID): EqualsSearchOperator = + JSearchOperator.equals(path, value) + + /** + * Returns a `SearchOperator` that searches for documents where a field matches null. + * + * @param path The indexed field to be searched. + * @param value The uuid value to query for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/equals/ equals operator]] + */ + def equalsNull(path: FieldSearchPath): EqualsSearchOperator = + JSearchOperator.equalsNull(path) + /** * Returns a `SearchOperator` that returns documents similar to input document. * 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 135190347b..0a0c7662c0 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 @@ -198,6 +198,13 @@ package object search { @Beta(Array(Reason.CLIENT)) type GeoNearSearchOperator = com.mongodb.client.model.search.GeoNearSearchOperator + /** + * @see `SearchOperator.equals` + */ + @Sealed + @Beta(Array(Reason.CLIENT)) + type EqualsSearchOperator = com.mongodb.client.model.search.EqualsSearchOperator + /** * @see `SearchOperator.moreLikeThis` */