Skip to content

Add support for managing Atlas search indexes. #1158

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 34 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bb610dd
Add support for managing Atlas search indexes.
vbabanin Jul 5, 2023
123c8cd
Fix formatting, spotbugs issues.
vbabanin Jul 5, 2023
1dd433d
Fix formatting.
vbabanin Jul 5, 2023
8ce344a
Add javadoc.
vbabanin Jul 5, 2023
e89553c
Fix tests.
vbabanin Jul 6, 2023
f977401
Change javadoc.
vbabanin Jul 6, 2023
ef13ea8
Change assignment ordering.
vbabanin Jul 7, 2023
795d351
Remove wildcard imports.
vbabanin Jul 11, 2023
891d386
Change documentation.
vbabanin Jul 11, 2023
ab7d67d
Change javadoc.
vbabanin Jul 11, 2023
95b6810
Remove maxAwaitTime option for ListSearchIndexes iterable/publisher i…
vbabanin Jul 11, 2023
78dcd04
Update driver-core/src/main/com/mongodb/internal/operation/SearchInde…
vbabanin Jul 15, 2023
d88b6a8
Update driver-core/src/main/com/mongodb/client/model/SearchIndexModel…
vbabanin Jul 15, 2023
36c6bc8
Update driver-core/src/main/com/mongodb/internal/operation/AbstractWr…
vbabanin Jul 15, 2023
d7208ca
Update driver-core/src/main/com/mongodb/internal/operation/AbstractWr…
vbabanin Jul 15, 2023
d04f161
Update driver-core/src/main/com/mongodb/internal/operation/AbstractWr…
vbabanin Jul 15, 2023
a7298ad
Update driver-core/src/main/com/mongodb/internal/operation/CreateSear…
vbabanin Jul 15, 2023
f8879d1
Update driver-core/src/main/com/mongodb/internal/operation/CreateSear…
vbabanin Jul 15, 2023
eb25379
Apply suggestions from code review
vbabanin Jul 15, 2023
f83e870
Add retryable functionality.
vbabanin Jul 15, 2023
1554663
Change javadoc.
vbabanin Jul 15, 2023
ba83e2e
Change Java doc of the createSearchIndexes methods.
vbabanin Jul 17, 2023
da5bed0
Change verb from "remove" to "drop" in javadoc.
vbabanin Jul 17, 2023
7bae99b
Fix spotBugs.
vbabanin Jul 17, 2023
4d8365c
Move test files.
vbabanin Jul 17, 2023
a24d795
Remove whitespaces.
vbabanin Jul 17, 2023
7163044
Revert retryable writes.
vbabanin Jul 18, 2023
dc04204
Remove mentioning of nullubility from ListSearchIterable and Publishe…
vbabanin Jul 24, 2023
684c455
Change javadoc.
vbabanin Jul 24, 2023
2235541
Update driver-sync/src/main/com/mongodb/client/MongoCollection.java
vbabanin Jul 24, 2023
e7b8192
Fix spotless warnings.
vbabanin Jul 25, 2023
f5f6230
Make class internal.
vbabanin Jul 25, 2023
f63ff7b
Add null-check.
vbabanin Jul 26, 2023
5cbee87
Add null-check.
vbabanin Jul 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions driver-core/src/main/com/mongodb/assertions/Assertions.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,34 @@ public static <T> T notNull(final String name, final T value) {
return value;
}

/**
* Throw IllegalArgumentException if the values is null or contains null.
*
* <p><b>Note:</b> If performance is a concern, consider deferring the integrity validation
* to the point of actual data iteration to avoid incurring additional reference chasing for collections of complex objects.
* However, if performance considerations are low and it is acceptable to iterate over the data twice,
* this method can still be used for validation purposes.
*
* @param name the parameter name.
* @param values the values that should not contain null elements.
* @param <T> the type of elements in the collection.
* @return the input collection if it passes the null element validation.
* @throws java.lang.IllegalArgumentException if the input collection is null or contains null elements.
*/
public static <T> Iterable<T> notNullElements(final String name, final Iterable<T> values) {
if (values == null) {
throw new IllegalArgumentException(name + " can not be null");
}

for (T value : values) {
if (value == null){
throw new IllegalArgumentException(name + " can not contain null");
}
}

return values;
}

