All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mongodb.internal.operation.FindOperation Maven / Gradle / Ivy

Go to download

The Java operations layer for the MongoDB Java Driver. Third parties can wrap this layer to provide custom higher-level APIs

There is a newer version: 5.3.0-beta0
Show newest version
/*
 * 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.CursorType;
import com.mongodb.ExplainVerbosity;
import com.mongodb.MongoCommandException;
import com.mongodb.MongoNamespace;
import com.mongodb.MongoQueryException;
import com.mongodb.client.model.Collation;
import com.mongodb.internal.async.AsyncBatchCursor;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.async.function.AsyncCallbackSupplier;
import com.mongodb.internal.async.function.RetryState;
import com.mongodb.internal.binding.AsyncReadBinding;
import com.mongodb.internal.binding.ReadBinding;
import com.mongodb.internal.connection.NoOpSessionContext;
import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
import org.bson.BsonInt64;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.codecs.Decoder;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
import static com.mongodb.internal.operation.AsyncOperationHelper.CommandReadTransformerAsync;
import static com.mongodb.internal.operation.AsyncOperationHelper.createReadCommandAndExecuteAsync;
import static com.mongodb.internal.operation.AsyncOperationHelper.decorateReadWithRetriesAsync;
import static com.mongodb.internal.operation.AsyncOperationHelper.withAsyncSourceAndConnection;
import static com.mongodb.internal.operation.CommandOperationHelper.CommandCreator;
import static com.mongodb.internal.operation.CommandOperationHelper.initialRetryState;
import static com.mongodb.internal.operation.DocumentHelper.putIfNotNull;
import static com.mongodb.internal.operation.DocumentHelper.putIfNotNullOrEmpty;
import static com.mongodb.internal.operation.ExplainHelper.asExplainCommand;
import static com.mongodb.internal.operation.OperationHelper.LOGGER;
import static com.mongodb.internal.operation.OperationHelper.canRetryRead;
import static com.mongodb.internal.operation.OperationReadConcernHelper.appendReadConcernToCommand;
import static com.mongodb.internal.operation.ServerVersionHelper.MIN_WIRE_VERSION;
import static com.mongodb.internal.operation.SyncOperationHelper.CommandReadTransformer;
import static com.mongodb.internal.operation.SyncOperationHelper.createReadCommandAndExecute;
import static com.mongodb.internal.operation.SyncOperationHelper.decorateReadWithRetries;
import static com.mongodb.internal.operation.SyncOperationHelper.withSourceAndConnection;

/**
 * An operation that queries a collection using the provided criteria.
 *
 * 

This class is not part of the public API and may be removed or changed at any time

*/ public class FindOperation implements AsyncExplainableReadOperation>, ExplainableReadOperation> { private static final String FIRST_BATCH = "firstBatch"; private final MongoNamespace namespace; private final Decoder decoder; private boolean retryReads; private BsonDocument filter; private int batchSize; private int limit; private BsonDocument projection; private long maxTimeMS; private long maxAwaitTimeMS; private int skip; private BsonDocument sort; private CursorType cursorType = CursorType.NonTailable; private boolean noCursorTimeout; private boolean partial; private Collation collation; private BsonValue comment; private BsonValue hint; private BsonDocument variables; private BsonDocument max; private BsonDocument min; private boolean returnKey; private boolean showRecordId; private Boolean allowDiskUse; public FindOperation(final MongoNamespace namespace, final Decoder decoder) { this.namespace = notNull("namespace", namespace); this.decoder = notNull("decoder", decoder); } public MongoNamespace getNamespace() { return namespace; } public Decoder getDecoder() { return decoder; } public BsonDocument getFilter() { return filter; } public FindOperation filter(@Nullable final BsonDocument filter) { this.filter = filter; return this; } public int getBatchSize() { return batchSize; } public FindOperation batchSize(final int batchSize) { this.batchSize = batchSize; return this; } public int getLimit() { return limit; } public FindOperation limit(final int limit) { this.limit = limit; return this; } public BsonDocument getProjection() { return projection; } public FindOperation projection(@Nullable final BsonDocument projection) { this.projection = projection; return this; } public long getMaxTime(final TimeUnit timeUnit) { notNull("timeUnit", timeUnit); return timeUnit.convert(maxTimeMS, TimeUnit.MILLISECONDS); } public FindOperation maxTime(final long maxTime, final TimeUnit timeUnit) { notNull("timeUnit", timeUnit); isTrueArgument("maxTime >= 0", maxTime >= 0); this.maxTimeMS = TimeUnit.MILLISECONDS.convert(maxTime, timeUnit); return this; } public long getMaxAwaitTime(final TimeUnit timeUnit) { notNull("timeUnit", timeUnit); return timeUnit.convert(maxAwaitTimeMS, TimeUnit.MILLISECONDS); } public FindOperation maxAwaitTime(final long maxAwaitTime, final TimeUnit timeUnit) { notNull("timeUnit", timeUnit); isTrueArgument("maxAwaitTime >= 0", maxAwaitTime >= 0); this.maxAwaitTimeMS = TimeUnit.MILLISECONDS.convert(maxAwaitTime, timeUnit); return this; } public int getSkip() { return skip; } public FindOperation skip(final int skip) { this.skip = skip; return this; } public BsonDocument getSort() { return sort; } public FindOperation sort(@Nullable final BsonDocument sort) { this.sort = sort; return this; } public CursorType getCursorType() { return cursorType; } public FindOperation cursorType(final CursorType cursorType) { this.cursorType = notNull("cursorType", cursorType); return this; } public boolean isNoCursorTimeout() { return noCursorTimeout; } public FindOperation noCursorTimeout(final boolean noCursorTimeout) { this.noCursorTimeout = noCursorTimeout; return this; } public boolean isPartial() { return partial; } public FindOperation partial(final boolean partial) { this.partial = partial; return this; } public Collation getCollation() { return collation; } public FindOperation collation(@Nullable final Collation collation) { this.collation = collation; return this; } public BsonValue getComment() { return comment; } public FindOperation comment(@Nullable final BsonValue comment) { this.comment = comment; return this; } public BsonValue getHint() { return hint; } public FindOperation hint(@Nullable final BsonValue hint) { this.hint = hint; return this; } public BsonDocument getLet() { return variables; } public FindOperation let(@Nullable final BsonDocument variables) { this.variables = variables; return this; } public BsonDocument getMax() { return max; } public FindOperation max(@Nullable final BsonDocument max) { this.max = max; return this; } public BsonDocument getMin() { return min; } public FindOperation min(@Nullable final BsonDocument min) { this.min = min; return this; } public boolean isReturnKey() { return returnKey; } public FindOperation returnKey(final boolean returnKey) { this.returnKey = returnKey; return this; } public boolean isShowRecordId() { return showRecordId; } public FindOperation showRecordId(final boolean showRecordId) { this.showRecordId = showRecordId; return this; } public FindOperation retryReads(final boolean retryReads) { this.retryReads = retryReads; return this; } public boolean getRetryReads() { return retryReads; } public Boolean isAllowDiskUse() { return allowDiskUse; } public FindOperation allowDiskUse(@Nullable final Boolean allowDiskUse) { this.allowDiskUse = allowDiskUse; return this; } @Override public BatchCursor execute(final ReadBinding binding) { RetryState retryState = initialRetryState(retryReads); Supplier> read = decorateReadWithRetries(retryState, binding.getOperationContext(), () -> withSourceAndConnection(binding::getReadConnectionSource, false, (source, connection) -> { retryState.breakAndThrowIfRetryAnd(() -> !canRetryRead(source.getServerDescription(), binding.getSessionContext())); try { return createReadCommandAndExecute(retryState, binding, source, namespace.getDatabaseName(), getCommandCreator(binding.getSessionContext()), CommandResultDocumentCodec.create(decoder, FIRST_BATCH), transformer(), connection); } catch (MongoCommandException e) { throw new MongoQueryException(e.getResponse(), e.getServerAddress()); } }) ); return read.get(); } @Override public void executeAsync(final AsyncReadBinding binding, final SingleResultCallback> callback) { RetryState retryState = initialRetryState(retryReads); binding.retain(); AsyncCallbackSupplier> asyncRead = decorateReadWithRetriesAsync( retryState, binding.getOperationContext(), (AsyncCallbackSupplier>) funcCallback -> withAsyncSourceAndConnection(binding::getReadConnectionSource, false, funcCallback, (source, connection, releasingCallback) -> { if (retryState.breakAndCompleteIfRetryAnd(() -> !canRetryRead(source.getServerDescription(), binding.getSessionContext()), releasingCallback)) { return; } SingleResultCallback> wrappedCallback = exceptionTransformingCallback(releasingCallback); createReadCommandAndExecuteAsync(retryState, binding, source, namespace.getDatabaseName(), getCommandCreator(binding.getSessionContext()), CommandResultDocumentCodec.create(decoder, FIRST_BATCH), asyncTransformer(), connection, wrappedCallback); }) ).whenComplete(binding::release); asyncRead.get(errorHandlingCallback(callback, LOGGER)); } private static SingleResultCallback exceptionTransformingCallback(final SingleResultCallback callback) { return (result, t) -> { if (t != null) { if (t instanceof MongoCommandException) { MongoCommandException commandException = (MongoCommandException) t; callback.onResult(result, new MongoQueryException(commandException.getResponse(), commandException.getServerAddress())); } else { callback.onResult(result, t); } } else { callback.onResult(result, null); } }; } @Override public ReadOperation asExplainableOperation(@Nullable final ExplainVerbosity verbosity, final Decoder resultDecoder) { return new CommandReadOperation<>(getNamespace().getDatabaseName(), asExplainCommand(getCommand(NoOpSessionContext.INSTANCE, MIN_WIRE_VERSION), verbosity), resultDecoder); } @Override public AsyncReadOperation asAsyncExplainableOperation(@Nullable final ExplainVerbosity verbosity, final Decoder resultDecoder) { return new CommandReadOperation<>(getNamespace().getDatabaseName(), asExplainCommand(getCommand(NoOpSessionContext.INSTANCE, MIN_WIRE_VERSION), verbosity), resultDecoder); } private BsonDocument getCommand(final SessionContext sessionContext, final int maxWireVersion) { BsonDocument commandDocument = new BsonDocument("find", new BsonString(namespace.getCollectionName())); appendReadConcernToCommand(sessionContext, maxWireVersion, commandDocument); putIfNotNull(commandDocument, "filter", filter); putIfNotNullOrEmpty(commandDocument, "sort", sort); putIfNotNullOrEmpty(commandDocument, "projection", projection); if (skip > 0) { commandDocument.put("skip", new BsonInt32(skip)); } if (limit != 0) { commandDocument.put("limit", new BsonInt32(Math.abs(limit))); } if (limit >= 0) { if (batchSize < 0 && Math.abs(batchSize) < limit) { commandDocument.put("limit", new BsonInt32(Math.abs(batchSize))); } else if (batchSize != 0) { commandDocument.put("batchSize", new BsonInt32(Math.abs(batchSize))); } } if (limit < 0 || batchSize < 0) { commandDocument.put("singleBatch", BsonBoolean.TRUE); } if (maxTimeMS > 0) { commandDocument.put("maxTimeMS", new BsonInt64(maxTimeMS)); } if (isTailableCursor()) { commandDocument.put("tailable", BsonBoolean.TRUE); } if (isAwaitData()) { commandDocument.put("awaitData", BsonBoolean.TRUE); } if (noCursorTimeout) { commandDocument.put("noCursorTimeout", BsonBoolean.TRUE); } if (partial) { commandDocument.put("allowPartialResults", BsonBoolean.TRUE); } if (collation != null) { commandDocument.put("collation", collation.asDocument()); } if (comment != null) { commandDocument.put("comment", comment); } if (hint != null) { commandDocument.put("hint", hint); } if (variables != null) { commandDocument.put("let", variables); } if (max != null) { commandDocument.put("max", max); } if (min != null) { commandDocument.put("min", min); } if (returnKey) { commandDocument.put("returnKey", BsonBoolean.TRUE); } if (showRecordId) { commandDocument.put("showRecordId", BsonBoolean.TRUE); } if (allowDiskUse != null) { commandDocument.put("allowDiskUse", BsonBoolean.valueOf(allowDiskUse)); } return commandDocument; } private CommandCreator getCommandCreator(final SessionContext sessionContext) { return (serverDescription, connectionDescription) -> getCommand(sessionContext, connectionDescription.getMaxWireVersion()); } private boolean isTailableCursor() { return cursorType.isTailable(); } private boolean isAwaitData() { return cursorType == CursorType.TailableAwait; } private CommandReadTransformer> transformer() { return (result, source, connection) -> new CommandBatchCursor<>(result, batchSize, getMaxTimeForCursor(), decoder, comment, source, connection); } private long getMaxTimeForCursor() { return cursorType == CursorType.TailableAwait ? maxAwaitTimeMS : 0; } private CommandReadTransformerAsync> asyncTransformer() { return (result, source, connection) -> new AsyncCommandBatchCursor<>(result, batchSize, getMaxTimeForCursor(), decoder, comment, source, connection); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy