From 88fa0ad96b57e0d0c096a5a9fe7bc6d25850abbe Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 14 Mar 2024 15:17:29 -0600 Subject: [PATCH 01/17] misc test fixes --- .../collection-management/collection.test.ts | 18 ++---- test/integration/index_management.test.js | 61 ++++++------------- 2 files changed, 23 insertions(+), 56 deletions(-) diff --git a/test/integration/collection-management/collection.test.ts b/test/integration/collection-management/collection.test.ts index 5d645c46aa0..a4fa1d03677 100644 --- a/test/integration/collection-management/collection.test.ts +++ b/test/integration/collection-management/collection.test.ts @@ -389,20 +389,10 @@ describe('Collection', function () { ); }); - it('should support createIndex with no options', function (done) { - db.createCollection('create_index_without_options', {}, (err, collection) => { - collection.createIndex({ createdAt: 1 }, err => { - expect(err).to.not.exist; - - collection.indexInformation({ full: true }, (err, indexes) => { - expect(err).to.not.exist; - const indexNames = indexes.map(i => i.name); - expect(indexNames).to.include('createdAt_1'); - - done(); - }); - }); - }); + it('should support createIndex with no options', async function () { + const collection = await db.createCollection('create_index_without_options', {}); + await collection.createIndex({ createdAt: 1 }); + expect(await collection.indexExists('createdAt_1')).to.be.true; }); }); diff --git a/test/integration/index_management.test.js b/test/integration/index_management.test.js index 188452f11e0..1dd6fe6cc8e 100644 --- a/test/integration/index_management.test.js +++ b/test/integration/index_management.test.js @@ -459,48 +459,25 @@ describe('Indexes', function () { } }); - it('shouldCorrectlyCreateAndUseSparseIndex', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - db.createCollection('create_and_use_sparse_index_test', function (err) { - expect(err).to.not.exist; - const collection = db.collection('create_and_use_sparse_index_test'); - collection.createIndex( - { title: 1 }, - { sparse: true, writeConcern: { w: 1 } }, - function (err) { - expect(err).to.not.exist; - collection.insert( - [{ name: 'Jim' }, { name: 'Sarah', title: 'Princess' }], - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - collection - .find({ title: { $ne: null } }) - .sort({ title: 1 }) - .toArray(function (err, items) { - test.equal(1, items.length); - test.equal('Sarah', items[0].name); - - // Fetch the info for the indexes - collection.indexInformation({ full: true }, function (err, indexInfo) { - expect(err).to.not.exist; - test.equal(2, indexInfo.length); - client.close(done); - }); - }); - } - ); - } - ); - }); - } + it('shouldCorrectlyCreateAndUseSparseIndex', async function () { + const db = client.db(this.configuration.db); + await db.createCollection('create_and_use_sparse_index_test'); + const collection = db.collection('create_and_use_sparse_index_test'); + await collection.createIndex({ title: 1 }, { sparse: true, writeConcern: { w: 1 } }); + await collection.insertMany( + [{ name: 'Jim' }, { name: 'Sarah', title: 'Princess' }], + this.configuration.writeConcernMax() + ); + const items = await collection + .find({ title: { $ne: null } }) + .sort({ title: 1 }) + .toArray(); + expect(items).to.have.lengthOf(1); + expect(items[0]).to.have.property('name', 'Sarah'); + + // Fetch the info for the indexes + const indexInfo = await collection.indexInformation({ full: true }); + expect(indexInfo).to.have.lengthOf(2); }); it('shouldCorrectlyHandleGeospatialIndexes', { From f78b65fb964f7e5a90aadbd67f1e186b5c900530 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 14 Mar 2024 15:24:05 -0600 Subject: [PATCH 02/17] convert index managment spec tests to TS --- ...ement.test.js => index_management.test.ts} | 243 +++++++++--------- 1 file changed, 121 insertions(+), 122 deletions(-) rename test/integration/{index_management.test.js => index_management.test.ts} (83%) diff --git a/test/integration/index_management.test.js b/test/integration/index_management.test.ts similarity index 83% rename from test/integration/index_management.test.js rename to test/integration/index_management.test.ts index 1dd6fe6cc8e..4f70e3aaee8 100644 --- a/test/integration/index_management.test.js +++ b/test/integration/index_management.test.ts @@ -1,8 +1,7 @@ -'use strict'; -const { expect } = require('chai'); +import { expect } from 'chai'; -const { assert: test, setupDatabase } = require('./shared'); -const shared = require('../tools/contexts'); +import shared from '../tools/contexts'; +import { assert as test, setupDatabase } from './shared'; describe('Indexes', function () { let client; @@ -29,7 +28,7 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; + const configuration = this.configuration; const client = configuration.newClient({ maxPoolSize: 5 }); // Create an index client @@ -51,7 +50,7 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; + const configuration = this.configuration; const client = configuration.newClient({ maxPoolSize: 5 }); // Create an index @@ -72,9 +71,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection('test_index_information', function (err, collection) { collection.insertMany([{ a: 1 }], configuration.writeConcernMax(), function (err) { expect(err).to.not.exist; @@ -97,7 +96,7 @@ describe('Indexes', function () { test.deepEqual([['a', 1]], collectionInfo['a_1']); db.indexInformation(collection.collectionName, function (err, collectionInfo2) { - var count1 = Object.keys(collectionInfo).length, + const count1 = Object.keys(collectionInfo).length, count2 = Object.keys(collectionInfo2).length; // Tests @@ -126,9 +125,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection('test_multiple_index_cols', function (err, collection) { collection.insert({ a: 1 }, function (err) { expect(err).to.not.exist; @@ -146,7 +145,7 @@ describe('Indexes', function () { test.equal('a_-1_b_1_c_-1', indexName); // Let's fetch the index information db.indexInformation(collection.collectionName, function (err, collectionInfo) { - var count1 = Object.keys(collectionInfo).length; + const count1 = Object.keys(collectionInfo).length; // Test test.equal(2, count1); @@ -176,9 +175,9 @@ describe('Indexes', function () { metadata: { requires: { topology: 'single' } }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // Create a non-unique index and test inserts db.createCollection('test_unique_index', function (err, collection) { db.createIndex( @@ -229,9 +228,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // Create a non-unique index and test inserts db.createCollection('test_index_on_subfield', function (err, collection) { collection.insert( @@ -379,9 +378,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection('test_distinct_queries', function (err, collection) { collection.insert( [ @@ -415,9 +414,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection('test_ensure_index', function (err, collection) { expect(err).to.not.exist; // Create an index on the collection @@ -491,9 +490,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection('geospatial_index_test', function (err) { expect(err).to.not.exist; const collection = db.collection('geospatial_index_test'); @@ -525,9 +524,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection('geospatial_index_altered_test', function (err) { expect(err).to.not.exist; const collection = db.collection('geospatial_index_altered_test'); @@ -568,9 +567,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection( 'shouldThrowDuplicateKeyErrorWhenCreatingIndex', function (err, collection) { @@ -597,9 +596,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.createCollection( 'shouldThrowDuplicateKeyErrorWhenDriverInStrictMode', function (err, collection) { @@ -626,9 +625,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // Establish connection to db db.createCollection( 'shouldCorrectlyUseMinMaxForSettingRangeInEnsureIndex', @@ -662,9 +661,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // Establish connection to db db.createCollection( 'shouldCorrectlyCreateAnIndexWithOverridenName', @@ -691,9 +690,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('indexcontext').createIndex( shared.object, @@ -719,10 +718,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('should_throw_error_due_to_duplicates'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('should_throw_error_due_to_duplicates'); collection.insert( [{ a: 1 }, { a: 1 }, { a: 1 }], configuration.writeConcernMax(), @@ -748,10 +747,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('should_correctly_drop_index'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('should_correctly_drop_index'); collection.insert([{ a: 1 }], configuration.writeConcernMax(), function (err) { expect(err).to.not.exist; @@ -777,10 +776,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('should_correctly_apply_hint'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('should_correctly_apply_hint'); collection.insert([{ a: 1 }], configuration.writeConcernMax(), function (err) { expect(err).to.not.exist; @@ -810,10 +809,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('should_correctly_set_language_override'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('should_correctly_set_language_override'); collection.insert( [{ text: 'Lorem ipsum dolor sit amet.', langua: 'italian' }], function (err) { @@ -827,7 +826,7 @@ describe('Indexes', function () { collection.indexInformation({ full: true }, function (err, indexInformation) { expect(err).to.not.exist; - for (var i = 0; i < indexInformation.length; i++) { + for (let i = 0; i < indexInformation.length; i++) { if (indexInformation[i].name === 'language_override_index') test.equal(indexInformation[i].language_override, 'langua'); } @@ -847,9 +846,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('testListIndexes').createIndex({ a: 1 }, function (err) { expect(err).to.not.exist; @@ -872,9 +871,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('testListIndexes_2').createIndex({ a: 1 }, function (err) { expect(err).to.not.exist; @@ -897,9 +896,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('ensureIndexWithNestedStyleIndex').createIndex({ 'c.d': 1 }, function (err) { expect(err).to.not.exist; @@ -920,9 +919,9 @@ describe('Indexes', function () { metadata: { requires: { mongodb: '>=2.6.0', topology: ['single'] } }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('createIndexes').createIndexes( [{ key: { a: 1 } }, { key: { b: 1 }, name: 'hello1' }], function (err, r) { @@ -933,9 +932,9 @@ describe('Indexes', function () { .listIndexes() .toArray(function (err, docs) { expect(err).to.not.exist; - var keys = {}; + const keys = {}; - for (var i = 0; i < docs.length; i++) { + for (let i = 0; i < docs.length; i++) { keys[docs[i].name] = true; } @@ -953,9 +952,9 @@ describe('Indexes', function () { metadata: { requires: { mongodb: '>=2.6.0', topology: ['single'] } }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('createIndexes').createIndexes([{ key: { a: 1 } }], function (err, r) { expect(err).to.not.exist; expect(r).to.deep.equal(['a_1']); @@ -964,9 +963,9 @@ describe('Indexes', function () { .listIndexes() .toArray(function (err, docs) { expect(err).to.not.exist; - var keys = {}; + const keys = {}; - for (var i = 0; i < docs.length; i++) { + for (let i = 0; i < docs.length; i++) { keys[docs[i].name] = true; } @@ -985,9 +984,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); db.collection('text_index').createIndex( { '$**': 'text' }, { name: 'TextIndex' }, @@ -1010,10 +1009,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var started = []; - var succeeded = []; - var client = configuration.newClient(configuration.writeConcernMax(), { + const configuration = this.configuration; + const started = []; + const succeeded = []; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1, monitorCommands: true }); @@ -1026,7 +1025,7 @@ describe('Indexes', function () { if (event.commandName === 'createIndexes') succeeded.push(event); }); - var db = client.db(configuration.db); + const db = client.db(configuration.db); db.collection('partialIndexes').createIndex( { a: 1 }, @@ -1049,16 +1048,16 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // Can't use $exists: false in partial filter expression, see // https://jira.mongodb.org/browse/SERVER-17853 - var opts = { partialFilterExpression: { a: { $exists: false } } }; + const opts = { partialFilterExpression: { a: { $exists: false } } }; db.collection('partialIndexes').createIndex({ a: 1 }, opts, function (err) { test.ok(err); test.equal(err.code, 67); - var msg = "key $exists must not start with '$'"; + const msg = "key $exists must not start with '$'"; test.ok(err.toString().indexOf(msg) === -1); client.close(done); @@ -1072,10 +1071,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('embedded_key_indes'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('embedded_key_indes'); collection.insertMany( [ @@ -1104,10 +1103,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('embedded_key_indes_1'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('embedded_key_indes_1'); collection.createIndex( { 'key.external_id': 1, 'key.type': 1 }, { unique: true, sparse: true, name: 'indexname' }, @@ -1126,10 +1125,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('embedded_key_indes_2'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('embedded_key_indes_2'); collection.insertMany( [ { @@ -1161,9 +1160,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // insert a doc db.collection('messed_up_index').createIndex( { temporary: 1, 'store.addressLines': 1, lifecycleStatus: 1 }, @@ -1186,10 +1185,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('messed_up_options'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('messed_up_options'); collection.createIndex( { 'a.one': 1, 'a.two': 1 }, @@ -1221,10 +1220,10 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); - var collection = db.collection('messed_up_options'); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); + const collection = db.collection('messed_up_options'); collection.createIndex({ 'b.one': 1, 'b.two': 1 }, { name: 'test' }, function (err) { expect(err).to.not.exist; @@ -1245,9 +1244,9 @@ describe('Indexes', function () { }, test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - var db = client.db(configuration.db); + const configuration = this.configuration; + const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); + const db = client.db(configuration.db); // insert a doc db.collection('messed_up_index_2').createIndex( { 'accessControl.get': 1 }, From 1e9a7522b8d44c3118ff43c862f9d3a7f313eb1a Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 15 Mar 2024 09:19:26 -0600 Subject: [PATCH 03/17] refactor `index_management` tests to async-await --- test/integration/index_management.test.ts | 1471 ++++++--------------- 1 file changed, 370 insertions(+), 1101 deletions(-) diff --git a/test/integration/index_management.test.ts b/test/integration/index_management.test.ts index 4f70e3aaee8..502b6af1e2f 100644 --- a/test/integration/index_management.test.ts +++ b/test/integration/index_management.test.ts @@ -1,273 +1,146 @@ import { expect } from 'chai'; -import shared from '../tools/contexts'; +import { + type Collection, + type CommandStartedEvent, + type CommandSucceededEvent, + type Db, + type MongoClient, + MongoServerError +} from '../mongodb'; import { assert as test, setupDatabase } from './shared'; describe('Indexes', function () { - let client; - let db; + let client: MongoClient; + let db: Db; + let collection: Collection; before(function () { return setupDatabase(this.configuration); }); - beforeEach(function () { - client = this.configuration.newClient(); - db = client.db(); + beforeEach(async function () { + client = this.configuration.newClient({}, { monitorCommands: true }); + db = client.db('indexes_test_db'); + collection = await db.createCollection('indexes_test_coll', { w: 1 }); }); afterEach(async function () { + await db.dropDatabase({ writeConcern: { w: 1 } }).catch(() => null); await client.close(); }); - it('Should correctly execute createIndex', { - metadata: { - requires: { - topology: ['single'] - } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient({ maxPoolSize: 5 }); - // Create an index - client - .db(configuration.db) - .createIndex('promiseCollectionCollections1', { a: 1 }) - .then(function (r) { - test.ok(r != null); - - client.close(done); - }); - } + it('Should correctly execute createIndex', async function () { + // Create an index + const response = await db.createIndex('promiseCollectionCollections1', { a: 1 }, { w: 1 }); + expect(response).to.exist; }); - it('Should correctly execute ensureIndex using Promise', { - metadata: { - requires: { - topology: ['single'] - } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient({ maxPoolSize: 5 }); - - // Create an index - client - .db(configuration.db) - .createIndex('promiseCollectionCollections2', { a: 1 }) - .then(function (r) { - test.ok(r != null); - - client.close(done); - }); - } - }); + it('shouldCorrectlyExtractIndexInformation', async function () { + const collection = await db.createCollection('test_index_information'); + await collection.insertMany([{ a: 1 }], this.configuration.writeConcernMax()); - it('shouldCorrectlyExtractIndexInformation', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('test_index_information', function (err, collection) { - collection.insertMany([{ a: 1 }], configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - - // Create an index on the collection - db.createIndex( - collection.collectionName, - 'a', - configuration.writeConcernMax(), - function (err, indexName) { - expect(err).to.not.exist; - test.equal('a_1', indexName); - - // Let's fetch the index information - db.indexInformation(collection.collectionName, function (err, collectionInfo) { - expect(err).to.not.exist; - test.ok(collectionInfo['_id_'] != null); - test.equal('_id', collectionInfo['_id_'][0][0]); - test.ok(collectionInfo['a_1'] != null); - test.deepEqual([['a', 1]], collectionInfo['a_1']); - - db.indexInformation(collection.collectionName, function (err, collectionInfo2) { - const count1 = Object.keys(collectionInfo).length, - count2 = Object.keys(collectionInfo2).length; - - // Tests - test.ok(count2 >= count1); - test.ok(collectionInfo2['_id_'] != null); - test.equal('_id', collectionInfo2['_id_'][0][0]); - test.ok(collectionInfo2['a_1'] != null); - test.deepEqual([['a', 1]], collectionInfo2['a_1']); - test.ok(collectionInfo[indexName] != null); - test.deepEqual([['a', 1]], collectionInfo[indexName]); - - // Let's close the db - client.close(done); - }); - }); - } - ); - }); - }); - } - }); - - it('shouldCorrectlyHandleMultipleColumnIndexes', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('test_multiple_index_cols', function (err, collection) { - collection.insert({ a: 1 }, function (err) { - expect(err).to.not.exist; - // Create an index on the collection - db.createIndex( - collection.collectionName, - [ - ['a', -1], - ['b', 1], - ['c', -1] - ], - configuration.writeConcernMax(), - function (err, indexName) { - expect(err).to.not.exist; - test.equal('a_-1_b_1_c_-1', indexName); - // Let's fetch the index information - db.indexInformation(collection.collectionName, function (err, collectionInfo) { - const count1 = Object.keys(collectionInfo).length; - - // Test - test.equal(2, count1); - test.ok(collectionInfo[indexName] != null); - test.deepEqual( - [ - ['a', -1], - ['b', 1], - ['c', -1] - ], - collectionInfo[indexName] - ); - - // Let's close the db - client.close(done); - }); - } - ); - }); - }); - } + // Create an index on the collection + const indexName = await db.createIndex( + collection.collectionName, + 'a', + this.configuration.writeConcernMax() + ); + expect(indexName).to.equal('a_1'); + + // Let's fetch the index information + const collectionInfo = await db.indexInformation(collection.collectionName); + test.ok(collectionInfo['_id_'] != null); + test.equal('_id', collectionInfo['_id_'][0][0]); + test.ok(collectionInfo['a_1'] != null); + test.deepEqual([['a', 1]], collectionInfo['a_1']); + + const collectionInfo2 = await db.indexInformation(collection.collectionName); + const count1 = Object.keys(collectionInfo).length, + count2 = Object.keys(collectionInfo2).length; + + // Tests + test.ok(count2 >= count1); + test.ok(collectionInfo2['_id_'] != null); + test.equal('_id', collectionInfo2['_id_'][0][0]); + test.ok(collectionInfo2['a_1'] != null); + test.deepEqual([['a', 1]], collectionInfo2['a_1']); + test.ok(collectionInfo[indexName] != null); + test.deepEqual([['a', 1]], collectionInfo[indexName]); + }); + + it('shouldCorrectlyHandleMultipleColumnIndexes', async function () { + await collection.insert({ a: 1 }); + + const indexName = await db.createIndex( + collection.collectionName, + [ + ['a', -1], + ['b', 1], + ['c', -1] + ], + this.configuration.writeConcernMax() + ); + test.equal('a_-1_b_1_c_-1', indexName); + // Let's fetch the index information + const collectionInfo = await db.indexInformation(collection.collectionName); + const count1 = Object.keys(collectionInfo).length; + + // Test + test.equal(2, count1); + test.ok(collectionInfo[indexName] != null); + test.deepEqual( + [ + ['a', -1], + ['b', 1], + ['c', -1] + ], + collectionInfo[indexName] + ); }); - it('shouldCorrectlyHandleUniqueIndex', { - // Add a tag that our runner can trigger on - // in this case we are setting that node needs to be higher than 0.10.X to run - metadata: { requires: { topology: 'single' } }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // Create a non-unique index and test inserts - db.createCollection('test_unique_index', function (err, collection) { - db.createIndex( - collection.collectionName, - 'hello', - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - // Insert some docs - collection.insert( - [{ hello: 'world' }, { hello: 'mike' }, { hello: 'world' }], - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - - // Create a unique index and test that insert fails - db.createCollection('test_unique_index2', function (err, collection) { - db.createIndex( - collection.collectionName, - 'hello', - { unique: true, writeConcern: { w: 1 } }, - function (err) { - expect(err).to.not.exist; - // Insert some docs - collection.insert( - [{ hello: 'world' }, { hello: 'mike' }, { hello: 'world' }], - configuration.writeConcernMax(), - function (err) { - test.ok(err != null); - test.equal(11000, err.code); - client.close(done); - } - ); - } - ); - }); - } - ); - } - ); + it('shouldCorrectlyHandleUniqueIndex', async function () { + await db.createCollection('test_unique_index'); + await db.createIndex(collection.collectionName, 'hello', this.configuration.writeConcernMax()); + // Insert some docs + await collection.insertMany( + [{ hello: 'world' }, { hello: 'mike' }, { hello: 'world' }], + this.configuration.writeConcernMax() + ); + // Create a unique index and test that insert fails + { + const collection = await db.createCollection('test_unique_index2'); + await db.createIndex(collection.collectionName, 'hello', { + unique: true, + writeConcern: { w: 1 } }); + // Insert some docs + const err = await collection + .insertMany( + [{ hello: 'world' }, { hello: 'mike' }, { hello: 'world' }], + this.configuration.writeConcernMax() + ) + .catch(e => e); + expect(err).to.be.instanceOf(Error).to.have.property('code', 11000); } }); - it('shouldCorrectlyCreateSubfieldIndex', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // Create a non-unique index and test inserts - db.createCollection('test_index_on_subfield', function (err, collection) { - collection.insert( - [{ hello: { a: 4, b: 5 } }, { hello: { a: 7, b: 2 } }, { hello: { a: 4, b: 10 } }], - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; + it('shouldCorrectlyCreateSubfieldIndex', async function () { + await collection.insertMany( + [{ hello: { a: 4, b: 5 } }, { hello: { a: 7, b: 2 } }, { hello: { a: 4, b: 10 } }], + this.configuration.writeConcernMax() + ); - // Create a unique subfield index and test that insert fails - db.createCollection('test_index_on_subfield2', function (err, collection) { - db.createIndex( - collection.collectionName, - 'hello_a', - { writeConcern: { w: 1 }, unique: true }, - function (err) { - expect(err).to.not.exist; - - collection.insert( - [ - { hello: { a: 4, b: 5 } }, - { hello: { a: 7, b: 2 } }, - { hello: { a: 4, b: 10 } } - ], - configuration.writeConcernMax(), - function (err) { - // Assert that we have erros - test.ok(err != null); - client.close(done); - } - ); - } - ); - }); - } - ); - }); - } + // Create a unique subfield index and test that insert fails + const collection2 = await db.createCollection('test_index_on_subfield2'); + await collection2.createIndex('hello_a', { writeConcern: { w: 1 }, unique: true }); + const err = await collection2 + .insertMany( + [{ hello: { a: 4, b: 5 } }, { hello: { a: 7, b: 2 } }, { hello: { a: 4, b: 10 } }], + this.configuration.writeConcernMax() + ) + .catch(e => e); + expect(err).to.be.instanceOf(Error); }); context('when dropIndexes succeeds', function () { @@ -372,89 +245,26 @@ describe('Indexes', function () { }); }); - it('shouldCorrectlyHandleDistinctIndexes', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('test_distinct_queries', function (err, collection) { - collection.insert( - [ - { a: 0, b: { c: 'a' } }, - { a: 1, b: { c: 'b' } }, - { a: 1, b: { c: 'c' } }, - { a: 2, b: { c: 'a' } }, - { a: 3 }, - { a: 3 } - ], - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - collection.distinct('a', function (err, docs) { - test.deepEqual([0, 1, 2, 3], docs.sort()); - - collection.distinct('b.c', function (err, docs) { - test.deepEqual(['a', 'b', 'c'], docs.sort()); - client.close(done); - }); - }); - } - ); - }); + it('shouldCorrectlyHandleDistinctIndexes', async function () { + await collection.insertMany( + [ + { a: 0, b: { c: 'a' } }, + { a: 1, b: { c: 'b' } }, + { a: 1, b: { c: 'c' } }, + { a: 2, b: { c: 'a' } }, + { a: 3 }, + { a: 3 } + ], + this.configuration.writeConcernMax() + ); + { + const docs = await collection.distinct('a'); + expect(docs.sort()).to.deep.equal([0, 1, 2, 3]); } - }); - it('shouldCorrectlyExecuteEnsureIndex', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('test_ensure_index', function (err, collection) { - expect(err).to.not.exist; - // Create an index on the collection - db.createIndex( - collection.collectionName, - 'a', - configuration.writeConcernMax(), - function (err, indexName) { - expect(err).to.not.exist; - test.equal('a_1', indexName); - // Let's fetch the index information - db.indexInformation(collection.collectionName, function (err, collectionInfo) { - test.ok(collectionInfo['_id_'] != null); - test.equal('_id', collectionInfo['_id_'][0][0]); - test.ok(collectionInfo['a_1'] != null); - test.deepEqual([['a', 1]], collectionInfo['a_1']); - - db.createIndex( - collection.collectionName, - 'a', - configuration.writeConcernMax(), - function (err, indexName) { - test.equal('a_1', indexName); - // Let's fetch the index information - db.indexInformation(collection.collectionName, function (err, collectionInfo) { - test.ok(collectionInfo['_id_'] != null); - test.equal('_id', collectionInfo['_id_'][0][0]); - test.ok(collectionInfo['a_1'] != null); - test.deepEqual([['a', 1]], collectionInfo['a_1']); - // Let's close the db - client.close(done); - }); - } - ); - }); - } - ); - }); + { + const docs = await collection.distinct('b.c'); + expect(docs.sort()).to.deep.equal(['a', 'b', 'c']); } }); @@ -479,785 +289,264 @@ describe('Indexes', function () { expect(indexInfo).to.have.lengthOf(2); }); - it('shouldCorrectlyHandleGeospatialIndexes', { - // Add a tag that our runner can trigger on - // in this case we are setting that node needs to be higher than 0.10.X to run - metadata: { - requires: { - mongodb: '>2.6.0', - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] - } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('geospatial_index_test', function (err) { - expect(err).to.not.exist; - const collection = db.collection('geospatial_index_test'); - collection.createIndex({ loc: '2d' }, configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - collection.insert({ loc: [-100, 100] }, configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; + it('shouldCorrectlyHandleGeospatialIndexes', async function () { + await collection.createIndex({ loc: '2d' }, this.configuration.writeConcernMax()); + await collection.insertOne({ loc: [-100, 100] }, this.configuration.writeConcernMax()); - collection.insert({ loc: [200, 200] }, configuration.writeConcernMax(), function (err) { - test.ok(err.errmsg.indexOf('point not in interval of') !== -1); - test.ok(err.errmsg.indexOf('-180') !== -1); - test.ok(err.errmsg.indexOf('180') !== -1); - client.close(done); - }); - }); - }); - }); - } + const err = await collection + .insertOne({ loc: [200, 200] }, this.configuration.writeConcernMax()) + .catch(e => e); + test.ok(err.errmsg.indexOf('point not in interval of') !== -1); + test.ok(err.errmsg.indexOf('-180') !== -1); + test.ok(err.errmsg.indexOf('180') !== -1); }); - it('shouldCorrectlyHandleGeospatialIndexesAlteredRange', { - // Add a tag that our runner can trigger on - // in this case we are setting that node needs to be higher than 0.10.X to run - metadata: { - requires: { - mongodb: '>2.6.0', - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] - } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('geospatial_index_altered_test', function (err) { - expect(err).to.not.exist; - const collection = db.collection('geospatial_index_altered_test'); - collection.createIndex( - { loc: '2d' }, - { min: 0, max: 1024, writeConcern: { w: 1 } }, - function (err) { - expect(err).to.not.exist; - collection.insert({ loc: [100, 100] }, configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - collection.insert( - { loc: [200, 200] }, - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - collection.insert( - { loc: [-200, -200] }, - configuration.writeConcernMax(), - function (err) { - test.ok(err.errmsg.indexOf('point not in interval of') !== -1); - test.ok(err.errmsg.indexOf('0') !== -1); - test.ok(err.errmsg.indexOf('1024') !== -1); - client.close(done); - } - ); - } - ); - }); - } - ); - }); - } + it('shouldCorrectlyHandleGeospatialIndexesAlteredRange', async function () { + await collection.createIndex({ loc: '2d' }, { min: 0, max: 1024, writeConcern: { w: 1 } }); + await collection.insertOne({ loc: [100, 100] }, this.configuration.writeConcernMax()); + await collection.insertOne({ loc: [200, 200] }, this.configuration.writeConcernMax()); + const err = await collection + .insertOne({ loc: [-200, -200] }, this.configuration.writeConcernMax()) + .catch(e => e); + test.ok(err.errmsg.indexOf('point not in interval of') !== -1); + test.ok(err.errmsg.indexOf('0') !== -1); + test.ok(err.errmsg.indexOf('1024') !== -1); }); - it('shouldThrowDuplicateKeyErrorWhenCreatingIndex', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection( - 'shouldThrowDuplicateKeyErrorWhenCreatingIndex', - function (err, collection) { - collection.insert([{ a: 1 }, { a: 1 }], configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - - collection.createIndex( - { a: 1 }, - { unique: true, writeConcern: { w: 1 } }, - function (err) { - test.ok(err != null); - client.close(done); - } - ); - }); - } - ); - } + it('shouldThrowDuplicateKeyErrorWhenCreatingIndex', async function () { + await collection.insertMany([{ a: 1 }, { a: 1 }], this.configuration.writeConcernMax()); + const err = await collection + .createIndex({ a: 1 }, { unique: true, writeConcern: { w: 1 } }) + .catch(e => e); + expect(err).to.exist; }); - it('shouldThrowDuplicateKeyErrorWhenDriverInStrictMode', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection( - 'shouldThrowDuplicateKeyErrorWhenDriverInStrictMode', - function (err, collection) { - collection.insert([{ a: 1 }, { a: 1 }], configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - - collection.createIndex( - { a: 1 }, - { unique: true, writeConcern: { w: 1 } }, - function (err) { - test.ok(err != null); - client.close(done); - } - ); - }); - } - ); - } + it('shouldThrowDuplicateKeyErrorWhenDriverInStrictMode', async function () { + await collection.insertMany([{ a: 1 }, { a: 1 }], this.configuration.writeConcernMax()); + const err = await collection + .createIndex({ a: 1 }, { unique: true, writeConcern: { w: 1 } }) + .catch(e => e); + expect(err) + .to.be.instanceOf(MongoServerError) + .to.match(/duplicate key error/); }); - it('shouldCorrectlyUseMinMaxForSettingRangeInEnsureIndex', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // Establish connection to db - db.createCollection( - 'shouldCorrectlyUseMinMaxForSettingRangeInEnsureIndex', - function (err, collection) { - expect(err).to.not.exist; - - collection.createIndex( - { loc: '2d' }, - { min: 200, max: 1400, writeConcern: { w: 1 } }, - function (err) { - expect(err).to.not.exist; - - collection.insert( - { loc: [600, 600] }, - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - client.close(done); - } - ); - } - ); - } - ); - } + it('shouldCorrectlyUseMinMaxForSettingRangeInEnsureIndex', async function () { + await collection.createIndex({ loc: '2d' }, { min: 200, max: 1400, writeConcern: { w: 1 } }); + await collection.insertOne({ loc: [600, 600] }, this.configuration.writeConcernMax()); }); - it('Should correctly create an index with overriden name', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, + it('Should correctly create an index with overriden name', async function () { + await collection.createIndex('name', { name: 'myfunky_name' }); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // Establish connection to db - db.createCollection( - 'shouldCorrectlyCreateAnIndexWithOverridenName', - function (err, collection) { - expect(err).to.not.exist; - - collection.createIndex('name', { name: 'myfunky_name' }, function (err) { - expect(err).to.not.exist; - - // Fetch full index information - collection.indexInformation({ full: false }, function (err, indexInformation) { - test.ok(indexInformation['myfunky_name'] != null); - client.close(done); - }); - }); - } - ); - } + // Fetch full index information + const indexInformation = await collection.indexInformation({ full: false }); + expect(indexInformation).to.have.property('myfunky_name'); }); - it('should handle index declarations using objects from other contexts', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, + it('should correctly return error message when applying unique index to duplicate documents', async function () { + await collection.insertMany( + [{ a: 1 }, { a: 1 }, { a: 1 }], + this.configuration.writeConcernMax() + ); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - - db.collection('indexcontext').createIndex( - shared.object, - { background: true }, - function (err) { - expect(err).to.not.exist; - db.collection('indexcontext').createIndex( - shared.array, - { background: true }, - function (err) { - expect(err).to.not.exist; - client.close(done); - } - ); - } - ); - } + const err = await collection + .createIndex({ a: 1 }, { writeConcern: { w: 1 }, unique: true }) + .catch(e => e); + expect(err) + .to.be.instanceOf(MongoServerError) + .to.match(/duplicate key error/); }); - it('should correctly return error message when applying unique index to duplicate documents', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, + it('should correctly drop index with no callback', async function () { + await collection.insertMany([{ a: 1 }], this.configuration.writeConcernMax()); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('should_throw_error_due_to_duplicates'); - collection.insert( - [{ a: 1 }, { a: 1 }, { a: 1 }], - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - - collection.createIndex( - { a: 1 }, - { writeConcern: { w: 1 }, unique: true }, - function (err) { - test.ok(err != null); - client.close(done); - } - ); - } - ); - } + await collection.createIndex({ a: 1 }, { writeConcern: this.configuration.writeConcernMax() }); + await collection.dropIndex('a_1'); }); - it('should correctly drop index with no callback', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('should_correctly_drop_index'); - collection.insert([{ a: 1 }], configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - - collection.createIndex({ a: 1 }, configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; - collection - .dropIndex('a_1') - .then(() => { - client.close(done); - }) - .catch(err => { - client.close(); - done(err); - }); - }); - }); - } - }); + it('should correctly apply hint to find', async function () { + await collection.insertMany([{ a: 1 }], this.configuration.writeConcernMax()); - it('should correctly apply hint to find', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, + await collection.createIndex({ a: 1 }, { writeConcern: this.configuration.writeConcernMax() }); + await collection.indexInformation({ full: false }); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('should_correctly_apply_hint'); - collection.insert([{ a: 1 }], configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; + const [doc] = await collection.find({}, { hint: 'a_1' }).toArray(); + expect(doc.a).to.equal(1); + }); - collection.createIndex({ a: 1 }, configuration.writeConcernMax(), function (err) { - expect(err).to.not.exist; + it('should correctly set language_override option', async function () { + await collection.insertMany([{ text: 'Lorem ipsum dolor sit amet.', langua: 'italian' }]); - collection.indexInformation({ full: false }, function (err) { - expect(err).to.not.exist; + await collection.createIndex( + { text: 'text' }, + { language_override: 'langua', name: 'language_override_index' } + ); - collection.find({}, { hint: 'a_1' }).toArray(function (err, docs) { - expect(err).to.not.exist; - test.equal(1, docs[0].a); - client.close(done); - }); - }); - }); - }); + const indexInformation = await collection.indexInformation({ full: true }); + for (let i = 0; i < indexInformation.length; i++) { + if (indexInformation[i].name === 'language_override_index') + test.equal(indexInformation[i].language_override, 'langua'); } }); - it('should correctly set language_override option', { - metadata: { - requires: { - mongodb: '>=2.6.0', - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] - } - }, + it('should correctly use listIndexes to retrieve index list', async function () { + await db.collection('testListIndexes').createIndex({ a: 1 }); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('should_correctly_set_language_override'); - collection.insert( - [{ text: 'Lorem ipsum dolor sit amet.', langua: 'italian' }], - function (err) { - expect(err).to.not.exist; - - collection.createIndex( - { text: 'text' }, - { language_override: 'langua', name: 'language_override_index' }, - function (err) { - expect(err).to.not.exist; - - collection.indexInformation({ full: true }, function (err, indexInformation) { - expect(err).to.not.exist; - for (let i = 0; i < indexInformation.length; i++) { - if (indexInformation[i].name === 'language_override_index') - test.equal(indexInformation[i].language_override, 'langua'); - } - - client.close(done); - }); - } - ); - } - ); - } + const indexes = await db.collection('testListIndexes').listIndexes().toArray(); + expect(indexes).to.have.lengthOf(2); }); - it('should correctly use listIndexes to retrieve index list', { - metadata: { - requires: { mongodb: '>=2.4.0', topology: ['single', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.collection('testListIndexes').createIndex({ a: 1 }, function (err) { - expect(err).to.not.exist; - - // Get the list of indexes - db.collection('testListIndexes') - .listIndexes() - .toArray(function (err, indexes) { - expect(err).to.not.exist; - test.equal(2, indexes.length); + it('should correctly use listIndexes to retrieve index list using hasNext', async function () { + await db.collection('testListIndexes_2').createIndex({ a: 1 }); - client.close(done); - }); - }); - } + const result = await db.collection('testListIndexes_2').listIndexes().hasNext(); + expect(result).to.be.true; }); - it('should correctly use listIndexes to retrieve index list using hasNext', { - metadata: { - requires: { mongodb: '>=2.4.0', topology: ['single', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.collection('testListIndexes_2').createIndex({ a: 1 }, function (err) { - expect(err).to.not.exist; - - // Get the list of indexes - db.collection('testListIndexes_2') - .listIndexes() - .hasNext(function (err, result) { - expect(err).to.not.exist; - test.equal(true, result); + it('should correctly ensureIndex for nested style index name c.d', async function () { + await db.collection('ensureIndexWithNestedStyleIndex').createIndex({ 'c.d': 1 }); - client.close(done); - }); - }); - } + // Get the list of indexes + const indexes = await db.collection('ensureIndexWithNestedStyleIndex').listIndexes().toArray(); + expect(indexes).to.have.lengthOf(2); }); - it('should correctly ensureIndex for nested style index name c.d', { - metadata: { - requires: { mongodb: '>=2.4.0', topology: ['single', 'ssl', 'heap', 'wiredtiger'] } - }, + it('should correctly execute createIndexes with multiple indexes', async function () { + const r = await db + .collection('createIndexes') + .createIndexes([{ key: { a: 1 } }, { key: { b: 1 }, name: 'hello1' }]); + expect(r).to.deep.equal(['a_1', 'hello1']); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.collection('ensureIndexWithNestedStyleIndex').createIndex({ 'c.d': 1 }, function (err) { - expect(err).to.not.exist; - - // Get the list of indexes - db.collection('ensureIndexWithNestedStyleIndex') - .listIndexes() - .toArray(function (err, indexes) { - expect(err).to.not.exist; - test.equal(2, indexes.length); + const docs = await db.collection('createIndexes').listIndexes().toArray(); + const keys = {}; - client.close(done); - }); - }); + for (let i = 0; i < docs.length; i++) { + keys[docs[i].name] = true; } - }); - it('should correctly execute createIndexes with multiple indexes', { - metadata: { requires: { mongodb: '>=2.6.0', topology: ['single'] } }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.collection('createIndexes').createIndexes( - [{ key: { a: 1 } }, { key: { b: 1 }, name: 'hello1' }], - function (err, r) { - expect(err).to.not.exist; - expect(r).to.deep.equal(['a_1', 'hello1']); - - db.collection('createIndexes') - .listIndexes() - .toArray(function (err, docs) { - expect(err).to.not.exist; - const keys = {}; - - for (let i = 0; i < docs.length; i++) { - keys[docs[i].name] = true; - } - - test.ok(keys['a_1']); - test.ok(keys['hello1']); - - client.close(done); - }); - } - ); - } + test.ok(keys['a_1']); + test.ok(keys['hello1']); }); - it('should correctly execute createIndexes with one index', { - metadata: { requires: { mongodb: '>=2.6.0', topology: ['single'] } }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.collection('createIndexes').createIndexes([{ key: { a: 1 } }], function (err, r) { - expect(err).to.not.exist; - expect(r).to.deep.equal(['a_1']); - - db.collection('createIndexes') - .listIndexes() - .toArray(function (err, docs) { - expect(err).to.not.exist; - const keys = {}; - - for (let i = 0; i < docs.length; i++) { - keys[docs[i].name] = true; - } - - test.ok(keys['a_1']); - test.ok(keys['hello1']); + it('should correctly execute createIndexes with one index', async function () { + const r = await db.collection('createIndexes').createIndexes([{ key: { a: 1 } }]); + expect(r).to.deep.equal(['a_1']); - client.close(done); - }); - }); - } + await collection.indexExists('a_1'); }); - it('shouldCorrectlyCreateTextIndex', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.collection('text_index').createIndex( - { '$**': 'text' }, - { name: 'TextIndex' }, - function (err, r) { - expect(err).to.not.exist; - test.equal('TextIndex', r); - // Let's close the db - client.close(done); - } - ); - } + it('shouldCorrectlyCreateTextIndex', async function () { + const r = await collection.createIndex({ '$**': 'text' }, { name: 'TextIndex' }); + expect(r).to.equal('TextIndex'); }); - it('should correctly pass partialIndexes through to createIndexCommand', { - metadata: { - requires: { - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'], - mongodb: '>=3.1.8' - } - }, + it('should correctly pass partialIndexes through to createIndexCommand', async function () { + const configuration = this.configuration; + const started: Array = []; + const succeeded: Array = []; - test: function (done) { - const configuration = this.configuration; - const started = []; - const succeeded = []; - const client = configuration.newClient(configuration.writeConcernMax(), { - maxPoolSize: 1, - monitorCommands: true - }); + client.on('commandStarted', function (event) { + if (event.commandName === 'createIndexes') started.push(event); + }); - client.on('commandStarted', function (event) { - if (event.commandName === 'createIndexes') started.push(event); - }); + client.on('commandSucceeded', function (event) { + if (event.commandName === 'createIndexes') succeeded.push(event); + }); - client.on('commandSucceeded', function (event) { - if (event.commandName === 'createIndexes') succeeded.push(event); - }); + const db = client.db(configuration.db); - const db = client.db(configuration.db); + await db + .collection('partialIndexes') + .createIndex({ a: 1 }, { partialFilterExpression: { a: 1 } }); + expect(started[0].command.indexes[0].partialFilterExpression).to.deep.equal({ a: 1 }); + }); - db.collection('partialIndexes').createIndex( - { a: 1 }, - { partialFilterExpression: { a: 1 } }, - function (err) { - expect(err).to.not.exist; - test.deepEqual({ a: 1 }, started[0].command.indexes[0].partialFilterExpression); - client.close(done); - } - ); - } + it('should not retry partial index expression error', async function () { + // Can't use $exists: false in partial filter expression, see + // https://jira.mongodb.org/browse/SERVER-17853 + const opts = { partialFilterExpression: { a: { $exists: false } } }; + const err = await db + .collection('partialIndexes') + .createIndex({ a: 1 }, opts) + .catch(e => e); + expect(err).to.be.instanceOf(Error).to.have.property('code', 67); }); - it('should not retry partial index expression error', { - metadata: { - requires: { - topology: ['single', 'replicaset', 'sharded'], - mongodb: '>=3.1.8' + it('should correctly create index on embedded key', async function () { + await collection.insertMany([ + { + a: { a: 1 } + }, + { + a: { a: 2 } } - }, + ]); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // Can't use $exists: false in partial filter expression, see - // https://jira.mongodb.org/browse/SERVER-17853 - const opts = { partialFilterExpression: { a: { $exists: false } } }; - db.collection('partialIndexes').createIndex({ a: 1 }, opts, function (err) { - test.ok(err); - test.equal(err.code, 67); - const msg = "key $exists must not start with '$'"; - test.ok(err.toString().indexOf(msg) === -1); - - client.close(done); - }); - } + await collection.createIndex({ 'a.a': 1 }); }); - it('should correctly create index on embedded key', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('embedded_key_indes'); - - collection.insertMany( - [ - { - a: { a: 1 } - }, - { - a: { a: 2 } - } - ], - function (err) { - expect(err).to.not.exist; - - collection.createIndex({ 'a.a': 1 }, function (err) { - expect(err).to.not.exist; - client.close(done); - }); - } - ); - } + it('should correctly create index using . keys', async function () { + await collection.createIndex( + { 'key.external_id': 1, 'key.type': 1 }, + { unique: true, sparse: true, name: 'indexname' } + ); }); - it('should correctly create index using . keys', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('embedded_key_indes_1'); - collection.createIndex( + it('error on duplicate key index', async function () { + await collection.insertMany([ + { + key: { external_id: 1, type: 1 } + }, + { + key: { external_id: 1, type: 1 } + } + ]); + const err = await collection + .createIndex( { 'key.external_id': 1, 'key.type': 1 }, - { unique: true, sparse: true, name: 'indexname' }, - function (err) { - expect(err).to.not.exist; - - client.close(done); - } - ); - } - }); - - it('error on duplicate key index', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, + { unique: true, sparse: true, name: 'indexname' } + ) + .catch(e => e); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('embedded_key_indes_2'); - collection.insertMany( - [ - { - key: { external_id: 1, type: 1 } - }, - { - key: { external_id: 1, type: 1 } - } - ], - function (err) { - expect(err).to.not.exist; - collection.createIndex( - { 'key.external_id': 1, 'key.type': 1 }, - { unique: true, sparse: true, name: 'indexname' }, - function (err) { - test.equal(11000, err.code); - - client.close(done); - } - ); - } - ); - } + expect(err).to.be.instanceOf(Error).to.have.property('code', 11000); }); - it('should correctly create Index with sub element', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // insert a doc - db.collection('messed_up_index').createIndex( - { temporary: 1, 'store.addressLines': 1, lifecycleStatus: 1 }, - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - - client.close(done); - } - ); - } + it('should correctly create Index with sub element', async function () { + await collection.createIndex( + { temporary: 1, 'store.addressLines': 1, lifecycleStatus: 1 }, + this.configuration.writeConcernMax() + ); }); - it('should correctly fail detect error code 85 when performing createIndex', { - metadata: { + it( + 'should correctly fail detect error code 85 when performing createIndex', + { requires: { - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'], - mongodb: '>=3.0.0 <=4.8.0' + mongodb: '<=4.8.0' } }, + async function () { + await collection.createIndex({ 'a.one': 1, 'a.two': 1 }, { name: 'n1', sparse: false }); - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('messed_up_options'); - - collection.createIndex( - { 'a.one': 1, 'a.two': 1 }, - { name: 'n1', sparse: false }, - function (err) { - expect(err).to.not.exist; - - collection.createIndex( - { 'a.one': 1, 'a.two': 1 }, - { name: 'n2', sparse: true }, - function (err) { - test.ok(err); - test.equal(85, err.code); - - client.close(done); - } - ); - } - ); + const err = collection + .createIndex({ 'a.one': 1, 'a.two': 1 }, { name: 'n2', sparse: true }) + .catch(e => e); + expect(err).to.be.instanceOf(Error).to.have.property('code', 85); } - }); - - it('should correctly fail by detecting error code 86 when performing createIndex', { - metadata: { - requires: { - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'], - mongodb: '>=3.0.0' - } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - const collection = db.collection('messed_up_options'); + ); - collection.createIndex({ 'b.one': 1, 'b.two': 1 }, { name: 'test' }, function (err) { - expect(err).to.not.exist; + it('should correctly fail by detecting error code 86 when performing createIndex', async function () { + await collection.createIndex({ 'b.one': 1, 'b.two': 1 }, { name: 'test' }); + const err = await collection + .createIndex({ 'b.one': -1, 'b.two': -1 }, { name: 'test' }) + .catch(err => err); - collection.createIndex({ 'b.one': -1, 'b.two': -1 }, { name: 'test' }, function (err) { - test.ok(err); - test.equal(86, err.code); - - client.close(done); - }); - }); - } + expect(err).to.be.instanceOf(Error).to.have.property('code', 86); }); - it('should correctly create Index with sub element running in background', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] } - }, - - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - // insert a doc - db.collection('messed_up_index_2').createIndex( - { 'accessControl.get': 1 }, - { background: true }, - function (err) { - expect(err).to.not.exist; - - client.close(done); - } - ); - } + it('should correctly create Index with sub element running in background', async function () { + await collection.createIndex({ 'accessControl.get': 1 }, { background: true }); }); context('commitQuorum', function () { @@ -1270,123 +559,103 @@ describe('Indexes', function () { await client.close(); }); - function throwErrorTest(testCommand) { + function throwErrorTest(testCommand: (db: Db, collection: Collection) => Promise) { return { metadata: { requires: { mongodb: '<4.4' } }, - test: function (done) { + test: async function () { const db = client.db('test'); const collection = db.collection('commitQuorum'); - testCommand(db, collection, (err, result) => { - expect(err).to.exist; - expect(err.message).to.equal( - 'Option `commitQuorum` for `createIndexes` not supported on servers < 4.4' - ); - expect(result).to.not.exist; - done(); - }); + const err = await testCommand(db, collection).catch(e => e); + expect(err.message).to.equal( + 'Option `commitQuorum` for `createIndexes` not supported on servers < 4.4' + ); } }; } it( 'should throw an error if commitQuorum specified on db.createIndex', - throwErrorTest((db, collection, cb) => - db.createIndex(collection.collectionName, 'a', { commitQuorum: 'all' }, cb) + throwErrorTest((db, collection) => + db.createIndex(collection.collectionName, 'a', { commitQuorum: 'all' }) ) ); it( 'should throw an error if commitQuorum specified on collection.createIndex', - throwErrorTest((db, collection, cb) => - collection.createIndex('a', { commitQuorum: 'all' }, cb) - ) + throwErrorTest((db, collection) => collection.createIndex('a', { commitQuorum: 'all' })) ); it( 'should throw an error if commitQuorum specified on collection.createIndexes', - throwErrorTest((db, collection, cb) => - collection.createIndexes( - [{ key: { a: 1 } }, { key: { b: 1 } }], - { commitQuorum: 'all' }, - cb - ) + throwErrorTest((db, collection) => + collection.createIndexes([{ key: { a: 1 } }, { key: { b: 1 } }], { commitQuorum: 'all' }) ) ); - function commitQuorumTest(testCommand) { + function commitQuorumTest( + testCommand: (db: Db, collection: Collection) => Promise + ): any { return { metadata: { requires: { mongodb: '>=4.4', topology: ['replicaset', 'sharded'] } }, - test: function (done) { - const events = []; + test: async function () { + const events: CommandStartedEvent[] = []; client.on('commandStarted', event => { if (event.commandName === 'createIndexes') events.push(event); }); const db = client.db('test'); const collection = db.collection('commitQuorum'); - collection.insertOne({ a: 1 }, function (err) { + await collection.insertOne({ a: 1 }); + await testCommand(db, collection); + + expect(events).to.be.an('array').with.lengthOf(1); + expect(events[0]).nested.property('command.commitQuorum').to.equal(0); + await collection.drop(err => { expect(err).to.not.exist; - testCommand(db, collection, err => { - expect(err).to.not.exist; - - expect(events).to.be.an('array').with.lengthOf(1); - expect(events[0]).nested.property('command.commitQuorum').to.equal(0); - collection.drop(err => { - expect(err).to.not.exist; - done(); - }); - }); }); } }; } it( 'should run command with commitQuorum if specified on db.createIndex', - commitQuorumTest((db, collection, cb) => - db.createIndex( - collection.collectionName, - 'a', - { writeConcern: { w: 'majority' }, commitQuorum: 0 }, - cb - ) + commitQuorumTest((db, collection) => + db.createIndex(collection.collectionName, 'a', { + // @ts-expect-error revaluate this? + writeConcern: { w: 'majority' }, + commitQuorum: 0 + }) ) ); it( 'should run command with commitQuorum if specified on collection.createIndex', - commitQuorumTest((db, collection, cb) => - collection.createIndex('a', { writeConcern: { w: 'majority' }, commitQuorum: 0 }, cb) + commitQuorumTest((db, collection) => + // @ts-expect-error revaluate this? + collection.createIndex('a', { writeConcern: { w: 'majority' }, commitQuorum: 0 }) ) ); it( 'should run command with commitQuorum if specified on collection.createIndexes', - commitQuorumTest((db, collection, cb) => - collection.createIndexes( - [{ key: { a: 1 } }], - { writeConcern: { w: 'majority' }, commitQuorum: 0 }, - cb - ) + commitQuorumTest((db, collection) => + collection.createIndexes([{ key: { a: 1 } }], { + // @ts-expect-error revaluate this? + writeConcern: { w: 'majority' }, + commitQuorum: 0 + }) ) ); }); - it('should create index hidden', { - metadata: { requires: { mongodb: '>=4.4', topology: 'single' } }, - test: function (done) { - const configuration = this.configuration; - const client = configuration.newClient(configuration.writeConcernMax(), { maxPoolSize: 1 }); - const db = client.db(configuration.db); - db.createCollection('hidden_index_collection', (err, collection) => { - expect(err).to.not.exist; - collection.createIndex('a', { hidden: true }, (err, index) => { - expect(err).to.not.exist; - expect(index).to.equal('a_1'); - collection.listIndexes().toArray((err, indexes) => { - expect(err).to.not.exist; - expect(indexes).to.deep.equal([ - { v: 2, key: { _id: 1 }, name: '_id_' }, - { v: 2, key: { a: 1 }, name: 'a_1', hidden: true } - ]); - client.close(done); - }); - }); - }); - } - }); + it( + 'should create index hidden', + { + requires: { mongodb: '>=4.4', topology: 'single' } + }, + async function () { + const collection = await db.createCollection('hidden_index_collection'); + const indexName = await collection.createIndex('a', { hidden: true }); + expect(indexName).to.equal('a_1'); + const indexes = await collection.listIndexes().toArray(); + expect(indexes).to.deep.equal([ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { v: 2, key: { a: 1 }, name: 'a_1', hidden: true } + ]); + } + ); }); From 4fe4a2d896200c100276855c73033642a1789905 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 15 Mar 2024 11:19:14 -0600 Subject: [PATCH 04/17] add tests that enforce default behavior of index listing functions --- test/integration/index_management.test.ts | 122 +++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/test/integration/index_management.test.ts b/test/integration/index_management.test.ts index 502b6af1e2f..336764df216 100644 --- a/test/integration/index_management.test.ts +++ b/test/integration/index_management.test.ts @@ -70,7 +70,7 @@ describe('Indexes', function () { }); it('shouldCorrectlyHandleMultipleColumnIndexes', async function () { - await collection.insert({ a: 1 }); + await collection.insertOne({ a: 1 }); const indexName = await db.createIndex( collection.collectionName, @@ -99,6 +99,124 @@ describe('Indexes', function () { ); }); + describe('Collection.indexes()', function () { + beforeEach(() => collection.createIndex({ age: 1 })); + afterEach(() => collection.dropIndexes()); + + context('when `full` is not provided', () => { + it('returns an array of indexes', async function () { + const indexes = await collection.indexes(); + expect(indexes).to.be.a('array'); + }); + }); + + context('when `full` is set to `true`', () => { + it('returns an array of indexes', async function () { + const indexes = await collection.indexes({ full: true }); + expect(indexes).to.be.a('array'); + }); + }); + + context('when `full` is set to `false`', () => { + it('returns a document mapping key to index definition', async function () { + const indexes = await collection.indexes({ full: false }); + expect(indexes).to.be.a('object'); + expect(indexes) + .to.have.property('age_1') + .to.deep.equal([['age', 1]]); + }); + }); + }); + + describe('Collection.indexInformation()', function () { + beforeEach(() => collection.createIndex({ age: 1 })); + afterEach(() => collection.dropIndexes()); + + context('when `full` is not provided', () => { + it('defaults to `false` and returns a document', async function () { + const indexes = await collection.indexInformation(); + expect(indexes).to.be.a('object'); + expect(indexes) + .to.have.property('age_1') + .to.deep.equal([['age', 1]]); + }); + }); + + context('when `full` is set to `true`', () => { + it('returns an array of indexes', async function () { + const indexes = await collection.indexInformation({ full: true }); + expect(indexes).to.be.a('array'); + }); + }); + + context('when `full` is set to `false`', () => { + it('returns a document mapping key to index definition', async function () { + const indexes = await collection.indexInformation({ full: false }); + expect(indexes).to.be.a('object'); + expect(indexes) + .to.have.property('age_1') + .to.deep.equal([['age', 1]]); + }); + }); + }); + + describe('Collection.indexExists()', function () { + beforeEach(() => collection.createIndex({ age: 1 })); + afterEach(() => collection.dropIndexes()); + + context('when provided a string index name', () => { + it('returns true when the index exists', async () => { + expect(await collection.indexExists('age_1')).to.be.true; + }); + + it('returns false when the index does not exist', async () => { + expect(await collection.indexExists('name_1')).to.be.false; + }); + }); + + context('when provided an array of index names', () => { + it('returns true when all indexes exists', async () => { + expect(await collection.indexExists(['age_1'])).to.be.true; + }); + + it('returns false when the none of the indexes exist', async () => { + expect(await collection.indexExists(['name_1'])).to.be.false; + }); + + it('returns false when only some of hte indexes exist', async () => { + expect(await collection.indexExists(['name_1', 'age_1'])).to.be.false; + }); + }); + context('when `full` is true', () => { + // These tests are broken! + it('returns true when all indexes exists', async () => { + expect(await collection.indexExists(['age_1'], { full: true })).to.be.true; + }); + + it('returns false when the none of the indexes exist', async () => { + expect(await collection.indexExists(['name_1'], { full: true })).to.be.false; + }); + + it('returns false when only some of hte indexes exist', async () => { + expect(await collection.indexExists(['name_1', 'age_1'], { full: true })).to.be.false; + }); + }); + + context('when `full` is false', () => { + it('returns true when all indexes exists', async () => { + expect(await collection.indexExists(['age_1'], { full: false })).to.be.true; + }); + + it('returns false when the none of the indexes exist', async () => { + expect(await collection.indexExists(['name_1'], { full: false })).to.be.false; + }); + + it('returns false when only some of hte indexes exist', async () => { + expect(await collection.indexExists(['name_1', 'age_1'], { full: false })).to.be.false; + }); + }); + }); + it('shouldCorrectlyHandleUniqueIndex', async function () { await db.createCollection('test_unique_index'); await db.createIndex(collection.collectionName, 'hello', this.configuration.writeConcernMax()); @@ -529,7 +647,7 @@ describe('Indexes', function () { async function () { await collection.createIndex({ 'a.one': 1, 'a.two': 1 }, { name: 'n1', sparse: false }); - const err = collection + const err = await collection .createIndex({ 'a.one': 1, 'a.two': 1 }, { name: 'n2', sparse: true }) .catch(e => e); expect(err).to.be.instanceOf(Error).to.have.property('code', 85); From 90c5645a8724ddb1f0aeab4dc3190085df6b1efe Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 12 Mar 2024 14:35:08 -0600 Subject: [PATCH 05/17] remove `EnsureIndexOperation` and `CreateIndexOperation` --- src/collection.ts | 9 +- src/db.ts | 6 +- src/operations/create_collection.ts | 11 ++- src/operations/indexes.ts | 89 ++++++------------- .../crud/abstract_operation.test.ts | 15 +--- test/unit/operations/indexes.test.ts | 11 ++- 6 files changed, 52 insertions(+), 89 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index a735a3a0828..fe4de069ace 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -52,7 +52,6 @@ import { import { CreateIndexesOperation, type CreateIndexesOptions, - CreateIndexOperation, type DropIndexesOptions, DropIndexOperation, type IndexDescription, @@ -575,15 +574,17 @@ export class Collection { indexSpec: IndexSpecification, options?: CreateIndexesOptions ): Promise { - return executeOperation( + const indexes = await executeOperation( this.client, - new CreateIndexOperation( + CreateIndexesOperation.fromIndexSpecification( this as TODO_NODE_3286, this.collectionName, indexSpec, resolveOptions(this, options) ) ); + + return indexes[0]; } /** @@ -623,7 +624,7 @@ export class Collection { ): Promise { return executeOperation( this.client, - new CreateIndexesOperation( + CreateIndexesOperation.fromIndexDescriptionArray( this as TODO_NODE_3286, this.collectionName, indexSpecs, diff --git a/src/db.ts b/src/db.ts index c12c22c7243..9b5c4b582cf 100644 --- a/src/db.ts +++ b/src/db.ts @@ -25,7 +25,6 @@ import { import { executeOperation } from './operations/execute_operation'; import { type CreateIndexesOptions, - CreateIndexOperation, IndexInformationOperation, type IndexSpecification } from './operations/indexes'; @@ -426,10 +425,7 @@ export class Db { indexSpec: IndexSpecification, options?: CreateIndexesOptions ): Promise { - return executeOperation( - this.client, - new CreateIndexOperation(this, name, indexSpec, resolveOptions(this, options)) - ); + return this.collection(name).createIndex(indexSpec, options); } /** diff --git a/src/operations/create_collection.ts b/src/operations/create_collection.ts index 9732e879b12..f6077f108f4 100644 --- a/src/operations/create_collection.ts +++ b/src/operations/create_collection.ts @@ -10,7 +10,7 @@ import type { PkFactory } from '../mongo_client'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { CommandOperation, type CommandOperationOptions } from './command'; -import { CreateIndexOperation } from './indexes'; +import { CreateIndexesOperation } from './indexes'; import { Aspect, defineAspects } from './operation'; const ILLEGAL_COMMAND_FIELDS = new Set([ @@ -167,7 +167,14 @@ export class CreateCollectionOperation extends CommandOperation { if (encryptedFields) { // Create the required index for queryable encryption support. - const createIndexOp = new CreateIndexOperation(db, name, { __safeContent__: 1 }, {}); + // We could use `this.db.collection(name).createIndex()` to create the index, + // but we can use the same session & server to avoid an extra server selection. + const createIndexOp = CreateIndexesOperation.fromIndexSpecification( + db, + name, + { __safeContent__: 1 }, + {} + ); await createIndexOp.execute(server, session); } diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index cbeb82ff818..b966cd35b64 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -204,14 +204,12 @@ export class IndexesOperation extends AbstractOperation { } /** @internal */ -export class CreateIndexesOperation< - T extends string | string[] = string[] -> extends CommandOperation { +export class CreateIndexesOperation extends CommandOperation { override options: CreateIndexesOptions; collectionName: string; indexes: ReadonlyArray & { key: Map }>; - constructor( + private constructor( parent: OperationParent, collectionName: string, indexes: IndexDescription[], @@ -239,11 +237,34 @@ export class CreateIndexesOperation< }); } + static fromIndexDescriptionArray( + parent: OperationParent, + collectionName: string, + indexes: IndexDescription[], + options?: CreateIndexesOptions + ): CreateIndexesOperation { + return new CreateIndexesOperation(parent, collectionName, indexes, options); + } + + static fromIndexSpecification( + parent: OperationParent, + collectionName: string, + indexSpec: IndexSpecification, + options?: CreateIndexesOptions + ): CreateIndexesOperation { + return new CreateIndexesOperation( + parent, + collectionName, + [makeIndexSpec(indexSpec, options)], + options + ); + } + override get commandName() { return 'createIndexes'; } - override async execute(server: Server, session: ClientSession | undefined): Promise { + override async execute(server: Server, session: ClientSession | undefined): Promise { const options = this.options; const indexes = this.indexes; @@ -266,61 +287,7 @@ export class CreateIndexesOperation< await super.executeCommand(server, session, cmd); const indexNames = indexes.map(index => index.name || ''); - return indexNames as T; - } -} - -/** @internal */ -export class CreateIndexOperation extends CreateIndexesOperation { - constructor( - parent: OperationParent, - collectionName: string, - indexSpec: IndexSpecification, - options?: CreateIndexesOptions - ) { - super(parent, collectionName, [makeIndexSpec(indexSpec, options)], options); - } - - override async execute(server: Server, session: ClientSession | undefined): Promise { - const indexNames = await super.execute(server, session); - return indexNames[0]; - } -} - -/** @internal */ -export class EnsureIndexOperation extends CreateIndexOperation { - db: Db; - - constructor( - db: Db, - collectionName: string, - indexSpec: IndexSpecification, - options?: CreateIndexesOptions - ) { - super(db, collectionName, indexSpec, options); - - this.readPreference = ReadPreference.primary; - this.db = db; - this.collectionName = collectionName; - } - - override get commandName() { - return 'listIndexes'; - } - - override async execute(server: Server, session: ClientSession | undefined): Promise { - const indexName = this.indexes[0].name; - const indexes = await this.db - .collection(this.collectionName) - .listIndexes({ session }) - .toArray() - .catch(error => { - if (error instanceof MongoError && error.code === MONGODB_ERROR_CODES.NamespaceNotFound) - return []; - throw error; - }); - if (indexName && indexes.some(index => index.name === indexName)) return indexName; - return super.execute(server, session); + return indexNames; } } @@ -470,6 +437,4 @@ defineAspects(ListIndexesOperation, [ Aspect.CURSOR_CREATING ]); defineAspects(CreateIndexesOperation, [Aspect.WRITE_OPERATION]); -defineAspects(CreateIndexOperation, [Aspect.WRITE_OPERATION]); -defineAspects(EnsureIndexOperation, [Aspect.WRITE_OPERATION]); defineAspects(DropIndexOperation, [Aspect.WRITE_OPERATION]); diff --git a/test/integration/crud/abstract_operation.test.ts b/test/integration/crud/abstract_operation.test.ts index 8134a7d437f..62daed09abe 100644 --- a/test/integration/crud/abstract_operation.test.ts +++ b/test/integration/crud/abstract_operation.test.ts @@ -146,23 +146,12 @@ describe('abstract operation', async function () { subclassType: mongodb.IndexesOperation, correctCommandName: 'listIndexes' }, - { - subclassCreator: () => new mongodb.CreateIndexesOperation(db, 'bar', [{ key: { a: 1 } }]), - subclassType: mongodb.CreateIndexesOperation, - correctCommandName: 'createIndexes' - }, { subclassCreator: () => - new mongodb.CreateIndexOperation(db, 'collectionName', 'indexDescription'), - subclassType: mongodb.CreateIndexOperation, + mongodb.CreateIndexesOperation.fromIndexDescriptionArray(db, 'bar', [{ key: { a: 1 } }]), + subclassType: mongodb.CreateIndexesOperation, correctCommandName: 'createIndexes' }, - { - subclassCreator: () => - new mongodb.EnsureIndexOperation(db, 'collectionName', 'indexDescription'), - subclassType: mongodb.EnsureIndexOperation, - correctCommandName: 'listIndexes' - }, { subclassCreator: () => new mongodb.DropIndexOperation(collection, 'a', {}), subclassType: mongodb.DropIndexOperation, diff --git a/test/unit/operations/indexes.test.ts b/test/unit/operations/indexes.test.ts index deded3793b3..8083cb5a6c5 100644 --- a/test/unit/operations/indexes.test.ts +++ b/test/unit/operations/indexes.test.ts @@ -1,13 +1,13 @@ import { expect } from 'chai'; import { + CreateIndexesOperation, type CreateIndexesOptions, - CreateIndexOperation, type IndexDirection, ns } from '../../mongodb'; -describe('class CreateIndexOperation', () => { +describe('class CreateIndexesOperation', () => { const testCases = [ { description: 'single string', @@ -101,7 +101,12 @@ describe('class CreateIndexOperation', () => { ]; const makeIndexOperation = (input, options: CreateIndexesOptions = {}) => - new CreateIndexOperation({ s: { namespace: ns('a.b') } }, 'b', input, options); + CreateIndexesOperation.fromIndexSpecification( + { s: { namespace: ns('a.b') } }, + 'b', + input, + options + ); describe('#constructor()', () => { for (const { description, input, mapData, name } of testCases) { From 1ef142f83052eb47b9142b9f624f8ceeeb9ce66a Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 12 Mar 2024 14:37:15 -0600 Subject: [PATCH 06/17] remove 'IndexesOperation' --- src/collection.ts | 6 +--- src/operations/indexes.ts | 28 ------------------- .../crud/abstract_operation.test.ts | 5 ---- 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index fe4de069ace..1d6d776b2b6 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -55,7 +55,6 @@ import { type DropIndexesOptions, DropIndexOperation, type IndexDescription, - IndexesOperation, IndexExistsOperation, IndexInformationOperation, type IndexSpecification, @@ -805,10 +804,7 @@ export class Collection { * @param options - Optional settings for the command */ async indexes(options?: IndexInformationOptions): Promise { - return executeOperation( - this.client, - new IndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)) - ); + return this.listIndexes(options).toArray(); } /** diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index b966cd35b64..2de7bb15079 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -175,34 +175,6 @@ function makeIndexSpec( return { ...options, key }; } -/** @internal */ -export class IndexesOperation extends AbstractOperation { - override options: IndexInformationOptions; - collection: Collection; - - constructor(collection: Collection, options: IndexInformationOptions) { - super(options); - this.options = options; - this.collection = collection; - } - - override get commandName() { - return 'listIndexes' as const; - } - - override async execute(_server: Server, session: ClientSession | undefined): Promise { - const coll = this.collection; - const options = this.options; - - return indexInformation(coll.s.db, coll.collectionName, { - full: true, - ...options, - readPreference: this.readPreference, - session - }); - } -} - /** @internal */ export class CreateIndexesOperation extends CommandOperation { override options: CreateIndexesOptions; diff --git a/test/integration/crud/abstract_operation.test.ts b/test/integration/crud/abstract_operation.test.ts index 62daed09abe..639a8e934eb 100644 --- a/test/integration/crud/abstract_operation.test.ts +++ b/test/integration/crud/abstract_operation.test.ts @@ -141,11 +141,6 @@ describe('abstract operation', async function () { subclassType: mongodb.GetMoreOperation, correctCommandName: 'getMore' }, - { - subclassCreator: () => new mongodb.IndexesOperation(collection, {}), - subclassType: mongodb.IndexesOperation, - correctCommandName: 'listIndexes' - }, { subclassCreator: () => mongodb.CreateIndexesOperation.fromIndexDescriptionArray(db, 'bar', [{ key: { a: 1 } }]), From 0b4a7088924dd818a2061adb9bfc0eaae7b060b0 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 12 Mar 2024 14:49:27 -0600 Subject: [PATCH 07/17] remove 'IndexExistsOperation' --- src/collection.ts | 14 ++++--- src/operations/indexes.ts | 37 ------------------- .../crud/abstract_operation.test.ts | 6 --- 3 files changed, 8 insertions(+), 49 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 1d6d776b2b6..e50ec0e742b 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -55,7 +55,6 @@ import { type DropIndexesOptions, DropIndexOperation, type IndexDescription, - IndexExistsOperation, IndexInformationOperation, type IndexSpecification, type ListIndexesOptions @@ -91,7 +90,8 @@ import { DEFAULT_PK_FACTORY, MongoDBCollectionNamespace, normalizeHintField, - resolveOptions + resolveOptions, + setDifference } from './utils'; import { WriteConcern, type WriteConcernOptions } from './write_concern'; @@ -684,10 +684,12 @@ export class Collection { indexes: string | string[], options?: IndexInformationOptions ): Promise { - return executeOperation( - this.client, - new IndexExistsOperation(this as TODO_NODE_3286, indexes, resolveOptions(this, options)) - ); + const indexNames: Set = new Set([indexes].flat()); + const allIndexes: string[] = await this.listIndexes(options) + .map(({ name }) => name) + .toArray(); + + return setDifference(indexNames, allIndexes).size === 0; } /** diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 2de7bb15079..c6422dbb539 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -337,43 +337,6 @@ export class ListIndexesOperation extends CommandOperation { } } -/** @internal */ -export class IndexExistsOperation extends AbstractOperation { - override options: IndexInformationOptions; - collection: Collection; - indexes: string | string[]; - - constructor( - collection: Collection, - indexes: string | string[], - options: IndexInformationOptions - ) { - super(options); - this.options = options; - this.collection = collection; - this.indexes = indexes; - } - - override get commandName() { - return 'listIndexes' as const; - } - - override async execute(server: Server, session: ClientSession | undefined): Promise { - const coll = this.collection; - const indexes = this.indexes; - - const info = await indexInformation(coll.s.db, coll.collectionName, { - ...this.options, - readPreference: this.readPreference, - session - }); - // Let's check for the index names - if (!Array.isArray(indexes)) return info[indexes] != null; - // All keys found return true - return indexes.every(indexName => info[indexName] != null); - } -} - /** @internal */ export class IndexInformationOperation extends AbstractOperation { override options: IndexInformationOptions; diff --git a/test/integration/crud/abstract_operation.test.ts b/test/integration/crud/abstract_operation.test.ts index 639a8e934eb..5c3492043f1 100644 --- a/test/integration/crud/abstract_operation.test.ts +++ b/test/integration/crud/abstract_operation.test.ts @@ -19,7 +19,6 @@ describe('abstract operation', async function () { 'OptionsOperation', 'IsCappedOperation', 'BulkWriteOperation', - 'IndexExistsOperation', 'IndexOperation', 'CollectionsOperation', 'IndexInformationOperation' @@ -157,11 +156,6 @@ describe('abstract operation', async function () { subclassType: mongodb.ListIndexesOperation, correctCommandName: 'listIndexes' }, - { - subclassCreator: () => new mongodb.IndexExistsOperation(collection, 'a', {}), - subclassType: mongodb.IndexExistsOperation, - correctCommandName: 'listIndexes' - }, { subclassCreator: () => new mongodb.IndexInformationOperation(db, 'a', {}), subclassType: mongodb.IndexInformationOperation, From 61680c43c29e9557848e35bd9d736085d5834d11 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 12 Mar 2024 15:01:10 -0600 Subject: [PATCH 08/17] remove 'IndexInformationOperation' --- src/collection.ts | 27 ++++++++++---- src/db.ts | 11 ++---- src/operations/indexes.ts | 36 ++----------------- .../crud/abstract_operation.test.ts | 8 +---- 4 files changed, 25 insertions(+), 57 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index e50ec0e742b..505900336f9 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -55,7 +55,6 @@ import { type DropIndexesOptions, DropIndexOperation, type IndexDescription, - IndexInformationOperation, type IndexSpecification, type ListIndexesOptions } from './operations/indexes'; @@ -684,7 +683,7 @@ export class Collection { indexes: string | string[], options?: IndexInformationOptions ): Promise { - const indexNames: Set = new Set([indexes].flat()); + const indexNames: string[] = [indexes].flat(); const allIndexes: string[] = await this.listIndexes(options) .map(({ name }) => name) .toArray(); @@ -698,10 +697,12 @@ export class Collection { * @param options - Optional settings for the command */ async indexInformation(options?: IndexInformationOptions): Promise { - return executeOperation( - this.client, - new IndexInformationOperation(this.s.db, this.collectionName, resolveOptions(this, options)) - ); + const resolvedOptions: IndexInformationOptions = { + readPreference: options?.readPreference, + session: options?.session, + full: false + }; + return this.indexes(resolvedOptions); } /** @@ -806,7 +807,19 @@ export class Collection { * @param options - Optional settings for the command */ async indexes(options?: IndexInformationOptions): Promise { - return this.listIndexes(options).toArray(); + const indexes = await this.listIndexes(options).toArray(); + const full = options?.full === true; + if (full) { + return indexes; + } + + const info: Record> = {}; + for (const { name, key } of indexes) { + info[name] = Object.entries(key); + } + + // @ts-expect-error The return type is broken + return info; } /** diff --git a/src/db.ts b/src/db.ts index 9b5c4b582cf..e93d0e52a86 100644 --- a/src/db.ts +++ b/src/db.ts @@ -23,11 +23,7 @@ import { type DropDatabaseOptions } from './operations/drop'; import { executeOperation } from './operations/execute_operation'; -import { - type CreateIndexesOptions, - IndexInformationOperation, - type IndexSpecification -} from './operations/indexes'; +import { type CreateIndexesOptions, type IndexSpecification } from './operations/indexes'; import type { CollectionInfo, ListCollectionsOptions } from './operations/list_collections'; import { ProfilingLevelOperation, type ProfilingLevelOptions } from './operations/profiling_level'; import { RemoveUserOperation, type RemoveUserOptions } from './operations/remove_user'; @@ -476,10 +472,7 @@ export class Db { * @param options - Optional settings for the command */ async indexInformation(name: string, options?: IndexInformationOptions): Promise { - return executeOperation( - this.client, - new IndexInformationOperation(this, name, resolveOptions(this, options)) - ); + return this.collection(name).indexInformation(resolveOptions(this, options)); } /** diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index c6422dbb539..11a84102c0c 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -1,9 +1,7 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; -import type { Db } from '../db'; -import { MongoCompatibilityError, MONGODB_ERROR_CODES, MongoError } from '../error'; +import { MongoCompatibilityError } from '../error'; import { type OneOrMore } from '../mongo_types'; -import { ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { isObject, maxWireVersion, type MongoDBNamespace } from '../utils'; @@ -13,8 +11,7 @@ import { type CommandOperationOptions, type OperationParent } from './command'; -import { indexInformation, type IndexInformationOptions } from './common_functions'; -import { AbstractOperation, Aspect, defineAspects } from './operation'; +import { Aspect, defineAspects } from './operation'; const VALID_INDEX_OPTIONS = new Set([ 'background', @@ -337,35 +334,6 @@ export class ListIndexesOperation extends CommandOperation { } } -/** @internal */ -export class IndexInformationOperation extends AbstractOperation { - override options: IndexInformationOptions; - db: Db; - name: string; - - constructor(db: Db, name: string, options?: IndexInformationOptions) { - super(options); - this.options = options ?? {}; - this.db = db; - this.name = name; - } - - override get commandName() { - return 'listIndexes' as const; - } - - override async execute(server: Server, session: ClientSession | undefined): Promise { - const db = this.db; - const name = this.name; - - return indexInformation(db, name, { - ...this.options, - readPreference: this.readPreference, - session - }); - } -} - defineAspects(ListIndexesOperation, [ Aspect.READ_OPERATION, Aspect.RETRYABLE, diff --git a/test/integration/crud/abstract_operation.test.ts b/test/integration/crud/abstract_operation.test.ts index 5c3492043f1..7f408ce4978 100644 --- a/test/integration/crud/abstract_operation.test.ts +++ b/test/integration/crud/abstract_operation.test.ts @@ -20,8 +20,7 @@ describe('abstract operation', async function () { 'IsCappedOperation', 'BulkWriteOperation', 'IndexOperation', - 'CollectionsOperation', - 'IndexInformationOperation' + 'CollectionsOperation' ]; const sameServerOnlyOperationSubclasses = ['GetMoreOperation', 'KillCursorsOperation']; @@ -156,11 +155,6 @@ describe('abstract operation', async function () { subclassType: mongodb.ListIndexesOperation, correctCommandName: 'listIndexes' }, - { - subclassCreator: () => new mongodb.IndexInformationOperation(db, 'a', {}), - subclassType: mongodb.IndexInformationOperation, - correctCommandName: 'listIndexes' - }, { subclassCreator: () => new mongodb.InsertOperation(collection.fullNamespace, [{ a: 1 }], {}), From 28fcc319bb864cf2535c93c911c8f7a11c4060bf Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 12 Mar 2024 15:24:19 -0600 Subject: [PATCH 09/17] remove index information utility --- src/collection.ts | 17 ++++++------ src/db.ts | 7 +++-- src/index.ts | 2 +- src/operations/common_functions.ts | 42 ------------------------------ src/operations/indexes.ts | 8 ++++++ 5 files changed, 22 insertions(+), 54 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 505900336f9..d5ddabf7e4a 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -24,7 +24,6 @@ import type { } from './mongo_types'; import type { AggregateOptions } from './operations/aggregate'; import { BulkWriteOperation } from './operations/bulk_write'; -import type { IndexInformationOptions } from './operations/common_functions'; import { CountOperation, type CountOptions } from './operations/count'; import { CountDocumentsOperation, type CountDocumentsOptions } from './operations/count_documents'; import { @@ -49,15 +48,15 @@ import { FindOneAndUpdateOperation, type FindOneAndUpdateOptions } from './operations/find_and_modify'; -import { - CreateIndexesOperation, - type CreateIndexesOptions, - type DropIndexesOptions, - DropIndexOperation, - type IndexDescription, - type IndexSpecification, - type ListIndexesOptions +import type { + CreateIndexesOptions, + DropIndexesOptions, + IndexDescription, + IndexInformationOptions, + IndexSpecification, + ListIndexesOptions } from './operations/indexes'; +import { CreateIndexesOperation, DropIndexOperation } from './operations/indexes'; import { InsertManyOperation, type InsertManyResult, diff --git a/src/db.ts b/src/db.ts index e93d0e52a86..4dd87095fa0 100644 --- a/src/db.ts +++ b/src/db.ts @@ -11,7 +11,6 @@ import type { MongoClient, PkFactory } from './mongo_client'; import type { TODO_NODE_3286 } from './mongo_types'; import type { AggregateOptions } from './operations/aggregate'; import { CollectionsOperation } from './operations/collections'; -import type { IndexInformationOptions } from './operations/common_functions'; import { CreateCollectionOperation, type CreateCollectionOptions @@ -23,7 +22,11 @@ import { type DropDatabaseOptions } from './operations/drop'; import { executeOperation } from './operations/execute_operation'; -import { type CreateIndexesOptions, type IndexSpecification } from './operations/indexes'; +import type { + CreateIndexesOptions, + IndexInformationOptions, + IndexSpecification +} from './operations/indexes'; import type { CollectionInfo, ListCollectionsOptions } from './operations/list_collections'; import { ProfilingLevelOperation, type ProfilingLevelOptions } from './operations/profiling_level'; import { RemoveUserOperation, type RemoveUserOptions } from './operations/remove_user'; diff --git a/src/index.ts b/src/index.ts index fa88e4638b3..9cd58ec0ac0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -447,7 +447,6 @@ export type { CommandOperationOptions, OperationParent } from './operations/command'; -export type { IndexInformationOptions } from './operations/common_functions'; export type { CountOptions } from './operations/count'; export type { CountDocumentsOptions } from './operations/count_documents'; export type { @@ -466,6 +465,7 @@ export type { FindOneAndReplaceOptions, FindOneAndUpdateOptions } from './operations/find_and_modify'; +export type { IndexInformationOptions } from './operations/indexes'; export type { CreateIndexesOptions, DropIndexesOptions, diff --git a/src/operations/common_functions.ts b/src/operations/common_functions.ts index 785ecbaf12d..bc1558e895a 100644 --- a/src/operations/common_functions.ts +++ b/src/operations/common_functions.ts @@ -1,47 +1,5 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; -import type { Db } from '../db'; -import type { ReadPreference } from '../read_preference'; -import type { ClientSession } from '../sessions'; - -/** @public */ -export interface IndexInformationOptions { - full?: boolean; - readPreference?: ReadPreference; - session?: ClientSession; -} -/** - * Retrieves this collections index info. - * - * @param db - The Db instance on which to retrieve the index info. - * @param name - The name of the collection. - */ -export async function indexInformation(db: Db, name: string): Promise; -export async function indexInformation( - db: Db, - name: string, - options?: IndexInformationOptions -): Promise; -export async function indexInformation( - db: Db, - name: string, - options?: IndexInformationOptions -): Promise { - if (options == null) { - options = {}; - } - // If we specified full information - const full = options.full == null ? false : options.full; - // Get the list of indexes of the specified collection - const indexes = await db.collection(name).listIndexes(options).toArray(); - if (full) return indexes; - - const info: Record> = {}; - for (const index of indexes) { - info[index.name] = Object.entries(index.key); - } - return info; -} export function maybeAddIdToDocuments( coll: Collection, diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 11a84102c0c..5d20aeca6ba 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -2,6 +2,7 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; import { MongoCompatibilityError } from '../error'; import { type OneOrMore } from '../mongo_types'; +import { type ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { isObject, maxWireVersion, type MongoDBNamespace } from '../utils'; @@ -70,6 +71,13 @@ export type IndexSpecification = OneOrMore< | Map >; +/** @public */ +export interface IndexInformationOptions { + full?: boolean; + readPreference?: ReadPreference; + session?: ClientSession; +} + /** @public */ export interface IndexDescription extends Pick< From 8d1f965e2e9ede50b93cbf4521eb5ae0c03563b9 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 12 Mar 2024 15:38:59 -0600 Subject: [PATCH 10/17] misc correctness fixes --- src/collection.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index d5ddabf7e4a..3431a9c08c7 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -683,11 +683,13 @@ export class Collection { options?: IndexInformationOptions ): Promise { const indexNames: string[] = [indexes].flat(); - const allIndexes: string[] = await this.listIndexes(options) - .map(({ name }) => name) - .toArray(); + const allIndexes: Set = new Set( + await this.listIndexes(options) + .map(({ name }) => name) + .toArray() + ); - return setDifference(indexNames, allIndexes).size === 0; + return indexNames.every(name => allIndexes.has(name)); } /** @@ -696,12 +698,7 @@ export class Collection { * @param options - Optional settings for the command */ async indexInformation(options?: IndexInformationOptions): Promise { - const resolvedOptions: IndexInformationOptions = { - readPreference: options?.readPreference, - session: options?.session, - full: false - }; - return this.indexes(resolvedOptions); + return this.indexes({ ...options, full: options?.full ?? false }); } /** @@ -807,7 +804,7 @@ export class Collection { */ async indexes(options?: IndexInformationOptions): Promise { const indexes = await this.listIndexes(options).toArray(); - const full = options?.full === true; + const full = options?.full ?? true === true; if (full) { return indexes; } From 4ec299b1e967b083a52d96fed2fdeb902e399417 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 15 Mar 2024 11:30:00 -0600 Subject: [PATCH 11/17] misc fixes --- src/collection.ts | 12 +++++------- test/integration/index_management.test.ts | 22 +++++++--------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 3431a9c08c7..8c25c818578 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -683,13 +683,11 @@ export class Collection { options?: IndexInformationOptions ): Promise { const indexNames: string[] = [indexes].flat(); - const allIndexes: Set = new Set( - await this.listIndexes(options) - .map(({ name }) => name) - .toArray() - ); + const allIndexes: string[] = await this.listIndexes(options) + .map(({ name }) => name) + .toArray(); - return indexNames.every(name => allIndexes.has(name)); + return indexNames.every(name => allIndexes.includes(name)); } /** @@ -804,7 +802,7 @@ export class Collection { */ async indexes(options?: IndexInformationOptions): Promise { const indexes = await this.listIndexes(options).toArray(); - const full = options?.full ?? true === true; + const full = options?.full ?? true; if (full) { return indexes; } diff --git a/test/integration/index_management.test.ts b/test/integration/index_management.test.ts index 336764df216..f05c038b569 100644 --- a/test/integration/index_management.test.ts +++ b/test/integration/index_management.test.ts @@ -286,14 +286,10 @@ describe('Indexes', function () { }); context('when dropIndexes fails', function () { - let collection; - beforeEach(async function () { - collection = await db.createCollection('test_drop_indexes'); - await collection.insert({ a: 1 }); + await collection.insertOne({ a: 1 }); // Create an index on the collection - await db.createIndex(collection.collectionName, 'a'); - /**@type {import('../tools/utils').FailPoint} */ + await collection.createIndex('a'); await client .db() .admin() @@ -309,22 +305,18 @@ describe('Indexes', function () { }); }); - afterEach(async function () { - await db.dropCollection('test_drop_indexes'); - }); - - it('should return false', { - metadata: { + it( + 'should return false', + { requires: { mongodb: '>4.0' } }, - - test: async function () { + async function () { const result = await collection.dropIndexes(); expect(result).to.equal(false); } - }); + ); }); context('indexExists', function () { From 8297f087678f438ac11dd393cf357d0bfece5a36 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 15 Mar 2024 12:25:53 -0600 Subject: [PATCH 12/17] fix lint --- src/collection.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 8c25c818578..8146f8c9580 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -88,8 +88,7 @@ import { DEFAULT_PK_FACTORY, MongoDBCollectionNamespace, normalizeHintField, - resolveOptions, - setDifference + resolveOptions } from './utils'; import { WriteConcern, type WriteConcernOptions } from './write_concern'; From d3af9b1264944a424debb710681231634e1b64a4 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 15 Mar 2024 12:51:22 -0600 Subject: [PATCH 13/17] add Node ticket with todo --- src/collection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collection.ts b/src/collection.ts index 8146f8c9580..187a376d929 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -811,7 +811,7 @@ export class Collection { info[name] = Object.entries(key); } - // @ts-expect-error The return type is broken + // @ts-expect-error TODO(NODE-6029): fix return type of `indexes()` and `indexInformation()` return info; } From 61ffd4e35e893e932ba41b4fe020a309945282cc Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 15 Mar 2024 14:36:49 -0600 Subject: [PATCH 14/17] comments part 1 --- src/bulk/common.ts | 2 +- src/collection.ts | 15 ++++++------ src/operations/common_functions.ts | 37 ------------------------------ src/operations/indexes.ts | 18 +++++++++++++++ src/operations/insert.ts | 2 +- src/utils.ts | 35 ++++++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 47 deletions(-) delete mode 100644 src/operations/common_functions.ts diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 5de005449e4..61050677c24 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -12,7 +12,6 @@ import { } from '../error'; import type { Filter, OneOrMore, OptionalId, UpdateFilter, WithoutId } from '../mongo_types'; import type { CollationOptions, CommandOperationOptions } from '../operations/command'; -import { maybeAddIdToDocuments } from '../operations/common_functions'; import { DeleteOperation, type DeleteStatement, makeDeleteStatement } from '../operations/delete'; import { executeOperation } from '../operations/execute_operation'; import { InsertOperation } from '../operations/insert'; @@ -21,6 +20,7 @@ import { makeUpdateStatement, UpdateOperation, type UpdateStatement } from '../o import type { Server } from '../sdam/server'; import type { Topology } from '../sdam/topology'; import type { ClientSession } from '../sessions'; +import { maybeAddIdToDocuments } from '../utils'; import { applyRetryableWrites, type Callback, diff --git a/src/collection.ts b/src/collection.ts index 187a376d929..d8646dd0e5f 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -573,7 +573,7 @@ export class Collection { const indexes = await executeOperation( this.client, CreateIndexesOperation.fromIndexSpecification( - this as TODO_NODE_3286, + this, this.collectionName, indexSpec, resolveOptions(this, options) @@ -621,7 +621,7 @@ export class Collection { return executeOperation( this.client, CreateIndexesOperation.fromIndexDescriptionArray( - this as TODO_NODE_3286, + this, this.collectionName, indexSpecs, resolveOptions(this, { ...options, maxTimeMS: undefined }) @@ -681,7 +681,7 @@ export class Collection { indexes: string | string[], options?: IndexInformationOptions ): Promise { - const indexNames: string[] = [indexes].flat(); + const indexNames: string[] = Array.isArray(indexes) ? indexes : [indexes]; const allIndexes: string[] = await this.listIndexes(options) .map(({ name }) => name) .toArray(); @@ -806,13 +806,12 @@ export class Collection { return indexes; } - const info: Record> = {}; - for (const { name, key } of indexes) { - info[name] = Object.entries(key); - } + const object: Record> = Object.fromEntries( + indexes.map(({ name, key }) => [name, Object.entries(key)]) + ); // @ts-expect-error TODO(NODE-6029): fix return type of `indexes()` and `indexInformation()` - return info; + return object; } /** diff --git a/src/operations/common_functions.ts b/src/operations/common_functions.ts deleted file mode 100644 index bc1558e895a..00000000000 --- a/src/operations/common_functions.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Document } from '../bson'; -import type { Collection } from '../collection'; - -export function maybeAddIdToDocuments( - coll: Collection, - docs: Document[], - options: { forceServerObjectId?: boolean } -): Document[]; -export function maybeAddIdToDocuments( - coll: Collection, - docs: Document, - options: { forceServerObjectId?: boolean } -): Document; -export function maybeAddIdToDocuments( - coll: Collection, - docOrDocs: Document[] | Document, - options: { forceServerObjectId?: boolean } -): Document[] | Document { - const forceServerObjectId = - typeof options.forceServerObjectId === 'boolean' - ? options.forceServerObjectId - : coll.s.db.options?.forceServerObjectId; - - // no need to modify the docs if server sets the ObjectId - if (forceServerObjectId === true) { - return docOrDocs; - } - - const transform = (doc: Document): Document => { - if (doc._id == null) { - doc._id = coll.s.pkFactory.createPk(); - } - - return doc; - }; - return Array.isArray(docOrDocs) ? docOrDocs.map(transform) : transform(docOrDocs); -} diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 5d20aeca6ba..8c926f33247 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -73,6 +73,24 @@ export type IndexSpecification = OneOrMore< /** @public */ export interface IndexInformationOptions { + /** + * When `true`, an array of index descriptions is returned. + * When `false`, the driver returns an object that with keys corresponding to index names with values + * corresponding to the entries of the indexes' key. + * + * For example, the given the following indexes: + * ``` + * [ { name: 'a_1', key: { a: 1 } }, { name: 'b_1_c_1' , key: { b: 1, c: 1 } }] + * ``` + * + * When `full` is `true`, the above array is returned. When `full` is `false`, the following is returned: + * ``` + * { + * 'a_1': [['a', 1]], + * 'b_1_c_1': [['b', 1], ['c', 1]], + * } + * ``` + */ full?: boolean; readPreference?: ReadPreference; session?: ClientSession; diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 9b3ba4b24bc..0246590ff36 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -6,10 +6,10 @@ import type { InferIdType } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { MongoDBNamespace } from '../utils'; +import { maybeAddIdToDocuments } from '../utils'; import { WriteConcern } from '../write_concern'; import { BulkWriteOperation } from './bulk_write'; import { CommandOperation, type CommandOperationOptions } from './command'; -import { maybeAddIdToDocuments } from './common_functions'; import { AbstractOperation, Aspect, defineAspects } from './operation'; /** @internal */ diff --git a/src/utils.ts b/src/utils.ts index 8b2faf7d3b3..3e158d42fd2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1284,3 +1284,38 @@ export async function once(ee: EventEmitter, name: string): Promise { throw error; } } + +export function maybeAddIdToDocuments( + coll: Collection, + docs: Document[], + options: { forceServerObjectId?: boolean } +): Document[]; +export function maybeAddIdToDocuments( + coll: Collection, + docs: Document, + options: { forceServerObjectId?: boolean } +): Document; +export function maybeAddIdToDocuments( + coll: Collection, + docOrDocs: Document[] | Document, + options: { forceServerObjectId?: boolean } +): Document[] | Document { + const forceServerObjectId = + typeof options.forceServerObjectId === 'boolean' + ? options.forceServerObjectId + : coll.s.db.options?.forceServerObjectId; + + // no need to modify the docs if server sets the ObjectId + if (forceServerObjectId === true) { + return docOrDocs; + } + + const transform = (doc: Document): Document => { + if (doc._id == null) { + doc._id = coll.s.pkFactory.createPk(); + } + + return doc; + }; + return Array.isArray(docOrDocs) ? docOrDocs.map(transform) : transform(docOrDocs); +} From 9316bb9362093d45bf6fa51f5c711c9f490683bf Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 18 Mar 2024 10:43:15 -0600 Subject: [PATCH 15/17] address comments and fix CI --- src/collection.ts | 19 +++++++++++-------- src/db.ts | 15 ++++++++++----- src/operations/create_collection.ts | 2 -- test/mongodb.ts | 1 - 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index d8646dd0e5f..0bfc1b5fa30 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -52,6 +52,7 @@ import type { CreateIndexesOptions, DropIndexesOptions, IndexDescription, + IndexDirection, IndexInformationOptions, IndexSpecification, ListIndexesOptions @@ -682,11 +683,12 @@ export class Collection { options?: IndexInformationOptions ): Promise { const indexNames: string[] = Array.isArray(indexes) ? indexes : [indexes]; - const allIndexes: string[] = await this.listIndexes(options) - .map(({ name }) => name) - .toArray(); - - return indexNames.every(name => allIndexes.includes(name)); + const allIndexes: Set = new Set( + await this.listIndexes(options) + .map(({ name }) => name) + .toArray() + ); + return indexNames.every(name => allIndexes.has(name)); } /** @@ -806,9 +808,10 @@ export class Collection { return indexes; } - const object: Record> = Object.fromEntries( - indexes.map(({ name, key }) => [name, Object.entries(key)]) - ); + const object: Record< + string, + Array<[name: string, direction: IndexDirection]> + > = Object.fromEntries(indexes.map(({ name, key }) => [name, Object.entries(key)])); // @ts-expect-error TODO(NODE-6029): fix return type of `indexes()` and `indexInformation()` return object; diff --git a/src/db.ts b/src/db.ts index 4dd87095fa0..a6fb89e6c73 100644 --- a/src/db.ts +++ b/src/db.ts @@ -22,10 +22,11 @@ import { type DropDatabaseOptions } from './operations/drop'; import { executeOperation } from './operations/execute_operation'; -import type { - CreateIndexesOptions, - IndexInformationOptions, - IndexSpecification +import { + CreateIndexesOperation, + type CreateIndexesOptions, + type IndexInformationOptions, + type IndexSpecification } from './operations/indexes'; import type { CollectionInfo, ListCollectionsOptions } from './operations/list_collections'; import { ProfilingLevelOperation, type ProfilingLevelOptions } from './operations/profiling_level'; @@ -424,7 +425,11 @@ export class Db { indexSpec: IndexSpecification, options?: CreateIndexesOptions ): Promise { - return this.collection(name).createIndex(indexSpec, options); + const indexes = await executeOperation( + this.client, + CreateIndexesOperation.fromIndexSpecification(this, name, indexSpec, options) + ); + return indexes[0]; } /** diff --git a/src/operations/create_collection.ts b/src/operations/create_collection.ts index f6077f108f4..8edc7e9a1c4 100644 --- a/src/operations/create_collection.ts +++ b/src/operations/create_collection.ts @@ -167,8 +167,6 @@ export class CreateCollectionOperation extends CommandOperation { if (encryptedFields) { // Create the required index for queryable encryption support. - // We could use `this.db.collection(name).createIndex()` to create the index, - // but we can use the same session & server to avoid an extra server selection. const createIndexOp = CreateIndexesOperation.fromIndexSpecification( db, name, diff --git a/test/mongodb.ts b/test/mongodb.ts index 18986610e56..d2cbd3e16c6 100644 --- a/test/mongodb.ts +++ b/test/mongodb.ts @@ -155,7 +155,6 @@ export * from '../src/operations/aggregate'; export * from '../src/operations/bulk_write'; export * from '../src/operations/collections'; export * from '../src/operations/command'; -export * from '../src/operations/common_functions'; export * from '../src/operations/count'; export * from '../src/operations/count_documents'; export * from '../src/operations/create_collection'; From 4b054f1effffefe1a9f47e64d9551ba233cbb243 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 18 Mar 2024 15:36:17 -0600 Subject: [PATCH 16/17] remove support for `full` from `indexExists()` --- src/collection.ts | 2 +- test/integration/index_management.test.ts | 28 ----------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 0bfc1b5fa30..720e6c415c1 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -680,7 +680,7 @@ export class Collection { */ async indexExists( indexes: string | string[], - options?: IndexInformationOptions + options?: Omit ): Promise { const indexNames: string[] = Array.isArray(indexes) ? indexes : [indexes]; const allIndexes: Set = new Set( diff --git a/test/integration/index_management.test.ts b/test/integration/index_management.test.ts index f05c038b569..fab28b5890a 100644 --- a/test/integration/index_management.test.ts +++ b/test/integration/index_management.test.ts @@ -187,34 +187,6 @@ describe('Indexes', function () { expect(await collection.indexExists(['name_1', 'age_1'])).to.be.false; }); }); - context('when `full` is true', () => { - // These tests are broken! - it('returns true when all indexes exists', async () => { - expect(await collection.indexExists(['age_1'], { full: true })).to.be.true; - }); - - it('returns false when the none of the indexes exist', async () => { - expect(await collection.indexExists(['name_1'], { full: true })).to.be.false; - }); - - it('returns false when only some of hte indexes exist', async () => { - expect(await collection.indexExists(['name_1', 'age_1'], { full: true })).to.be.false; - }); - }); - - context('when `full` is false', () => { - it('returns true when all indexes exists', async () => { - expect(await collection.indexExists(['age_1'], { full: false })).to.be.true; - }); - - it('returns false when the none of the indexes exist', async () => { - expect(await collection.indexExists(['name_1'], { full: false })).to.be.false; - }); - - it('returns false when only some of hte indexes exist', async () => { - expect(await collection.indexExists(['name_1', 'age_1'], { full: false })).to.be.false; - }); - }); }); it('shouldCorrectlyHandleUniqueIndex', async function () { From 8be035b560c1ffa42e97394929069857a045b8a3 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 18 Mar 2024 15:45:10 -0600 Subject: [PATCH 17/17] add support for cusor options --- src/collection.ts | 5 +---- src/operations/indexes.ts | 11 +++-------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 720e6c415c1..b6581675ca5 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -678,10 +678,7 @@ export class Collection { * @param indexes - One or more index names to check. * @param options - Optional settings for the command */ - async indexExists( - indexes: string | string[], - options?: Omit - ): Promise { + async indexExists(indexes: string | string[], options?: ListIndexesOptions): Promise { const indexNames: string[] = Array.isArray(indexes) ? indexes : [indexes]; const allIndexes: Set = new Set( await this.listIndexes(options) diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 8c926f33247..a1c2f12bfcb 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -1,8 +1,8 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; +import { type AbstractCursorOptions } from '../cursor/abstract_cursor'; import { MongoCompatibilityError } from '../error'; import { type OneOrMore } from '../mongo_types'; -import { type ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { isObject, maxWireVersion, type MongoDBNamespace } from '../utils'; @@ -72,7 +72,7 @@ export type IndexSpecification = OneOrMore< >; /** @public */ -export interface IndexInformationOptions { +export interface IndexInformationOptions extends ListIndexesOptions { /** * When `true`, an array of index descriptions is returned. * When `false`, the driver returns an object that with keys corresponding to index names with values @@ -92,8 +92,6 @@ export interface IndexInformationOptions { * ``` */ full?: boolean; - readPreference?: ReadPreference; - session?: ClientSession; } /** @public */ @@ -314,10 +312,7 @@ export class DropIndexOperation extends CommandOperation { } /** @public */ -export interface ListIndexesOptions extends Omit { - /** The batchSize for the returned command cursor or if pre 2.8 the systems batch collection */ - batchSize?: number; -} +export type ListIndexesOptions = AbstractCursorOptions; /** @internal */ export class ListIndexesOperation extends CommandOperation {