diff --git a/driver-core/src/main/com/mongodb/client/model/search/RegexSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/RegexSearchOperator.java new file mode 100644 index 0000000000..c028607971 --- /dev/null +++ b/driver-core/src/main/com/mongodb/client/model/search/RegexSearchOperator.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * @see SearchOperator#regex(SearchPath, String) + * @see SearchOperator#regex(Iterable, Iterable) + * @since 5.3 + */ + +@Sealed +@Beta(Reason.CLIENT) +public interface RegexSearchOperator extends SearchOperator { + @Override + RegexSearchOperator 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 8f0b1e510c..ee168689df 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,7 +31,7 @@ final class SearchConstructibleBsonElement extends AbstractConstructibleBsonElement implements MustCompoundSearchOperator, MustNotCompoundSearchOperator, ShouldCompoundSearchOperator, FilterCompoundSearchOperator, ExistsSearchOperator, TextSearchOperator, AutocompleteSearchOperator, - NumberNearSearchOperator, DateNearSearchOperator, GeoNearSearchOperator, + NumberNearSearchOperator, DateNearSearchOperator, GeoNearSearchOperator, RegexSearchOperator, 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 00961bc3c1..90cb590d25 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 @@ -322,6 +322,36 @@ static PhraseSearchOperator phrase(final Iterable paths, f .append("query", queryIterator.hasNext() ? queries : firstQuery)); } + /** + * Returns a {@link SearchOperator} that performs a search using a regular expression. + * + * @param path The field to be searched. + * @param query The string to search for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/regex/ regex operator + */ + static RegexSearchOperator regex(final SearchPath path, final String query) { + return regex(singleton(notNull("path", path)), singleton(notNull("query", query))); + } + + /** + * Returns a {@link SearchOperator} that performs a search using a regular expression. + * + * @param paths The non-empty fields to be searched. + * @param queries The non-empty strings to search for. + * @return The requested {@link SearchOperator}. + * @mongodb.atlas.manual atlas-search/regex/ regex operator + */ + static RegexSearchOperator regex(final Iterable paths, final Iterable queries) { + Iterator pathIterator = notNull("paths", paths).iterator(); + isTrueArgument("paths must not be empty", pathIterator.hasNext()); + Iterator queryIterator = notNull("queries", queries).iterator(); + isTrueArgument("queries must not be empty", queryIterator.hasNext()); + String firstQuery = queryIterator.next(); + return new SearchConstructibleBsonElement("regex", new Document("path", combineToBsonValue(pathIterator, false)) + .append("query", queryIterator.hasNext() ? queries : firstQuery)); + } + /** * Creates a {@link SearchOperator} 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/functional/com/mongodb/client/model/search/AggregatesSearchIntegrationTest.java b/driver-core/src/test/functional/com/mongodb/client/model/search/AggregatesSearchIntegrationTest.java index 0dd8ab387d..790f596784 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 @@ -82,6 +82,7 @@ import static com.mongodb.client.model.search.SearchOperator.exists; import static com.mongodb.client.model.search.SearchOperator.near; import static com.mongodb.client.model.search.SearchOperator.numberRange; +import static com.mongodb.client.model.search.SearchOperator.regex; import static com.mongodb.client.model.search.SearchOperator.phrase; import static com.mongodb.client.model.search.SearchOperator.text; import static com.mongodb.client.model.search.SearchOptions.searchOptions; @@ -610,7 +611,8 @@ private static Stream searchAndSearchMetaArgs() { .lte(Instant.ofEpochMilli(1)), near(0, 1.5, fieldPath("fieldName7"), fieldPath("fieldName8")), near(Instant.ofEpochMilli(1), Duration.ofMillis(3), fieldPath("fieldName9")), - phrase(fieldPath("fieldName10"), "term6") + phrase(fieldPath("fieldName10"), "term6"), + regex(fieldPath("fieldName11"), "term7") )) .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 1b34e8cf15..02dc8d3fa0 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 @@ -649,6 +649,58 @@ void phrase() { ); } + @Test + void regex() { + assertAll( + () -> assertThrows(IllegalArgumentException.class, () -> + // queries must not be empty + SearchOperator.regex(singleton(fieldPath("fieldName")), emptyList()) + ), + () -> assertThrows(IllegalArgumentException.class, () -> + // paths must not be empty + SearchOperator.regex(emptyList(), singleton("term")) + ), + () -> assertEquals( + new BsonDocument("regex", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("query", new BsonString("term")) + ), + SearchOperator.regex( + fieldPath("fieldName"), + "term") + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("regex", + new BsonDocument("path", fieldPath("fieldName").toBsonValue()) + .append("query", new BsonString("term")) + ), + SearchOperator.regex( + singleton(fieldPath("fieldName")), + singleton("term")) + .toBsonDocument() + ), + () -> assertEquals( + new BsonDocument("regex", + new BsonDocument("path", new BsonArray(asList( + fieldPath("fieldName").toBsonValue(), + wildcardPath("wildc*rd").toBsonValue()))) + .append("query", new BsonArray(asList( + new BsonString("term1"), + new BsonString("term2")))) + ), + SearchOperator.regex( + asList( + fieldPath("fieldName"), + wildcardPath("wildc*rd")), + asList( + "term1", + "term2")) + .toBsonDocument() + ) + ); + } + private static SearchOperator docExamplePredefined() { return SearchOperator.exists( fieldPath("fieldName")); 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 a72e5b3dbc..b76f707d3a 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 @@ -249,6 +249,27 @@ object SearchOperator { def phrase(paths: Iterable[_ <: SearchPath], queries: Iterable[String]): PhraseSearchOperator = JSearchOperator.phrase(paths.asJava, queries.asJava) + /** + * Returns a `SearchOperator` that performs a search using a regular expression. + * + * @param path The field to be searched. + * @param query The string to search for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/regex/ regex operator]] + */ + def regex(path: SearchPath, query: String): RegexSearchOperator = JSearchOperator.regex(path, query) + + /** + * Returns a `SearchOperator` that performs a search using a regular expression. + * + * @param paths The non-empty fields to be searched. + * @param queries The non-empty strings to search for. + * @return The requested `SearchOperator`. + * @see [[https://www.mongodb.com/docs/atlas/atlas-search/regex/ regex operator]] + */ + def regex(paths: Iterable[_ <: SearchPath], queries: Iterable[String]): RegexSearchOperator = + JSearchOperator.regex(paths.asJava, queries.asJava) + /** * Creates a `SearchOperator` 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/package.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/search/package.scala index e9b8640147..8d6c1eff8e 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 @@ -135,6 +135,14 @@ package object search { @Beta(Array(Reason.CLIENT)) type AutocompleteSearchOperator = com.mongodb.client.model.search.AutocompleteSearchOperator + /** + * @see `SearchOperator.regex(String, SearchPath)` + * @see `SearchOperator.regex(Iterable, Iterable)` + */ + @Sealed + @Beta(Array(Reason.CLIENT)) + type RegexSearchOperator = com.mongodb.client.model.search.RegexSearchOperator + /** * A base for a [[NumberRangeSearchOperatorBase]] which allows creating instances of this operator. * This interface is a technicality and does not represent a meaningful element of the full-text search query syntax.