00:00

MongoDB Query Document

Last updated: 7 min read

Reading data is what most applications do most of the time. MongoDB's query language lets you filter on any field, including fields inside nested documents and arrays, choose which fields to return, and sort and page through the results.

This tutorial covers find() and findOne(), all the commonly used query operators, projection, sorting and pagination, counting, a first look at aggregation, checking query performance with explain(), and querying from Java.

1. Sample Data

Run this once in mongosh to follow along:

mongosh
use bookstore
db.books.drop()
db.books.insertMany([
  { _id: 1, title: "Practical Java Streams",   category: "Java",     price: 499, stock: 40, rating: 4.6,
    tags: ["java", "streams"], publisher: { name: "Techie Press", country: "IN" },
    published: ISODate("2025-03-14") },
  { _id: 2, title: "Designing Event Systems",  category: "Kafka",    price: 650, stock: 12, rating: 4.8,
    tags: ["kafka", "events", "architecture"], publisher: { name: "Northwind Books", country: "US" },
    published: ISODate("2024-11-02") },
  { _id: 3, title: "Document Modeling Basics", category: "Database", price: 420, stock: 0,  rating: 4.1,
    tags: ["mongodb", "modeling"], publisher: { name: "Techie Press", country: "IN" },
    published: ISODate("2023-06-20") },
  { _id: 4, title: "Cloud Native Spring",      category: "Java",     price: 720, stock: 25, rating: 4.4,
    tags: ["java", "spring", "cloud"], publisher: { name: "Northwind Books", country: "US" },
    published: ISODate("2026-01-09") },
  { _id: 5, title: "Indexing in Depth",        category: "Database", price: 560, stock: 8,
    tags: ["mongodb", "performance"], publisher: { name: "Data House", country: "PT" },
    published: ISODate("2025-08-30") }
])

2. find() and findOne()

  • find(filter, projection) returns a cursor over all matching documents. mongosh prints the first 20; type it for more.
  • findOne(filter, projection) returns the first matching document, or null.
mongosh
db.books.find()                         // every document
db.books.find({ category: "Java" })     // equality: category is "Java"
db.books.findOne({ _id: 3 })            // one document by _id
db.books.findOne({ title: "Missing" })  // null

Several conditions in one filter are combined with AND:

mongosh
db.books.find({ category: "Java", stock: 40 })

3. Comparison Operators

OperatorMeaningExample
$eqEqual{ price: { $eq: 499 } } (same as { price: 499 })
$neNot equal{ category: { $ne: "Java" } }
$gt / $gteGreater than / or equal{ rating: { $gte: 4.5 } }
$lt / $lteLess than / or equal{ price: { $lt: 500 } }
$inMatches any value in a list{ category: { $in: ["Java", "Kafka"] } }
$ninMatches none of the values{ category: { $nin: ["Database"] } }
mongosh
// Books between 400 and 600, inclusive
db.books.find({ price: { $gte: 400, $lte: 600 } })

// Published in 2025
db.books.find({ published: { $gte: ISODate("2025-01-01"), $lt: ISODate("2026-01-01") } })
$ne and $nin also match documents that do not have the field at all. Combine them with $exists: true when that matters.

4. Logical Operators

mongosh
// OR: Kafka books, or anything out of stock
db.books.find({ $or: [ { category: "Kafka" }, { stock: 0 } ] })

// Explicit AND, needed when the same field appears twice
db.books.find({ $and: [ { price: { $gt: 400 } }, { price: { $ne: 560 } } ] })

// NOT: rating not greater than 4.5 (also matches missing rating)
db.books.find({ rating: { $not: { $gt: 4.5 } } })

// NOR: neither Java nor out of stock
db.books.find({ $nor: [ { category: "Java" }, { stock: 0 } ] })

5. Element Operators

mongosh
db.books.find({ rating: { $exists: false } })     // documents without a rating
db.books.find({ price: { $type: "number" } })     // price stored as any numeric type
db.books.find({ rating: null })                   // rating is null OR missing

6. Querying Embedded Documents

Use dot notation, in quotes, to reach a field inside an embedded document:

mongosh
db.books.find({ "publisher.country": "IN" })
db.books.find({ "publisher.name": "Northwind Books", price: { $gt: 600 } })
{ publisher: { name: "Techie Press", country: "IN" } } matches only if the embedded document is exactly that, with the same fields in the same order. Dot notation is almost always what you want.

7. Querying Arrays

mongosh
db.books.find({ tags: "java" })                          // array contains "java"
db.books.find({ tags: { $all: ["java", "cloud"] } })     // contains both, in any order
db.books.find({ tags: { $size: 3 } })                    // exactly 3 elements
db.books.find({ "tags.0": "mongodb" })                   // first element is "mongodb"

For arrays of embedded documents, $elemMatch requires all conditions to be true for the same element:

