Skip to content

Add moreLikeThis operator to Atlas Search #1609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 17, 2025
Original file line number Diff line number Diff line change
@@ -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;
import org.bson.BsonDocument;

/**
* @see SearchOperator#moreLikeThis(BsonDocument)
* @see SearchOperator#moreLikeThis(Iterable)
* @since 4.7
*/
@Sealed
@Beta(Reason.CLIENT)
public interface MoreLikeThisSearchOperator extends SearchOperator {
@Override
TextSearchOperator score(SearchScore modifier);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ final class SearchConstructibleBsonElement extends AbstractConstructibleBsonElem
MustCompoundSearchOperator, MustNotCompoundSearchOperator, ShouldCompoundSearchOperator, FilterCompoundSearchOperator,
ExistsSearchOperator, TextSearchOperator, AutocompleteSearchOperator,
NumberNearSearchOperator, DateNearSearchOperator, GeoNearSearchOperator, RegexSearchOperator, QueryStringSearchOperator, WildcardSearchOperator,
MoreLikeThisSearchOperator,
ValueBoostSearchScore, PathBoostSearchScore, ConstantSearchScore, FunctionSearchScore,
GaussSearchScoreExpression, PathSearchScoreExpression,
FacetSearchCollector,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.geojson.Point;
import org.bson.BsonDocument;
import org.bson.BsonType;
import org.bson.Document;
import org.bson.conversions.Bson;
Expand Down Expand Up @@ -292,6 +293,31 @@ static GeoNearSearchOperator near(final Point origin, final Number pivot, final
.append("pivot", notNull("pivot", pivot)));
}

/**
* Returns a {@link SearchOperator} that returns documents similar to input documents.
*
* @param like The BSON document that is used to extract representative terms to query for.
* @return The requested {@link SearchOperator}.
* @mongodb.atlas.manual atlas-search/morelikethis/ moreLikeThis operator
*/
static MoreLikeThisSearchOperator moreLikeThis(final BsonDocument like) {
return moreLikeThis(singleton(notNull("like", like)));
}

/**
* Returns a {@link SearchOperator} that returns documents similar to input document.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I put the suggestion on the wrong method, and the plural might be backwards on these (this should have the s, the other, not).

*
* @param likes The BSON documents that are used to extract representative terms to query for.
* @return The requested {@link SearchOperator}.
* @mongodb.atlas.manual atlas-search/morelikethis/ moreLikeThis operator
*/
static MoreLikeThisSearchOperator moreLikeThis(final Iterable<BsonDocument> likes) {
Iterator<? extends BsonDocument> likesIterator = notNull("likes", likes).iterator();
isTrueArgument("likes must not be empty", likesIterator.hasNext());
BsonDocument firstLike = likesIterator.next();
return new SearchConstructibleBsonElement("moreLikeThis", new Document("like", likesIterator.hasNext() ? likes : firstLike));
}

