In this project, we will build a library management database using MongoDB. The database will store book details, library members, book issue records, and book return information. We will practise CRUD operations, queries, indexes, and aggregation.
Our library management system will manage:
Database name: libraryDB
Collections:
booksmemberstransactionsWe will keep books, members, and transactions in separate collections so that each type of information can be managed independently.
Open MongoDB Shell:
mongosh
Select the database:
use libraryDB
Create the collections:
db.createCollection("books")
db.createCollection("members")
db.createCollection("transactions")
MongoDB creates a database when data is first stored in it. The collections can also be created automatically when documents are first inserted.
If a collection already exists, MongoDB will report
an error for another createCollection()
call. You can continue using the existing collection.
Example book document:
{
book_id: 101,
title: "Introduction to JavaScript",
author: "John Smith",
category: "Programming",
publisher: "Tech Publications",
price: 450,
total_copies: 5,
available_copies: 5,
published_year: 2025
}
The book_id is an application-level
identifier. MongoDB automatically adds an
_id field when one is not supplied.
The total_copies field stores the total
number of copies, while available_copies
stores how many copies are currently available.
Insert one book using insertOne():
db.books.insertOne({
book_id: 101,
title: "Introduction to JavaScript",
author: "John Smith",
category: "Programming",
publisher: "Tech Publications",
price: 450,
total_copies: 5,
available_copies: 5,
published_year: 2025
})
Insert several additional books:
db.books.insertMany([
{
book_id: 102,
title: "Learning Python",
author: "David Miller",
category: "Programming",
price: 550,
total_copies: 4,
available_copies: 4,
published_year: 2024
},
{
book_id: 103,
title: "Computer Fundamentals",
author: "Anita Sharma",
category: "Computer Basics",
price: 300,
total_copies: 6,
available_copies: 6,
published_year: 2023
},
{
book_id: 104,
title: "Database Management",
author: "Robert Brown",
category: "Database",
price: 600,
total_copies: 3,
available_copies: 3,
published_year: 2025
}
])
Run these insertions once in your practice database. Repeating them unchanged will create additional records unless a unique index prevents duplicate IDs.
Display all books:
db.books.find()
Display books in a readable format:
db.books.find().pretty()
Find a book by its ID:
db.books.findOne({
book_id: 101
})
Find books in the Programming category:
db.books.find({
category: "Programming"
})
Find books costing less than ₹500:
db.books.find({
price: { $lt: 500 }
})
Find books with available copies:
db.books.find({
available_copies: { $gt: 0 }
})
These queries can help staff find books by ID, category, price, or availability.
Example member document:
{
member_id: 201,
name: "Rahul Kumar",
mobile: "9876543210",
email: "rahul@example.com",
membership_type: "Student",
city: "Aurangabad",
active: true
}
Insert members:
db.members.insertMany([
{
member_id: 201,
name: "Rahul Kumar",
mobile: "9876543210",
email: "rahul@example.com",
membership_type: "Student",
city: "Aurangabad",
active: true
},
{
member_id: 202,
name: "Priya Singh",
mobile: "9876543211",
email: "priya@example.com",
membership_type: "Student",
city: "Patna",
active: true
},
{
member_id: 203,
name: "Amit Kumar",
mobile: "9876543212",
email: "amit@example.com",
membership_type: "General",
city: "Gaya",
active: true
}
])
A member ID identifies the member within this application. In a real system, protect personal information and validate mobile numbers and email addresses before storing them.
A transaction document records which member borrowed which book and when the book is due.
db.transactions.insertOne({
transaction_id: 301,
book_id: 101,
member_id: 201,
issue_date: ISODate("2026-10-01T00:00:00Z"),
due_date: ISODate("2026-10-15T00:00:00Z"),
return_date: null,
status: "ISSUED"
})
This design stores the application-level book and member IDs in the transaction. MongoDB does not automatically enforce foreign-key constraints between these collections.
Your application should verify that the book and member exist before creating the transaction.
The dates above are sample values for learning. Use appropriate dates for your own project.
Find all books currently issued:
db.transactions.find({
status: "ISSUED"
})
Find transactions that are overdue and not returned:
db.transactions.find({
status: "ISSUED",
due_date: { $lt: new Date() }
})
The second query compares the due date with the current date and time. A transaction is selected if it is still marked as issued and its due date has passed.
Before issuing a book, check that a copy is available. In this beginner example, we will update the available copy count and create an issue transaction.
First, reduce the available copy count:
const bookResult = db.books.updateOne(
{
book_id: 101,
available_copies: { $gt: 0 }
},
{
$inc: {
available_copies: -1
}
}
)
bookResult
If modifiedCount is
1, a copy was available and the count
was reduced. If no document was modified, the book
may not exist or no copies may be available.
Only after confirming the successful update should the application create the transaction record. In a real multi-step issue workflow, use a transaction where supported, or design retry and recovery logic so the book count and issue record cannot become inconsistent.
Example issue record:
db.transactions.insertOne({
transaction_id: 302,
book_id: 101,
member_id: 202,
issue_date: new Date(),
due_date: new Date(
Date.now() + 14 * 24 * 60 * 60 * 1000
),
return_date: null,
status: "ISSUED"
})
This two-command demonstration is not atomic. If one command succeeds and the other fails, the data may become inconsistent. Use a properly implemented transaction or another reliable application workflow for production systems. Do not repeat this example without checking the current availability count.
When a member returns a book, mark the transaction as returned and record the return date.
db.transactions.updateOne(
{
transaction_id: 301,
status: "ISSUED"
},
{
$set: {
status: "RETURNED",
return_date: new Date()
}
}
)
Increase the available copy count after confirming that the transaction was successfully changed from issued to returned.
db.books.updateOne(
{
book_id: 101
},
{
$inc: {
available_copies: 1
}
}
)
The copy count should only be increased once for a successful return. For a real application, perform the transaction status update and inventory change together in a database transaction where supported, or implement reliable safeguards against duplicate returns.
MongoDB's $lookup aggregation stage can
combine matching documents from different collections.
db.transactions.aggregate([
{
$lookup: {
from: "books",
localField: "book_id",
foreignField: "book_id",
as: "book_details"
}
},
{
$unwind: "$book_details"
},
{
$project: {
_id: 0,
transaction_id: 1,
member_id: 1,
status: 1,
book_title: "$book_details.title",
issue_date: 1,
due_date: 1
}
}
])
The query matches each transaction's
book_id with the corresponding
book_id in the books collection.
It then displays the book title alongside transaction details.
Count the number of books in each category:
db.books.aggregate([
{
$group: {
_id: "$category",
totalBooks: { $sum: 1 },
totalCopies: { $sum: "$total_copies" }
}
},
{
$sort: {
totalBooks: -1
}
}
])
Count transactions by status:
db.transactions.aggregate([
{
$group: {
_id: "$status",
totalTransactions: { $sum: 1 }
}
}
])
These reports can help librarians understand book categories, total copies, and how many transactions are issued or returned.
Unique indexes help prevent duplicate application-level identifiers.
db.books.createIndex(
{ book_id: 1 },
{ unique: true }
)
db.members.createIndex(
{ member_id: 1 },
{ unique: true }
)
db.transactions.createIndex(
{ transaction_id: 1 },
{ unique: true }
)
If duplicate IDs already exist, index creation may fail. Check existing records before adding these indexes.
For frequently used queries, additional indexes may improve performance. Choose them based on actual query patterns and monitor their storage and write costs.
libraryDB database.books, members, and transactions collections.$lookup to display book titles with transactions.1. Which collection stores book details?
2. Which aggregation stage combines matching documents from another collection?
3. What helps prevent duplicate book IDs?