mongosh
// orders where ONE item has bookId 1 AND qty of at least 2
db.orders.find({ items: { $elemMatch: { bookId: 1, qty: { $gte: 2 } } } })

8. Text Patterns

mongosh
db.books.find({ title: { $regex: "^Design", $options: "i" } })   // starts with "design", case-insensitive
db.books.find({ title: /streams/i })                             // shorthand

A regex anchored at the start (^Design) and case-sensitive can use an index on title; other regular expressions scan every value. For real search features, use a text index or Atlas Search.

9. Projection: Choosing Fields

The second argument of find() selects which fields to return. Returning less data is faster and safer:

mongosh
db.books.find({ category: "Java" }, { title: 1, price: 1 })          // include title, price (+ _id)
db.books.find({ category: "Java" }, { title: 1, price: 1, _id: 0 })  // hide _id too
db.books.find({}, { publisher: 0, tags: 0 })                        // everything except these
db.books.find({}, { title: 1, "publisher.name": 1, _id: 0 })        // nested field
db.books.find({}, { title: 1, firstTag: { $first: "$tags" } })      // computed field

Do not mix inclusion (1) and exclusion (0) in one projection; the only exception is _id: 0.

10. Sorting, Limiting and Paging

mongosh
db.books.find().sort({ price: -1 })                  // most expensive first
db.books.find().sort({ category: 1, price: 1 })      // by category, then price
db.books.find().sort({ rating: -1 }).limit(3)        // top 3 by rating

// Page 2 with 2 books per page (skip/limit)
db.books.find().sort({ _id: 1 }).skip(2).limit(2)

skip() gets slower as the page number grows, because skipped documents are still read. For large data sets, page with a range on a sorted, indexed field instead:

mongosh
// "Next page after the last _id I showed"
db.books.find({ _id: { $gt: 2 } }).sort({ _id: 1 }).limit(2)

11. Counting and Distinct Values

mongosh
db.books.countDocuments({ category: "Java" })   // exact count for a filter
db.books.estimatedDocumentCount()               // fast total from metadata, no filter
db.books.distinct("category")                    // [ 'Database', 'Java', 'Kafka' ]
db.books.distinct("tags", { category: "Java" })

12. Beyond find(): A First Look at Aggregation

When you need grouping, calculations or joins, use the aggregation pipeline. Each stage transforms the documents and passes them on:

mongosh
db.books.aggregate([
  { $match: { stock: { $gt: 0 } } },
  { $group: { _id: "$category", titles: { $sum: 1 }, avgPrice: { $avg: "$price" } } },
  { $sort: { avgPrice: -1 } }
])
// [ { _id: 'Kafka', titles: 1, avgPrice: 650 }, { _id: 'Java', titles: 2, avgPrice: 609.5 }, ... ]

13. Checking Performance with explain()

explain("executionStats") shows how MongoDB ran the query. Compare totalDocsExamined with nReturned: if MongoDB examines far more documents than it returns, the query needs an index.

mongosh
db.books.find({ category: "Java" }).explain("executionStats").executionStats
// { nReturned: 2, totalDocsExamined: 5, ... }  -> COLLSCAN: every document was read

db.books.createIndex({ category: 1 })
db.books.find({ category: "Java" }).explain("executionStats").executionStats
// { nReturned: 2, totalKeysExamined: 2, totalDocsExamined: 2, ... }  -> IXSCAN

See the Indexing tutorial for how to design indexes for your queries.

14. Querying from Java

The Java driver provides the Filters, Projections and Sorts helpers, which build the same filter documents you write in mongosh:

QueryBooksExample.java
import com.mongodb.client.*;
import org.bson.Document;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Projections.*;
import static com.mongodb.client.model.Sorts.*;

public class QueryBooksExample {
    public static void main(String[] args) {
        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {
            MongoCollection<Document> books = client.getDatabase("bookstore").getCollection("books");

            FindIterable<Document> result = books
                    .find(and(eq("category", "Java"), gte("rating", 4.5)))
                    .projection(fields(include("title", "price"), excludeId()))
                    .sort(descending("price"))
                    .limit(10);

            for (Document book : result) {
                System.out.println(book.getString("title") + " - " + book.get("price"));
            }

            long outOfStock = books.countDocuments(eq("stock", 0));
            Document first = books.find(in("tags", "mongodb")).first();   // null if none
        }
    }
}

15. Summary

  • find() returns a cursor of matching documents; findOne() returns one document or null.
  • Combine comparison ($gt, $in…), logical ($or, $not…), element ($exists) and array ($all, $elemMatch) operators.
  • Use dot notation for embedded fields and projection to return only what you need.
  • Sort, limit and page results, preferring range-based paging on large collections.
  • Use countDocuments(), distinct() and the aggregation pipeline for summaries, and explain() to find missing indexes.