Menu
CoddyTech

MongoDB Query Interview Questions

Reading data with find: filters, comparison and logical operators, projection, sorting, pagination, and queries on arrays and embedded documents.

13 questions
  1. What are the CRUD operations in MongoDB?

    Create, read, update and delete map to insertOne/insertMany, find/findOne, updateOne/updateMany/replaceOne and deleteOne/deleteMany. Each takes a filter document that selects documents, plus the new data or options.

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const orders = client.db('shop').collection('orders');
      await orders.insertMany([
        { item: 'pen', qty: 10, status: 'new' },
        { item: 'book', qty: 2, status: 'new' },
      ]);
      await orders.updateOne({ item: 'pen' }, { $set: { status: 'paid' } });
      await orders.deleteOne({ item: 'book' });
      console.log(await orders.find({}, { projection: { _id: 0 } }).toArray());
    }
    main().finally(() => client.close());
  2. What is the difference between find and findOne?

    findOne returns the first matching document, or null. find returns a cursor over every match, and nothing is fetched until you iterate it or call toArray(). Without a sort, "first" means natural order, which MongoDB does not guarantee, so pass a sort when the choice matters: findOne({}, { sort: { createdAt: -1 } }).

  3. Which comparison operators does MongoDB have?

    $eq, $ne, $gt, $gte, $lt, $lte, $in and $nin. Several operators on one field are combined with AND, which is how you write a range.

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const products = client.db('shop').collection('products');
      await products.insertMany([
        { name: 'pen', price: 5 },
        { name: 'cup', price: 12 },
        { name: 'lamp', price: 30 },
        { name: 'bag', price: 40 },
      ]);
      const mid = await products.find({ price: { $gte: 10, $lte: 30 } }).sort({ price: 1 }).toArray();
      console.log(mid.map((p) => p.name));
      const some = await products.find({ name: { $in: ['pen', 'bag'] } }).sort({ price: 1 }).toArray();
      console.log(some.map((p) => p.name));
    }
    main().finally(() => client.close());
  4. How do you write AND and OR conditions in MongoDB?

    Fields in one filter are combined with AND. For OR, use $or with an array of conditions; for OR on one field, $in is shorter. You need an explicit $and only when the same key appears twice, such as two $or clauses, because an object cannot hold one key twice.

    db.users.find({ city: 'Pune', age: { $gte: 18 } })            // AND
    db.users.find({ $or: [{ city: 'Pune' }, { plan: 'pro' }] })    // OR
    db.users.find({ $and: [
      { $or: [{ city: 'Pune' }, { city: 'Delhi' }] },
      { $or: [{ plan: 'pro' }, { trial: true }] },
    ] })
    
  5. What is a projection in MongoDB?

    The option that picks which fields come back: 1 includes a field, 0 excludes it. You cannot mix the two in one projection, except for _id, which is returned unless you exclude it. Fewer fields means less network transfer, and if an index holds every requested field, the query can be covered.

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const users = client.db('shop').collection('users');
      await users.insertOne({ name: 'Asha', email: 'asha@example.com', password: 'hash' });
      const a = await users.findOne({}, { projection: { _id: 0, name: 1, email: 1 } });
      console.log(a.name, a.email, a.password, a._id);
      const b = await users.findOne({}, { projection: { password: 0 } });
      console.log(b.name, b.email, b.password, typeof b._id);
    }
    main().finally(() => client.close());
  6. Does the order of limit() and sort() in a chain matter?

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const products = client.db('shop').collection('products');
      await products.insertMany([
        { name: 'pen', price: 5 },
        { name: 'bag', price: 40 },
        { name: 'cup', price: 12 },
        { name: 'lamp', price: 30 },
      ]);
      const top = await products.find().limit(2).sort({ price: -1 }).toArray();
      console.log(top.map((p) => p.name).join(' '));
    }
    main().finally(() => client.close());

    It prints bag lamp. A find cursor sends sort, skip and limit to the server as one query, and the server always applies them in that order, whatever order you chain them in. So you get the two most expensive products, not the first two inserted. In an aggregation pipeline it is different: stages run in the order you write them.

  7. How do you paginate results in MongoDB?

    skip((page - 1) * size).limit(size) with a stable sort works for small offsets, but the server still walks every skipped document, so deep pages get slow. For large collections, use range (keyset) pagination: sort on a unique field, remember the last value, and ask for the documents after it. It uses the index and costs the same on page 1 and page 1,000, but only moves page by page.

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const posts = client.db('blog').collection('posts');
      await posts.insertMany([1, 2, 3, 4, 5].map((n) => ({ _id: n, title: `Post ${n}` })));
      const page1 = await posts.find().sort({ _id: 1 }).limit(2).toArray();
      const lastId = page1[page1.length - 1]._id;
      const page2 = await posts.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(2).toArray();
      console.log(page1.map((p) => p.title), page2.map((p) => p.title));
    }
    main().finally(() => client.close());
  8. How does a query on an array field match?

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const posts = client.db('blog').collection('posts');
      await posts.insertMany([
        { title: 'A', tags: ['node', 'db'] },
        { title: 'B', tags: ['db'] },
        { title: 'C', tags: ['go'] },
      ]);
      const titles = async (filter) =>
        (await posts.find(filter).sort({ title: 1 }).toArray()).map((p) => p.title).join(',') || '(none)';
      console.log(await titles({ tags: 'db' }));
      console.log(await titles({ tags: ['db'] }));
      console.log(await titles({ tags: ['db', 'node'] }));
    }
    main().finally(() => client.close());

    It prints A,B, B and (none). A single value matches any array that contains it. An array value is an exact match: the same elements in the same order, so ['db', 'node'] does not match ['node', 'db']. To ask for "contains all of these, in any order", use { tags: { $all: ['db', 'node'] } }.

  9. When do you need $elemMatch?

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const results = client.db('school').collection('results');
      await results.insertMany([
        { name: 'Asha', scores: [3, 12] },
        { name: 'Ravi', scores: [7] },
      ]);
      const names = async (filter) =>
        (await results.find(filter).sort({ name: 1 }).toArray()).map((r) => r.name).join(',');
      console.log(await names({ scores: { $gt: 5, $lt: 10 } }));
      console.log(await names({ scores: { $elemMatch: { $gt: 5, $lt: 10 } } }));
    }
    main().finally(() => client.close());

    It prints Asha,Ravi, then Ravi. On an array, each condition may be met by a different element: 12 is greater than 5 and 3 is less than 10, so Asha matches although no score lies between them. $elemMatch requires one element to meet every condition. The same trap applies to arrays of objects: { 'items.sku': 'a', 'items.qty': 5 } can match two different items.

  10. What is the difference between { phone: null } and $exists?

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const users = client.db('shop').collection('users');
      await users.insertMany([
        { name: 'Asha', phone: null },
        { name: 'Ravi' },
        { name: 'Meera', phone: '98200' },
      ]);
      const names = async (filter) =>
        (await users.find(filter).sort({ name: 1 }).toArray()).map((u) => u.name).join(',');
      console.log(await names({ phone: null }));
      console.log(await names({ phone: { $exists: true } }));
      console.log(await names({ phone: { $exists: false } }));
    }
    main().finally(() => client.close());

    It prints Asha,Ravi, Asha,Meera and Ravi. { phone: null } matches documents where the field is null or missing. $exists: true matches every document that has the field, even when its value is null. To find a field that is present and null, use { phone: { $type: 'null' } }.

  11. Why does a query on an embedded document return nothing?

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const users = client.db('shop').collection('users');
      await users.insertOne({ name: 'Asha', address: { city: 'Pune', zip: '411001' } });
      console.log(await users.countDocuments({ 'address.city': 'Pune' }));
      console.log(await users.countDocuments({ address: { city: 'Pune' } }));
    }
    main().finally(() => client.close());

    It prints 1 and 0. Dot notation ('address.city') matches one field inside the embedded document. { address: { city: 'Pune' } } asks for an address equal to exactly that document, with no other fields and the same field order, so it matches nothing here. Use dot notation unless you really mean whole-document equality.

  12. How do you search with a regex, and when does it use an index?

    Pass a regular expression: { name: /^ash/i } or { name: { $regex: '^ash', $options: 'i' } }. An index is used efficiently only for a case-sensitive prefix such as /^Ash/, which becomes a range scan. A case-insensitive or unanchored pattern has to test every index key or document. For case-insensitive lookups, store a lowercased copy or use an index with a case-insensitive collation; for word search, use a text index or Atlas Search.

    Node.js
    const { MongoClient } = require('mongodb');
    const client = new MongoClient('mongodb://localhost:27017');
    
    async function main() {
      const users = client.db('shop').collection('users');
      await users.insertMany([{ name: 'Asha' }, { name: 'ashok' }, { name: 'Ravi' }]);
      const found = await users.find({ name: /^ash/i }).sort({ name: 1 }).toArray();
      console.log(found.map((u) => u.name));
    }
    main().finally(() => client.close());
  13. Why does find() return a cursor instead of an array?

    So large results stream in batches instead of loading at once. The first batch holds up to 101 documents by default, later batches up to 16 MB, fetched with getMore as you iterate. for await (const doc of cursor) keeps memory flat; toArray() loads everything. The server closes a cursor that has been idle for 10 minutes, so a slow loop can fail with a cursor not found error.

Coddy programming languages illustration

Learn to code with Coddy

Get started