com.mongodb.internal.operation.ChangeStreamOperation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mongodb-driver-core Show documentation
Show all versions of mongodb-driver-core Show documentation
The Java operations layer for the MongoDB Java Driver. Third parties can ' +
'wrap this layer to provide custom higher-level APIs
/*
* 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.client.model.Collation;
import com.mongodb.client.model.changestream.FullDocument;
import com.mongodb.client.model.changestream.FullDocumentBeforeChange;
import com.mongodb.internal.async.AsyncAggregateResponseBatchCursor;
import com.mongodb.internal.async.AsyncBatchCursor;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.binding.AsyncReadBinding;
import com.mongodb.internal.binding.ReadBinding;
import com.mongodb.internal.client.model.changestream.ChangeStreamLevel;
import com.mongodb.internal.operation.OperationHelper.CallableWithSource;
import com.mongodb.lang.Nullable;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
import org.bson.BsonString;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.RawBsonDocument;
import org.bson.codecs.Decoder;
import org.bson.codecs.RawBsonDocumentCodec;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.operation.OperationHelper.withAsyncReadConnection;
import static com.mongodb.internal.operation.OperationHelper.withReadConnectionSource;
/**
* An operation that executes an {@code $changeStream} aggregation.
*
* This class is not part of the public API and may be removed or changed at any time
*/
public class ChangeStreamOperation implements AsyncReadOperation>, ReadOperation> {
private static final RawBsonDocumentCodec RAW_BSON_DOCUMENT_CODEC = new RawBsonDocumentCodec();
private final AggregateOperationImpl wrapped;
private final FullDocument fullDocument;
private final FullDocumentBeforeChange fullDocumentBeforeChange;
private final Decoder decoder;
private final ChangeStreamLevel changeStreamLevel;
private BsonDocument resumeAfter;
private BsonDocument startAfter;
private BsonTimestamp startAtOperationTime;
private boolean showExpandedEvents;
public ChangeStreamOperation(final MongoNamespace namespace, final FullDocument fullDocument,
final FullDocumentBeforeChange fullDocumentBeforeChange, final List pipeline, final Decoder decoder) {
this(namespace, fullDocument, fullDocumentBeforeChange, pipeline, decoder, ChangeStreamLevel.COLLECTION);
}
public ChangeStreamOperation(final MongoNamespace namespace, final FullDocument fullDocument,
final FullDocumentBeforeChange fullDocumentBeforeChange, final List pipeline,
final Decoder decoder, final ChangeStreamLevel changeStreamLevel) {
this.wrapped = new AggregateOperationImpl<>(namespace, pipeline, RAW_BSON_DOCUMENT_CODEC,
getAggregateTarget(), getPipelineCreator());
this.fullDocument = notNull("fullDocument", fullDocument);
this.fullDocumentBeforeChange = notNull("fullDocumentBeforeChange", fullDocumentBeforeChange);
this.decoder = notNull("decoder", decoder);
this.changeStreamLevel = notNull("changeStreamLevel", changeStreamLevel);
}
public MongoNamespace getNamespace() {
return wrapped.getNamespace();
}
public Decoder getDecoder() {
return decoder;
}
public FullDocument getFullDocument() {
return fullDocument;
}
public BsonDocument getResumeAfter() {
return resumeAfter;
}
public ChangeStreamOperation resumeAfter(final BsonDocument resumeAfter) {
this.resumeAfter = resumeAfter;
return this;
}
public BsonDocument getStartAfter() {
return startAfter;
}
public ChangeStreamOperation startAfter(final BsonDocument startAfter) {
this.startAfter = startAfter;
return this;
}
public List getPipeline() {
return wrapped.getPipeline();
}
public Integer getBatchSize() {
return wrapped.getBatchSize();
}
public ChangeStreamOperation batchSize(@Nullable final Integer batchSize) {
wrapped.batchSize(batchSize);
return this;
}
public long getMaxAwaitTime(final TimeUnit timeUnit) {
return wrapped.getMaxAwaitTime(timeUnit);
}
public ChangeStreamOperation maxAwaitTime(final long maxAwaitTime, final TimeUnit timeUnit) {
wrapped.maxAwaitTime(maxAwaitTime, timeUnit);
return this;
}
public Collation getCollation() {
return wrapped.getCollation();
}
public ChangeStreamOperation collation(final Collation collation) {
wrapped.collation(collation);
return this;
}
public ChangeStreamOperation startAtOperationTime(final BsonTimestamp startAtOperationTime) {
this.startAtOperationTime = startAtOperationTime;
return this;
}
public BsonTimestamp getStartAtOperationTime() {
return startAtOperationTime;
}
public ChangeStreamOperation retryReads(final boolean retryReads) {
wrapped.retryReads(retryReads);
return this;
}
public boolean getRetryReads() {
return wrapped.getRetryReads();
}
public BsonValue getComment() {
return wrapped.getComment();
}
public ChangeStreamOperation comment(final BsonValue comment) {
wrapped.comment(comment);
return this;
}
public boolean getShowExpandedEvents() {
return this.showExpandedEvents;
}
public ChangeStreamOperation showExpandedEvents(final boolean showExpandedEvents) {
this.showExpandedEvents = showExpandedEvents;
return this;
}
@Override
public BatchCursor execute(final ReadBinding binding) {
return withReadConnectionSource(binding, (CallableWithSource>) source -> {
AggregateResponseBatchCursor cursor =
(AggregateResponseBatchCursor) wrapped.execute(binding);
return new ChangeStreamBatchCursor<>(ChangeStreamOperation.this, cursor, binding,
setChangeStreamOptions(cursor.getPostBatchResumeToken(), cursor.getOperationTime(),
cursor.getMaxWireVersion(), cursor.isFirstBatchEmpty()), cursor.getMaxWireVersion());
});
}
@Override
public void executeAsync(final AsyncReadBinding binding, final SingleResultCallback> callback) {
wrapped.executeAsync(binding, (result, t) -> {
if (t != null) {
callback.onResult(null, t);
} else {
AsyncAggregateResponseBatchCursor cursor =
(AsyncAggregateResponseBatchCursor) result;
withAsyncReadConnection(binding, (source, t1) -> {
if (t1 != null) {
callback.onResult(null, t1);
} else {
callback.onResult(new AsyncChangeStreamBatchCursor<>(ChangeStreamOperation.this, cursor, binding,
setChangeStreamOptions(cursor.getPostBatchResumeToken(), cursor.getOperationTime(),
cursor.getMaxWireVersion(), cursor.isFirstBatchEmpty()), cursor.getMaxWireVersion()), null);
}
source.release(); // TODO: can this be null?
});
}
});
}
@Nullable
private BsonDocument setChangeStreamOptions(@Nullable final BsonDocument postBatchResumeToken, final BsonTimestamp operationTime,
final int maxWireVersion, final boolean firstBatchEmpty) {
BsonDocument resumeToken = null;
if (startAfter != null) {
resumeToken = startAfter;
} else if (resumeAfter != null) {
resumeToken = resumeAfter;
} else if (startAtOperationTime == null && postBatchResumeToken == null && firstBatchEmpty && maxWireVersion >= 7) {
startAtOperationTime = operationTime;
}
return resumeToken;
}
public void setChangeStreamOptionsForResume(@Nullable final BsonDocument resumeToken, final int maxWireVersion) {
startAfter = null;
if (resumeToken != null) {
startAtOperationTime = null;
resumeAfter = resumeToken;
} else if (startAtOperationTime != null && maxWireVersion >= 7) {
resumeAfter = null;
} else {
resumeAfter = null;
startAtOperationTime = null;
}
}
// Leave as anonymous class so as not to confuse CustomMatchers#compare
private AggregateOperationImpl.AggregateTarget getAggregateTarget() {
return () -> changeStreamLevel == ChangeStreamLevel.COLLECTION
? new BsonString(getNamespace().getCollectionName()) : new BsonInt32(1);
}
// Leave as anonymous class so as not to confuse CustomMatchers#compare
private AggregateOperationImpl.PipelineCreator getPipelineCreator() {
return () -> {
List changeStreamPipeline = new ArrayList<>();
BsonDocument changeStream = new BsonDocument();
if (fullDocument != FullDocument.DEFAULT) {
changeStream.append("fullDocument", new BsonString(fullDocument.getValue()));
}
if (fullDocumentBeforeChange != FullDocumentBeforeChange.DEFAULT) {
changeStream.append("fullDocumentBeforeChange", new BsonString(fullDocumentBeforeChange.getValue()));
}
if (changeStreamLevel == ChangeStreamLevel.CLIENT) {
changeStream.append("allChangesForCluster", BsonBoolean.TRUE);
}
if (showExpandedEvents) {
changeStream.append("showExpandedEvents", BsonBoolean.TRUE);
}
if (resumeAfter != null) {
changeStream.append("resumeAfter", resumeAfter);
}
if (startAfter != null) {
changeStream.append("startAfter", startAfter);
}
if (startAtOperationTime != null) {
changeStream.append("startAtOperationTime", startAtOperationTime);
}
changeStreamPipeline.add(new BsonDocument("$changeStream", changeStream));
changeStreamPipeline.addAll(getPipeline());
return new BsonArray(changeStreamPipeline);
};
}
}