/**
* Throw IllegalArgumentException if the value is null.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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;

import com.mongodb.lang.Nullable;
import org.bson.conversions.Bson;

import static com.mongodb.assertions.Assertions.notNull;

/**
* A model describing the creation of a single Atlas Search index.
*
* @since 4.11
* @mongodb.server.release 7.0
*/
public final class SearchIndexModel {
@Nullable
private final String name;
private final Bson definition;

/**
* Construct an instance with the given Atlas Search index mapping definition.
*
* <p>After calling this constructor, the {@code name} field will be {@code null}. In that case, when passing this
* {@code SearchIndexModel} to the {@code createSearchIndexes} method, the default search index name 'default'
* will be used to create the search index.</p>
*
* @param definition the search index mapping definition.
*/
public SearchIndexModel(final Bson definition) {
this.definition = notNull("definition", definition);
this.name = null;
}

/**
* Construct an instance with the given Atlas Search name and index definition.
*
* @param name the search index name.
* @param definition the search index mapping definition.
*/
public SearchIndexModel(final String name, final Bson definition) {
this.definition = notNull("definition", definition);
this.name = notNull("name", name);
}

/**
* Get the Atlas Search index mapping definition.
*
* @return the index definition.
*/
public Bson getDefinition() {
return definition;
}

/**
* Get the Atlas Search index name.
*
* @return the search index name.
*/
@Nullable
public String getName() {
return name;
}

@Override
public String toString() {
return "SearchIndexModel{"
+ "name=" + name
+ ", definition=" + definition
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.internal.operation;


import com.mongodb.MongoCommandException;
import com.mongodb.MongoNamespace;
import com.mongodb.WriteConcern;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.binding.AsyncWriteBinding;
import com.mongodb.internal.binding.WriteBinding;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;

import static com.mongodb.internal.operation.CommandOperationHelper.executeCommand;
import static com.mongodb.internal.operation.CommandOperationHelper.executeCommandAsync;
import static com.mongodb.internal.operation.CommandOperationHelper.writeConcernErrorTransformer;
import static com.mongodb.internal.operation.CommandOperationHelper.writeConcernErrorWriteTransformer;
import static com.mongodb.internal.operation.OperationHelper.withAsyncSourceAndConnection;
import static com.mongodb.internal.operation.OperationHelper.withConnection;

/**
* An abstract class for defining operations for managing Atlas Search indexes.
*
* <p>This class is not part of the public API and may be removed or changed at any time</p>
*/
abstract class AbstractWriteSearchIndexOperation implements AsyncWriteOperation<Void>, WriteOperation<Void> {
private final MongoNamespace namespace;
private final WriteConcern writeConcern;

AbstractWriteSearchIndexOperation(final MongoNamespace mongoNamespace,
final WriteConcern writeConcern) {
this.namespace = mongoNamespace;
this.writeConcern = writeConcern;
}

@Override
public Void execute(final WriteBinding binding) {
return withConnection(binding, connection -> {
try {
executeCommand(binding, namespace.getDatabaseName(), buildCommand(), connection, writeConcernErrorTransformer());
} catch (MongoCommandException mongoCommandException) {
swallowOrThrow(mongoCommandException);
}
return null;
});
}

@Override
public void executeAsync(final AsyncWriteBinding binding, final SingleResultCallback<Void> callback) {
withAsyncSourceAndConnection(binding::getWriteConnectionSource, false, callback,
(connectionSource, connection, cb) ->
executeCommandAsync(binding, namespace.getDatabaseName(), buildCommand(), connection,
writeConcernErrorWriteTransformer(), (result, commandExecutionError) -> {
try {
swallowOrThrow(commandExecutionError);
callback.onResult(result, null);
} catch (Throwable mongoCommandException) {
callback.onResult(null, mongoCommandException);
}
}
)
);
}

/**
* Handles the provided execution exception by either throwing it or ignoring it. This method is meant to be overridden
* by subclasses that need to handle exceptions differently based on their specific requirements.
*
* <p>
* <strong>Note:</strong> While the method declaration allows throwing a checked exception to enhance readability, the implementation
* of this method must not throw a checked exception.
* </p>
*
* @param <E> The type of the execution exception.
* @param mongoExecutionException The execution exception to handle. If not null, it may be thrown or ignored.
* @throws E The execution exception, if it is not null (implementation-specific).
*/
<E extends Throwable> void swallowOrThrow(@Nullable final E mongoExecutionException) throws E {
if (mongoExecutionException != null) {
throw mongoExecutionException;
}
}

abstract BsonDocument buildCommand();

MongoNamespace getNamespace() {
return namespace;
}

WriteConcern getWriteConcern() {
return writeConcern;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,12 @@ public AggregateOperation<T> collation(@Nullable final Collation collation) {
return this;
}

@Nullable
public BsonValue getComment() {
return wrapped.getComment();
}

public AggregateOperation<T> comment(final BsonValue comment) {
public AggregateOperation<T> comment(@Nullable final BsonValue comment) {
wrapped.comment(comment);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,12 @@ AggregateOperationImpl<T> collation(@Nullable final Collation collation) {
return this;
}

@Nullable
BsonValue getComment() {
return comment;
}

AggregateOperationImpl<T> comment(final BsonValue comment) {
AggregateOperationImpl<T> comment(@Nullable final BsonValue comment) {
this.comment = comment;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.mongodb.client.model.InsertOneOptions;
import com.mongodb.client.model.RenameCollectionOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.SearchIndexModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
import com.mongodb.client.model.changestream.FullDocument;
Expand All @@ -48,6 +49,7 @@
import com.mongodb.internal.client.model.AggregationLevel;
import com.mongodb.internal.client.model.FindOptions;
import com.mongodb.internal.client.model.changestream.ChangeStreamLevel;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
Expand Down Expand Up @@ -274,6 +276,29 @@ public AsyncWriteOperation<Void> createIndexes(final List<IndexModel> indexes, f
return operations.createIndexes(indexes, options);
}

public AsyncWriteOperation<Void> createSearchIndexes(final List<SearchIndexModel> indexes) {
return operations.createSearchIndexes(indexes);
}

public AsyncWriteOperation<Void> updateSearchIndex(final String indexName, final Bson definition) {
return operations.updateSearchIndex(indexName, definition);
}

public AsyncWriteOperation<Void> dropSearchIndex(final String indexName) {
return operations.dropSearchIndex(indexName);
}

public <TResult> AsyncExplainableReadOperation<AsyncBatchCursor<TResult>> listSearchIndexes(final Class<TResult> resultClass,
final long maxTimeMS,
@Nullable final String indexName,
@Nullable final Integer batchSize,
@Nullable final Collation collation,
@Nullable final BsonValue comment,
@Nullable final Boolean allowDiskUse) {
return operations.listSearchIndexes(resultClass, maxTimeMS, indexName, batchSize, collation,
comment, allowDiskUse);
}

public AsyncWriteOperation<Void> dropIndex(final String indexName, final DropIndexOptions options) {
return operations.dropIndex(indexName, options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public boolean getRetryReads() {
return wrapped.getRetryReads();
}

@Nullable
public BsonValue getComment() {
return wrapped.getComment();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.internal.operation;

import com.mongodb.MongoNamespace;
import com.mongodb.WriteConcern;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonString;

import java.util.List;
import java.util.stream.Collectors;

import static com.mongodb.assertions.Assertions.assertNotNull;
import static com.mongodb.internal.operation.WriteConcernHelper.appendWriteConcernToCommand;

/**
* An operation that creates one or more Atlas Search indexes.
*
* <p>This class is not part of the public API and may be removed or changed at any time</p>
*/
final class CreateSearchIndexesOperation extends AbstractWriteSearchIndexOperation {
private static final String COMMAND_NAME = "createSearchIndexes";
private final List<SearchIndexRequest> indexRequests;

CreateSearchIndexesOperation(final MongoNamespace namespace, final List<SearchIndexRequest> indexRequests,
final WriteConcern writeConcern) {
super(namespace, writeConcern);
this.indexRequests = assertNotNull(indexRequests);
}

private static BsonArray convert(final List<SearchIndexRequest> requests) {
return requests.stream()
.map(CreateSearchIndexesOperation::convert)
.collect(Collectors.toCollection(BsonArray::new));
}

private static BsonDocument convert(final SearchIndexRequest request) {
BsonDocument bsonIndexRequest = new BsonDocument();
String searchIndexName = request.getIndexName();
if (searchIndexName != null) {
bsonIndexRequest.append("name", new BsonString(searchIndexName));
}
bsonIndexRequest.append("definition", request.getDefinition());
return bsonIndexRequest;
}

@Override
BsonDocument buildCommand() {
BsonDocument command = new BsonDocument(COMMAND_NAME, new BsonString(getNamespace().getCollectionName()))
.append("indexes", convert(indexRequests));
appendWriteConcernToCommand(getWriteConcern(), command);
return command;
}
}
Loading