// E2E part 3: secondary indexes through the official Node driver. // createIndex / getIndexes / dropIndex, unique + sparse + compound indexes, // and a find constrained by a non-_id unique index. const { MongoClient } = require('mongodb'); const URL = 'mongodb://127.0.0.1:27020'; const results = []; function check(name, cond, detail = '') { results.push({ name, ok: !!cond, detail: String(detail) }); if (!cond) console.error(` ✗ ${name} ${detail}`); } async function main() { const client = new MongoClient(URL, { serverSelectionTimeoutMS: 5000 }); await client.connect(); const db = client.db('e2e3'); const coll = db.collection('users'); await coll.drop().catch(() => {}); // --- createIndex + getIndexes shape --- await coll.createIndex({ email: 1 }, { unique: true }); const idxs = await coll.indexes(); const names = idxs.map((i) => i.name).sort(); check('getIndexes has _id_', names.includes('_id_'), names.join(',')); check('getIndexes has email_1', names.includes('email_1'), names.join(',')); const emailIdx = idxs.find((i) => i.name === 'email_1'); check('index spec shape', emailIdx && emailIdx.key.email === 1 && emailIdx.unique === true, JSON.stringify(emailIdx)); // Idempotent re-create of the same spec. await coll.createIndex({ email: 1 }, { unique: true }); check('idempotent re-create', (await coll.indexes()).filter((i) => i.name === 'email_1').length === 1); // --- unique constraint: _id-less unique index constrains finds --- await coll.insertOne({ name: 'alice', email: 'a@x.io' }); let dupErr = null; try { await coll.insertOne({ name: 'bob', email: 'a@x.io' }); } catch (e) { dupErr = e; } check('unique constraint rejected (11000)', dupErr && dupErr.code === 11000, dupErr && dupErr.message); check('dup key message names the index', dupErr && dupErr.message.includes('email_1'), dupErr && dupErr.message); // The unique index is a real index: find by the indexed field works and // the constrained doc is found. const found = await coll.find({ email: 'a@x.io' }).toArray(); check('find via unique index', found.length === 1 && found[0].name === 'alice', JSON.stringify(found)); const cnt = await coll.countDocuments({ email: 'a@x.io' }); check('count via unique index', cnt === 1, cnt); // --- compound index --- await coll.createIndex({ name: 1, age: 1 }, { name: 'name_1_age_1' }); await coll.insertMany([ { name: 'carol', age: 30, email: 'c@x.io' }, { name: 'carol', age: 35, email: 'd@x.io' }, { name: 'dave', age: 40, email: 'e@x.io' }, ]); const compound = await coll.find({ name: 'carol' }).sort({ age: 1 }).toArray(); check('compound prefix find', compound.length === 2 && compound[0].age === 30, JSON.stringify(compound.map((d) => d.age))); const compound2 = await coll.find({ name: 'carol', age: { $gt: 32 } }).count(); check('compound equality + range', compound2 === 1, compound2); // --- sparse index --- await coll.createIndex({ nickname: 1 }, { sparse: true, name: 'nickname_1_sparse' }); // Only one doc has a nickname; the sparse index covers it. await coll.updateOne({ name: 'dave' }, { $set: { nickname: 'davie' } }); const sparse = await coll.find({ nickname: 'davie' }).toArray(); check('sparse index find', sparse.length === 1 && sparse[0].name === 'dave', JSON.stringify(sparse)); // Docs without the field stay findable by other fields (scan fallback). const all = await coll.countDocuments({}); check('count unaffected by sparse index', all === 4, all); // --- descending key + default names --- await coll.createIndex({ score: -1 }); const after = await coll.indexes(); const scoreIdx = after.find((i) => i.key && i.key.score === -1); check('descending index created', !!scoreIdx, JSON.stringify(after)); // --- dropIndex --- await coll.dropIndex('name_1_age_1'); const afterDrop = await coll.indexes(); check('dropIndex removes it', !afterDrop.some((i) => i.name === 'name_1_age_1'), JSON.stringify(afterDrop.map((i) => i.name))); check('_id_ still present after drops', afterDrop.some((i) => i.name === '_id_')); // dropIndexes("*") removes the rest. await coll.dropIndexes(); const afterAll = await coll.indexes(); check('dropIndexes("*") keeps only _id_', afterAll.length === 1 && afterAll[0].name === '_id_', JSON.stringify(afterAll)); await client.close(); const failed = results.filter((r) => !r.ok); console.log(`\n${results.length - failed.length}/${results.length} checks passed`); if (failed.length) { console.log('FAILED:', failed.map((f) => f.name).join(', ')); process.exit(1); } console.log('E2E3_OK'); } main().catch((e) => { console.error('E2E3_FAIL', e); process.exit(1); });