Skip to content

Commit 0cb750c

Browse files
committed
JAVA-2862: Support nameOnly field for listCollections command
MongoDB 4.0 adds support for the nameOnly field to the listCollections command, which optimized for the case where the client only requires the name and not all of the other metadata associated with collections. This patch uses that optimization to implement the listCollectionNames methods that already exist in the driver.
1 parent 2cee042 commit 0cb750c

File tree

15 files changed

+181
-44
lines changed

15 files changed

+181
-44
lines changed

driver-async/src/main/com/mongodb/async/client/ListCollectionsIterableImpl.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ final class ListCollectionsIterableImpl<TResult> extends MongoIterableImpl<TResu
3737
private final Class<TResult> resultClass;
3838

3939
private Bson filter;
40+
private boolean collectionNamesOnly;
4041
private long maxTimeMS;
4142

42-
ListCollectionsIterableImpl(@Nullable final ClientSession clientSession, final String databaseName, final Class<TResult> resultClass,
43-
final CodecRegistry codecRegistry,
44-
final ReadPreference readPreference, final OperationExecutor executor) {
43+
ListCollectionsIterableImpl(@Nullable final ClientSession clientSession, final String databaseName, final boolean collectionNamesOnly,
44+
final Class<TResult> resultClass, final CodecRegistry codecRegistry, final ReadPreference readPreference,
45+
final OperationExecutor executor) {
4546
super(clientSession, executor, ReadConcern.DEFAULT, readPreference); // TODO: read concern?
47+
this.collectionNamesOnly = collectionNamesOnly;
4648
this.operations = new AsyncOperations<BsonDocument>(BsonDocument.class, readPreference, codecRegistry);
4749
this.databaseName = notNull("databaseName", databaseName);
4850
this.resultClass = notNull("resultClass", resultClass);
@@ -70,7 +72,7 @@ public ListCollectionsIterable<TResult> batchSize(final int batchSize) {
7072

7173
@Override
7274
AsyncReadOperation<AsyncBatchCursor<TResult>> asAsyncReadOperation() {
73-
return operations.listCollections(databaseName, resultClass, filter, getBatchSize(), maxTimeMS);
75+
return operations.listCollections(databaseName, resultClass, filter, collectionNamesOnly, getBatchSize(), maxTimeMS);
7476
}
7577

7678
}

driver-async/src/main/com/mongodb/async/client/MongoDatabaseImpl.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public MongoIterable<String> listCollectionNames(final ClientSession clientSessi
121121
}
122122

123123
private MongoIterable<String> createListCollectionNamesIterable(@Nullable final ClientSession clientSession) {
124-
return createListCollectionsIterable(clientSession, BsonDocument.class)
124+
return createListCollectionsIterable(clientSession, BsonDocument.class, true)
125125
.map(new Function<BsonDocument, String>() {
126126
@Override
127127
public String apply(final BsonDocument result) {
@@ -137,7 +137,7 @@ public ListCollectionsIterable<Document> listCollections() {
137137

138138
@Override
139139
public <TResult> ListCollectionsIterable<TResult> listCollections(final Class<TResult> resultClass) {
140-
return createListCollectionsIterable(null, resultClass);
140+
return createListCollectionsIterable(null, resultClass, false);
141141
}
142142

143143
@Override
@@ -148,13 +148,14 @@ public ListCollectionsIterable<Document> listCollections(final ClientSession cli
148148
@Override
149149
public <TResult> ListCollectionsIterable<TResult> listCollections(final ClientSession clientSession, final Class<TResult> resultClass) {
150150
notNull("clientSession", clientSession);
151-
return createListCollectionsIterable(clientSession, resultClass);
151+
return createListCollectionsIterable(clientSession, resultClass, false);
152152
}
153153

154154
private <TResult> ListCollectionsIterable<TResult> createListCollectionsIterable(@Nullable final ClientSession clientSession,
155-
final Class<TResult> resultClass) {
156-
return new ListCollectionsIterableImpl<TResult>(clientSession, name, resultClass, codecRegistry, ReadPreference.primary(),
157-
executor);
155+
final Class<TResult> resultClass,
156+
final boolean collectionNamesOnly) {
157+
return new ListCollectionsIterableImpl<TResult>(clientSession, name, collectionNamesOnly, resultClass, codecRegistry,
158+
ReadPreference.primary(), executor);
158159
}
159160

160161
@Override

driver-async/src/test/unit/com/mongodb/async/client/ListCollectionsIterableSpecification.groovy

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ class ListCollectionsIterableSpecification extends Specification {
4949
it[0].onResult(null, null)
5050
}
5151
}
52-
def executor = new TestOperationExecutor([cursor, cursor]);
53-
def listCollectionIterable = new ListCollectionsIterableImpl<Document>(null, 'db', Document, codecRegistry, readPreference,
52+
def executor = new TestOperationExecutor([cursor, cursor, cursor]);
53+
def listCollectionIterable = new ListCollectionsIterableImpl<Document>(null, 'db', false, Document, codecRegistry, readPreference,
5454
executor).filter(new Document('filter', 1)).batchSize(100).maxTime(1000, MILLISECONDS)
55+
def listCollectionNamesIterable = new ListCollectionsIterableImpl<Document>(null, 'db', true, Document, codecRegistry,
56+
readPreference, executor)
5557

5658
when: 'default input should be as expected'
5759
listCollectionIterable.into([]) { result, t -> }
@@ -72,6 +74,14 @@ class ListCollectionsIterableSpecification extends Specification {
7274
then: 'should use the overrides'
7375
expect operation, isTheSameAs(new ListCollectionsOperation<Document>('db', new DocumentCodec())
7476
.filter(new BsonDocument('filter', new BsonInt32(2))).batchSize(99).maxTime(999, MILLISECONDS))
77+
78+
when: 'requesting collection names only'
79+
listCollectionNamesIterable.into([]) { result, t -> }
80+
81+
operation = executor.getReadOperation() as ListCollectionsOperation<Document>
82+
83+
then: 'should create operation with nameOnly'
84+
expect operation, isTheSameAs(new ListCollectionsOperation<Document>('db', new DocumentCodec()).nameOnly(true))
7585
}
7686

7787
def 'should follow the MongoIterable interface as expected'() {
@@ -93,7 +103,7 @@ class ListCollectionsIterableSpecification extends Specification {
93103
}
94104
}
95105
def executor = new TestOperationExecutor([cursor(), cursor(), cursor(), cursor(), cursor()]);
96-
def mongoIterable = new ListCollectionsIterableImpl<Document>(null, 'db', Document, codecRegistry, readPreference, executor)
106+
def mongoIterable = new ListCollectionsIterableImpl<Document>(null, 'db', false, Document, codecRegistry, readPreference, executor)
97107

98108
when:
99109
def results = new FutureResultCallback()
@@ -155,7 +165,7 @@ class ListCollectionsIterableSpecification extends Specification {
155165

156166
def 'should check variables using notNull'() {
157167
given:
158-
def mongoIterable = new ListCollectionsIterableImpl<Document>(null, 'db', Document, codecRegistry, readPreference,
168+
def mongoIterable = new ListCollectionsIterableImpl<Document>(null, 'db', false, Document, codecRegistry, readPreference,
159169
Stub(OperationExecutor))
160170
def callback = Stub(SingleResultCallback)
161171
def block = Stub(Block)
@@ -201,7 +211,7 @@ class ListCollectionsIterableSpecification extends Specification {
201211
def 'should get and set batchSize as expected'() {
202212
when:
203213
def batchSize = 5
204-
def mongoIterable = new ListCollectionsIterableImpl<Document>(null, 'db', Document, codecRegistry, readPreference,
214+
def mongoIterable = new ListCollectionsIterableImpl<Document>(null, 'db', false, Document, codecRegistry, readPreference,
205215
Stub(OperationExecutor))
206216

207217
then:

driver-async/src/test/unit/com/mongodb/async/client/MongoDatabaseSpecification.groovy

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,23 +222,24 @@ class MongoDatabaseSpecification extends Specification {
222222
def listCollectionIterable = execute(listCollectionsMethod, session)
223223

224224
then:
225-
expect listCollectionIterable, isTheSameAs(new ListCollectionsIterableImpl<Document>(session, name, Document, codecRegistry,
225+
expect listCollectionIterable, isTheSameAs(new ListCollectionsIterableImpl<Document>(session, name, false, Document, codecRegistry,
226226
primary(), executor))
227227

228228
when:
229229
listCollectionIterable = execute(listCollectionsMethod, session, BsonDocument)
230230

231231
then:
232-
expect listCollectionIterable, isTheSameAs(new ListCollectionsIterableImpl<BsonDocument>(session, name, BsonDocument, codecRegistry,
232+
expect listCollectionIterable, isTheSameAs(new ListCollectionsIterableImpl<BsonDocument>(session, name, false, BsonDocument,
233+
codecRegistry,
233234
primary(), executor))
234235

235236
when:
236237
def listCollectionNamesIterable = execute(listCollectionNamesMethod, session)
237238

238239
then:
239240
// listCollectionNamesIterable is an instance of a MappingIterable, so have to get the mapped iterable inside it
240-
expect listCollectionNamesIterable.getMapped(), isTheSameAs(new ListCollectionsIterableImpl<BsonDocument>(session, name,
241-
BsonDocument, codecRegistry, primary(), executor))
241+
expect listCollectionNamesIterable.getMapped(), isTheSameAs(new ListCollectionsIterableImpl<BsonDocument>(session, name,
242+
true, BsonDocument, codecRegistry, primary(), executor))
242243

243244
where:
244245
session << [null, Stub(ClientSession)]

driver-core/src/main/com/mongodb/internal/operation/AsyncOperations.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,9 @@ public AsyncWriteOperation<Void> dropIndex(final Bson keys, final DropIndexOptio
206206

207207
public <TResult> AsyncReadOperation<AsyncBatchCursor<TResult>> listCollections(final String databaseName,
208208
final Class<TResult> resultClass, final Bson filter,
209+
final boolean collectionNamesOnly,
209210
final Integer batchSize, final long maxTimeMS) {
210-
return operations.listCollections(databaseName, resultClass, filter, batchSize, maxTimeMS);
211+
return operations.listCollections(databaseName, resultClass, filter, collectionNamesOnly, batchSize, maxTimeMS);
211212
}
212213

213214
public <TResult> AsyncReadOperation<AsyncBatchCursor<TResult>> listDatabases(final Class<TResult> resultClass, final Bson filter,

driver-core/src/main/com/mongodb/internal/operation/Operations.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,9 +453,11 @@ DropIndexOperation dropIndex(final Bson keys, final DropIndexOptions dropIndexOp
453453
}
454454

455455
<TResult> ListCollectionsOperation<TResult> listCollections(final String databaseName, final Class<TResult> resultClass,
456-
final Bson filter, final Integer batchSize, final long maxTimeMS) {
456+
final Bson filter, final boolean collectionNamesOnly,
457+
final Integer batchSize, final long maxTimeMS) {
457458
return new ListCollectionsOperation<TResult>(databaseName, codecRegistry.get(resultClass))
458459
.filter(toBsonDocumentOrNull(filter))
460+
.nameOnly(collectionNamesOnly)
459461
.batchSize(batchSize == null ? 0 : batchSize)
460462
.maxTime(maxTimeMS, MILLISECONDS);
461463
}

driver-core/src/main/com/mongodb/internal/operation/SyncOperations.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,9 @@ public WriteOperation<Void> dropIndex(final Bson keys, final DropIndexOptions op
201201
}
202202

203203
public <TResult> ReadOperation<BatchCursor<TResult>> listCollections(final String databaseName, final Class<TResult> resultClass,
204-
final Bson filter, final Integer batchSize, final long maxTimeMS) {
205-
return operations.listCollections(databaseName, resultClass, filter, batchSize, maxTimeMS);
204+
final Bson filter, final boolean collectionNamesOnly,
205+
final Integer batchSize, final long maxTimeMS) {
206+
return operations.listCollections(databaseName, resultClass, filter, collectionNamesOnly, batchSize, maxTimeMS);
206207
}
207208

208209
public <TResult> ReadOperation<BatchCursor<TResult>> listDatabases(final Class<TResult> resultClass, final Bson filter,

driver-core/src/main/com/mongodb/operation/ListCollectionsOperation.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.mongodb.connection.QueryResult;
3434
import com.mongodb.operation.CommandOperationHelper.CommandTransformer;
3535
import org.bson.BsonArray;
36+
import org.bson.BsonBoolean;
3637
import org.bson.BsonDocument;
3738
import org.bson.BsonDocumentReader;
3839
import org.bson.BsonInt32;
@@ -52,6 +53,7 @@
5253
import static com.mongodb.assertions.Assertions.notNull;
5354
import static com.mongodb.connection.ServerType.SHARD_ROUTER;
5455
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
56+
import static com.mongodb.internal.operation.ServerVersionHelper.serverIsAtLeastVersionThreeDotZero;
5557
import static com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol;
5658
import static com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocolAsync;
5759
import static com.mongodb.operation.CommandOperationHelper.isNamespaceError;
@@ -65,7 +67,6 @@
6567
import static com.mongodb.operation.OperationHelper.cursorDocumentToAsyncBatchCursor;
6668
import static com.mongodb.operation.OperationHelper.cursorDocumentToBatchCursor;
6769
import static com.mongodb.operation.OperationHelper.releasingCallback;
68-
import static com.mongodb.internal.operation.ServerVersionHelper.serverIsAtLeastVersionThreeDotZero;
6970
import static com.mongodb.operation.OperationHelper.withConnection;
7071
import static java.lang.String.format;
7172
import static java.util.Arrays.asList;
@@ -84,6 +85,7 @@ public class ListCollectionsOperation<T> implements AsyncReadOperation<AsyncBatc
8485
private BsonDocument filter;
8586
private int batchSize;
8687
private long maxTimeMS;
88+
private boolean nameOnly;
8789

8890
/**
8991
* Construct a new instance.
@@ -106,6 +108,17 @@ public BsonDocument getFilter() {
106108
return filter;
107109
}
108110

111+
/**
112+
* Gets whether only the collection names should be returned.
113+
*
114+
* @return true if only the collection names should be returned
115+
* @since 3.8
116+
* @mongodb.server.release 4.0
117+
*/
118+
public boolean isNameOnly() {
119+
return nameOnly;
120+
}
121+
109122
/**
110123
* Sets the query filter to apply to the query.
111124
*
@@ -118,6 +131,23 @@ public ListCollectionsOperation<T> filter(final BsonDocument filter) {
118131
return this;
119132
}
120133

134+
/**
135+
* Sets the query filter to apply to the query.
136+
* <p>
137+
* Note: this is advisory only, and should be considered an optimization. Server versions prior to MongoDB 4.0 will ignore
138+
* this request.
139+
* </p>
140+
*
141+
* @param nameOnly true if only the collection names should be requested from the server
142+
* @return this
143+
* @since 3.8
144+
* @mongodb.server.release 4.0
145+
*/
146+
public ListCollectionsOperation<T> nameOnly(final boolean nameOnly) {
147+
this.nameOnly = nameOnly;
148+
return this;
149+
}
150+
121151
/**
122152
* Gets the number of documents to return per batch.
123153
*
@@ -168,6 +198,8 @@ public ListCollectionsOperation<T> maxTime(final long maxTime, final TimeUnit ti
168198
return this;
169199
}
170200

201+
202+
171203
@Override
172204
public BatchCursor<T> execute(final ReadBinding binding) {
173205
return withConnection(binding, new CallableWithConnectionAndSource<BatchCursor<T>>() {
@@ -274,6 +306,9 @@ private BsonDocument getCommand() {
274306
if (filter != null) {
275307
command.append("filter", filter);
276308
}
309+
if (nameOnly) {
310+
command.append("nameOnly", BsonBoolean.TRUE);
311+
}
277312
if (maxTimeMS > 0) {
278313
command.put("maxTimeMS", new BsonInt64(maxTimeMS));
279314
}

driver-core/src/test/functional/com/mongodb/operation/ListCollectionsOperationSpecification.groovy

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,49 @@ class ListCollectionsOperationSpecification extends OperationFunctionalSpecifica
172172
!names.contains(collectionName)
173173
}
174174

175+
@IgnoreIf({ !serverVersionAtLeast(3, 4) || serverVersionAtLeast(4, 0) })
176+
def 'should get all fields when nameOnly is not requested'() {
177+
given:
178+
def operation = new ListCollectionsOperation(databaseName, new DocumentCodec())
179+
getCollectionHelper().create('collection4', new CreateCollectionOptions())
180+
181+
when:
182+
def cursor = operation.execute(getBinding())
183+
def collection = cursor.next()[0]
184+
185+
then:
186+
collection.size() > 2
187+
}
188+
189+
@IgnoreIf({ !serverVersionAtLeast(4, 0) })
190+
def 'should only get collection names when nameOnly is requested'() {
191+
given:
192+
def operation = new ListCollectionsOperation(databaseName, new DocumentCodec())
193+
.nameOnly(true)
194+
getCollectionHelper().create('collection5', new CreateCollectionOptions())
195+
196+
when:
197+
def cursor = operation.execute(getBinding())
198+
def collection = cursor.next()[0]
199+
200+
then:
201+
collection.size() == 2
202+
}
203+
204+
@IgnoreIf({ !serverVersionAtLeast(3, 4) || serverVersionAtLeast(4, 0) })
205+
def 'should only get all field names when nameOnly is requested on server versions that do not support nameOnly'() {
206+
given:
207+
def operation = new ListCollectionsOperation(databaseName, new DocumentCodec())
208+
.nameOnly(true)
209+
getCollectionHelper().create('collection6', new CreateCollectionOptions())
210+
211+
when:
212+
def cursor = operation.execute(getBinding())
213+
def collection = cursor.next()[0]
214+
215+
then:
216+
collection.size() > 2
217+
}
175218

176219
@Category(Async)
177220
def 'should return collection names if a collection exists asynchronously'() {

driver-legacy/src/main/com/mongodb/DB.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ public Set<String> getCollectionNames() {
260260
new MongoIterableImpl<DBObject>(null, executor, ReadConcern.DEFAULT, primary()) {
261261
@Override
262262
public ReadOperation<BatchCursor<DBObject>> asReadOperation() {
263-
return new ListCollectionsOperation<DBObject>(name, commandCodec);
263+
return new ListCollectionsOperation<DBObject>(name, commandCodec)
264+
.nameOnly(true);
264265
}
265266
}.map(new Function<DBObject, String>() {
266267
@Override

0 commit comments

Comments
 (0)