/**
* Returns a {@link SearchOperator} that supports querying a combination of indexed fields and values.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.mongodb.client.model.geojson.Position;
import com.mongodb.client.test.CollectionHelper;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.conversions.Bson;
import org.bson.json.JsonWriterSettings;
Expand Down Expand Up @@ -80,6 +81,7 @@
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.exists;
import static com.mongodb.client.model.search.SearchOperator.moreLikeThis;
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.queryString;
Expand Down Expand Up @@ -616,7 +618,9 @@ private static Stream<Arguments> searchAndSearchMetaArgs() {
phrase(fieldPath("fieldName10"), "term6"),
regex(fieldPath("fieldName11"), "term7"),
queryString(fieldPath("fieldName12"), "term8"),
wildcard(asList("term10", "term11"), asList(wildcardPath("wildc*rd"), fieldPath("fieldName14")))
wildcard(asList("term10", "term11"), asList(wildcardPath("wildc*rd"), fieldPath("fieldName14"))),
moreLikeThis(new BsonDocument("like", new BsonDocument("fieldName10",
new BsonString("term6"))))
))
.minimumShouldMatch(1)
.mustNot(singleton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,46 @@ void near() {
);
}

@Test
void moreLikeThis() {
assertAll(
() -> assertThrows(IllegalArgumentException.class, () ->
// likes must not be empty
SearchOperator.moreLikeThis(emptyList())
),
() -> assertEquals(
new BsonDocument("moreLikeThis",
new BsonDocument("like", new BsonDocument("fieldName", new BsonString("fieldValue")))
),
SearchOperator.moreLikeThis(new BsonDocument("fieldName", new BsonString("fieldValue")))
.toBsonDocument()
),
() -> assertEquals(
new BsonDocument("moreLikeThis",
new BsonDocument("like", new BsonDocument("fieldName", new BsonString("fieldValue"))
.append("fieldName2", new BsonString("fieldValue2")))
),
SearchOperator.moreLikeThis(new BsonDocument("fieldName", new BsonString("fieldValue"))
.append("fieldName2", new BsonString("fieldValue2")))
.toBsonDocument()
),
() -> assertEquals(
new BsonDocument("moreLikeThis",
new BsonDocument("like", new BsonArray(asList(
new BsonDocument("fieldName", new BsonString("fieldValue"))
.append("fieldName2", new BsonString("fieldValue2")),
new BsonDocument("fieldName3", new BsonString("fieldValue3"))
)))
),
SearchOperator.moreLikeThis(asList(
new BsonDocument("fieldName", new BsonString("fieldValue"))
.append("fieldName2", new BsonString("fieldValue2")),
new BsonDocument("fieldName3", new BsonString("fieldValue3"))))
.toBsonDocument()
)
);
}

@Test
void wildcard() {
assertAll(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package org.mongodb.scala.model.search

import com.mongodb.annotations.{ Beta, Reason }
import com.mongodb.client.model.search.{ SearchOperator => JSearchOperator }
import org.mongodb.scala.bson.BsonDocument
import org.mongodb.scala.bson.conversions.Bson
import org.mongodb.scala.model.geojson.Point

Expand Down Expand Up @@ -228,6 +229,25 @@ object SearchOperator {
def near(origin: Point, pivot: Number, paths: Iterable[_ <: FieldSearchPath]): GeoNearSearchOperator =
JSearchOperator.near(origin, pivot, paths.asJava)

/**
* Returns a `SearchOperator` that returns documents similar to input document.
*
* @param like The BSON document that is used to extract representative terms to query for.
* @return The requested `SearchOperator`.
* @see [[https://www.mongodb.com/docs/atlas/atlas-search/morelikethis/ moreLikeThis operator]]
*/
def moreLikeThis(like: BsonDocument): MoreLikeThisSearchOperator = JSearchOperator.moreLikeThis(like)

/**
* Returns a `SearchOperator` that returns documents similar to input documents.
*
* @param likes The BSON documents that are used to extract representative terms to query for.
* @return The requested `SearchOperator`.
* @see [[https://www.mongodb.com/docs/atlas/atlas-search/morelikethis/ moreLikeThis operator]]
*/
def moreLikeThis(likes: Iterable[BsonDocument]): MoreLikeThisSearchOperator =
JSearchOperator.moreLikeThis(likes.asJava)

/**
* Returns a `SearchOperator` that enables queries which use special characters in the search string that can match any character.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ package object search {
@Beta(Array(Reason.CLIENT))
type GeoNearSearchOperator = com.mongodb.client.model.search.GeoNearSearchOperator

/**
* @see `SearchOperator.moreLikeThis`
*/
@Sealed
@Beta(Array(Reason.CLIENT))
type MoreLikeThisSearchOperator = com.mongodb.client.model.search.MoreLikeThisSearchOperator

/**
* @see `SearchOperator.wildcard(String, SearchPath)`
* @see `SearchOperator.wildcard(Iterable, Iterable)`
Expand Down