MongoDB find(): Hướng dẫn hoàn chỉnh cho người mới bắt đầu truy vấn dữ liệu
Hướng dẫn này giải thích cách dùng phương thức find() của MongoDB để truy vấn, lọc, sắp xếp và phân trang dữ liệu với ví dụ thực tế. Hoàn hảo cho người mới và người chuyển từ SQL.
MongoDB là một cơ sở dữ liệu NoSQL phổ biến, được thiết kế cho tính linh hoạt và hiệu năng. Khác với các cơ sở dữ liệu quan hệ truyền thống dùng bảng có cấu trúc và SQL, MongoDB lưu trữ dữ liệu trong các tài liệu linh hoạt giống JSON. Điều này khiến nó lý tưởng cho nhà phát triển làm việc với dữ liệu động hoặc bán cấu trúc.
Một trong những thao tác quan trọng nhất bạn sẽ thực hiện trong MongoDB là truy vấn dữ liệu, và phương thức được dùng thường xuyên nhất là find(). Dù bạn mới bắt đầu hay muốn cải thiện kỹ năng truy vấn, hiểu cách find() hoạt động là điều thiết yếu để điều hướng và thao tác dữ liệu MongoDB hiệu quả.
Hãy tưởng tượng bạn đang khám phá một cơ sở dữ liệu phim. Bạn muốn tìm tất cả phim khoa học viễn tưởng phát hành sau năm 2000, hoặc có thể liệt kê tất cả bình luận người dùng trong một khoảng thời gian cụ thể. Đó là lúc find() phát huy tác dụng.
Trong bài viết này, bạn sẽ học cách dùng phương thức find() để truy xuất tài liệu từ một bộ sưu tập, áp dụng bộ lọc, chiếu trường, sắp xếp kết quả và tối ưu hiệu năng—tất cả với bộ dữ liệu sample_mflix.
Phương thức find() là cách chính của MongoDB để truy xuất tài liệu từ một bộ sưu tập. Nó trả về một con trỏ (cursor) trỏ tới các tài liệu khớp với truy vấn đã chỉ định. Sau đó, bạn có thể lặp qua con trỏ này, hoặc chuyển nó thành một mảng, tùy theo nhu cầu ứng dụng của bạn.
Cú pháp đầy đủ như sau:
db.collection.find(<query>,<projection>,<options>)Được hỗ trợ bởi
Tham số
query (tùy chọn): Một tài liệu xác định tiêu chí chọn tài liệu. Nếu bạn bỏ qua tham số này hoặc truyền một tài liệu trống ({}), find() sẽ trả về tất cả tài liệu trong bộ sưu tập.
projection (tùy chọn): Một tài liệu chỉ định những trường nào sẽ được trả về. Bạn có thể bao gồm (1) hoặc loại trừ (0) trường. Mặc định, MongoDB trả về tất cả trường, bao gồm _id.
options (tùy chọn): Một tài liệu bổ sung để cấu hình hành vi truy vấn. Bạn có thể đặt các thuộc tính như thứ tự sắp xếp, giới hạn phân trang, thời gian chờ và gợi ý chỉ mục.
Ví dụ
Để các khái niệm rõ ràng và dễ áp dụng nhất, chúng ta sẽ dùng bộ dữ liệu mẫu MongoDB Atlas—cụ thể là cơ sở dữ liệu sample_mflix. Bộ dữ liệu này gồm các bộ sưu tập như movies, comments, users và theaters, mà chúng ta sẽ tham chiếu xuyên suốt bài viết để minh họa từng ví dụ.
db.books.find({year:{$gte:2000}},// query: books published in or after 2000{title:1,author:1,_id:0},// projection: include only title and author{sort:{title:1},limit:5}// options: sort by title, return only 5 books)Được hỗ trợ bởi
Truy vấn này:
Lọc các sách xuất bản từ năm 2000 trở đi.
Chỉ trả về trường title và author (loại trừ _id).
Sắp xếp kết quả theo tiêu đề theo thứ tự chữ cái (A–Z).
Giới hạn đầu ra ở 5 cuốn sách khớp đầu tiên.
Hình 1. Truy vấn này tìm các phim phát hành sau năm 2000, sắp xếp theo tiêu đề và giới hạn ở năm kết quả.
Kết quả là một con trỏ, tải lười (lazy) các tài liệu khớp khi bạn lặp qua chúng. Bạn có thể dùng .toArray() hoặc .forEach() để xử lý kết quả.
Nếu bạn kết nối qua shell MongoDB (mongosh) và không gán kết quả cho biến, mặc định chỉ 20 tài liệu đầu tiên sẽ hiển thị. Để xem thêm, nhập nó rồi nhấn Enter. Quá trình này sẽ tiếp tục lấy lô tiếp theo từ con trỏ.
> Lưu ý: Trên đây là hành vi riêng của shell, không phải hạn chế của MongoDB. Trong hầu hết driver MongoDB (ví dụ, Node.js, Python, Java), gọi .toArray() hoặc lặp qua con trỏ sẽ tự động lấy tất cả kết quả khớp. Hành vi con trỏ có thể khác nhau tùy môi trường và ngôn ngữ bạn dùng.
Hình 2. Truy vấn này truy xuất tất cả phim, chỉ trả về trường _id và dùng con trỏ để lấy thêm kết quả.
Nếu bạn mới với cơ sở dữ liệu NoSQL, khóa học Introduction to NoSQL cung cấp các khái niệm nền tảng bổ trợ cho hướng dẫn này.
Viết các truy vấn đầu tiên của bạn
Tiếp tục với ví dụ phim, giả sử bạn muốn lấy tất cả phim thuộc thể loại Sci-Fi. Cách làm như sau:
Vậy nếu muốn cả hai điều kiện? Hãy kết hợp điều kiện:
db.movies.find({genres:"Sci-Fi",year:{$gte:2000}})Được hỗ trợ bởi
Hoặc, bạn có thể viết tường minh bằng $and:
db.movies.find({$and:[{genres:"Sci-Fi"},{year:{$gte:2000}}]})Được hỗ trợ bởi
Cả hai truy vấn trả về cùng kết quả—các tài liệu có thể loại Sci-Fi và năm phát hành từ 2000 trở đi. Dạng đầu ngắn gọn hơn và được ưa dùng khi các trường khác nhau.
Hình 3. Các cách viết truy vấn khác nhau này cho ra cùng một kết quả.
Hãy dùng find() như một phễu lọc—mỗi điều kiện sẽ thu hẹp kết quả của bạn.
Sử dụng toán tử so sánh và logic
Giờ hãy xem cách dùng các toán tử so sánh và logic để đưa truy vấn của bạn lên tầm tiếp theo.
Toán tử so sánh
$eq, $ne: bằng, không bằng
$gt, $gte, $lt, $lte: lớn hơn/nhỏ hơn (hoặc bằng)
$in, $nin: thuộc hoặc không thuộc mảng
Hãy tưởng tượng bạn đang theo dõi đơn hàng khách hàng và muốn các đơn trong khoảng 100–500 USD:
Giờ, giả sử bạn muốn hiển thị sản phẩm trong danh mục "Books" hoặc có giá dưới 20 USD. Dưới đây là một truy vấn ví dụ:
db.products.find({$or:[{category:"Books"},{price:{$lt:20}}]})Được hỗ trợ bởi
Truy vấn trường lồng nhau
Bạn có thể truy cập trường lồng nhau bằng cú pháp chấm:
db.customers.find({"address.city":"Chicago"})Được hỗ trợ bởi
Chiếu trường (Projection)
Projection cho phép bạn chỉ trả về những gì cần thiết, từ đó cải thiện đáng kể hiệu năng và độ rõ ràng.
db.movies.find({},{directors:1,languages:1,_id:0})Được hỗ trợ bởi
Quan trọng: Khi chỉ định projection, MongoDB yêu cầu bạn chọn giữa hai chế độ: Hoặc bao gồm các trường cụ thể (1) hoặc loại trừ các trường cụ thể (0). Bạn không thể trộn cả hai kiểu trong cùng một projection, ngoại trừ trường _id, có thể loại trừ ngay cả khi đang bao gồm các trường khác.
db.movies.find({},{directors:1,languages:0})// Not allowedĐược hỗ trợ bởi
Ngoại lệ duy nhất là trường _id, bạn có thể loại trừ ngay cả khi đang bao gồm các trường khác:
db.movies.find({},{directors:1,_id:0})// AllowedĐược hỗ trợ bởi
Hình 4. Ví dụ này minh họa lỗi khi trộn bao gồm và loại trừ trường trong một projection, điều MongoDB không cho phép, ngoại trừ `_id`.
Sắp xếp, giới hạn và bỏ qua kết quả
Khi làm việc với bộ dữ liệu lớn, hiển thị cho người dùng chỉ một phần dữ liệu mỗi lần là điều thường gặp—kỹ thuật này gọi là phân trang. Ví dụ, một ứng dụng streaming có thể hiển thị 10 phim mỗi trang thay vì tải hàng nghìn phim cùng lúc. Điều này không chỉ cải thiện hiệu năng mà còn nâng cao trải nghiệm người dùng.
Trong MongoDB, phân trang được thực hiện bằng cách kết hợp sort(), limit() và skip().
SQL vs. MongoDB: So sánh cơ bản
Nếu bạn đến từ nền tảng SQL, dưới đây là cách các khái niệm phân trang phổ biến tương ứng:
> Lưu ý: Dùng skip() cho phân trang sâu (ví dụ, skip(10000)) có thể kém hiệu quả, vì MongoDB vẫn quét và loại bỏ các tài liệu bị bỏ qua. Để hiệu năng tốt hơn với bộ dữ liệu lớn, hãy cân nhắc phân trang theo phạm vi dựa trên các trường được lập chỉ mục như _id.
Ví dụ kết hợp: Trang 3, 10 phim mỗi trang, sắp xếp theo tiêu đề
db.movies.find().sort({title:1}).skip(20).limit(10)Được hỗ trợ bởi
Mẫu này cũng hữu ích cho các tính năng như “tải thêm” hoặc cuộn vô hạn.
Hình 5. Truy vấn này truy xuất trang thứ ba của kết quả (10 phim mỗi trang), sắp xếp theo tiêu đề.
Làm việc với con trỏ (Cursor)
Phương thức find() trả về một con trỏ—một con trỏ tới tập kết quả. Bạn có thể lặp qua nó:
const cursor = db.movies.find({type:"movie"});
cursor.forEach(doc=>print(doc.name));Được hỗ trợ bởi
Hoặc chuyển nó thành mảng:
const results = db.collection.find().toArray();Được hỗ trợ bởi
Hình 6. Sau khi gọi `find()`, bạn nhận được một con trỏ. Bạn có thể dùng nó để lặp qua tài liệu hoặc chuyển toàn bộ tập kết quả thành mảng.
> Lưu ý: Đối tượng con trỏ có sẵn trong mongosh hoặc trong mã qua các driver (ví dụ, Node.js, Python). Các công cụ GUI như MongoDB Compass xử lý con trỏ nội bộ. Bạn sẽ không truy cập hoặc lặp qua chúng trực tiếp.
Tùy chọn nâng cao cho con trỏ
Để tinh chỉnh truy vấn, bạn có thể dùng thêm các phương thức trên con trỏ:
db.users.find({age:{$gte:30}}).sort({name:1}).limit(10).skip(5).maxTimeMS(200).hint({age:1})Được hỗ trợ bởi
maxTimeMS(ms): hủy truy vấn nếu mất quá nhiều thời gian
Điều này cho biết liệu các chỉ mục có được sử dụng không và truy vấn của bạn hiệu quả thế nào.
Không có chỉ mục:
Hình 7. Chạy `explain()` cho truy vấn trước khi lập chỉ mục.
Tạo chỉ mục:
Hình 8. Tạo một chỉ mục đơn giản trên trường type.
Có chỉ mục:
Hình 9. Chạy lại truy vấn với `explain()`, lúc này dùng chỉ mục.
Bạn có thể thấy giai đoạn trước đây là CollScan (quét bộ sưu tập) đã trở thành Fetch + IXSCAN, về cơ bản là dùng chỉ mục + tìm thêm dữ liệu không nằm trong chỉ mục (vì chúng ta đang trả về mọi thứ).
Dùng MongoDB Compass để trực quan hóa truy vấn
Nếu bạn thích công cụ trực quan, MongoDB Compass là ứng dụng GUI chính thức. Nó cho phép bạn:
Xây dựng truy vấn trực quan bằng bộ lọc.
Xem trước tài liệu và chỉ mục.
Tối ưu và xác thực hiệu năng truy vấn.
Bạn thậm chí có thể chuyển sang "JSON view" để sao chép/dán truy vấn vào mã của bạn.
Dùng Atlas + AI để tạo truy vấn
Nếu bạn đang dùng MongoDB Atlas, bạn có thể tận dụng Atlas Search với trình tạo truy vấn hỗ trợ AI.
Chỉ cần nhập lời nhắc ngôn ngữ tự nhiên như:
"Hiển thị tất cả khách hàng từ Brazil đã đặt đơn trên 100 USD trong tháng trước."
Atlas sẽ gợi ý một truy vấn MongoDB phù hợp bằng find() hoặc $search. Đây là một công cụ tăng năng suất tuyệt vời, đặc biệt khi bạn chưa chắc về cú pháp.
Hình 10. Dùng Compass để tạo truy vấn từ ngôn ngữ tự nhiên.
Những lỗi thường gặp và cách tránh
Truy vấn các trường chưa lập chỉ mục → Dẫn tới hiệu năng chậm
Tập kết quả lớn → Dùng phân trang (limit + skip)
Projection kém hiệu quả → Luôn chỉ chiếu những trường cần thiết
Không khớp kiểu dữ liệu → Đảm bảo kiểu trường khớp nhau (ví dụ, số vs. chuỗi)
Trộn bao gồm và loại trừ trong projection → Không được phép, ngoại trừ _id
So sánh MongoDB find() với SQL
Nếu bạn đến từ nền tảng SQL, hiểu cách find() của MongoDB so với các truy vấn SQL quen thuộc có thể giúp quá trình chuyển đổi dễ dàng hơn. Dưới đây là so sánh tổng quan các thao tác phổ biến:
Khái niệm
Ví dụ SQL
Tương đương trong MongoDB
Truy vấn cơ bản
SELECT * FROM users WHERE age >= 18
db.users.find({ age: { $gte: 18 } })
Chọn trường
SELECT name, email FROM users
db.users.find({}, { name: 1, email: 1, _id: 0 })
Sắp xếp
ORDER BY created_at DESC
.sort({ createdAt: -1 })
Giới hạn
LIMIT 10
.limit(10)
Bỏ qua
OFFSET 10
.skip(10)
Như bạn thấy, cú pháp của MongoDB giống JavaScript hơn và diễn đạt theo định dạng JSON, trong khi SQL mang tính khai báo và có cấu trúc. Biết những điểm tương đồng này sẽ giúp bạn nhanh chóng cảm thấy quen thuộc với MongoDB.
Tóm tắt và thực tiễn tốt
Hãy coi find() là bộ lọc dữ liệu của bạn—kết hợp điều kiện để có kết quả chính xác.
Luôn chỉ chiếu các trường cần thiết để rõ ràng và hiệu năng tốt.
Dùng chỉ mục để tránh quét chậm.
Dùng Compass nếu bạn thích phản hồi trực quan và lặp nhanh hơn.
Tận dụng trợ lý AI của MongoDB Atlas để tạo truy vấn nhanh hơn.
Khám phá các tùy chọn con trỏ nâng cao để kiểm soát chi tiết kết quả.
Dù bạn đang xây dựng bảng điều khiển cho startup, công cụ phân tích dữ liệu hay back end thương mại điện tử, thành thạo find() sẽ giúp trải nghiệm MongoDB của bạn mượt mà, nhanh và hiệu quả hơn.
Hãy đón đọc thêm các bài viết về MongoDB trong thời gian tới!
Về mặt thực tế, con trỏ là gì? Tại sao find() không trả về một mảng kết quả?⌄
MongoDB trả về một con trỏ, tải tài liệu theo lô (lazy loading). Điều này hiệu quả cho các bộ sưu tập lớn nhưng có thể gây bối rối cho người mới, những người mong đợi một mảng.
Tại sao find() có ba tham số, nhưng tôi thường chỉ thấy hai? Vì sao đối số thứ ba (options) hiếm khi xuất hiện trong ví dụ?⌄
Bởi vì hầu hết nhà phát triển dùng các phương thức xâu chuỗi như .sort(), .limit() và .skip() thay vì truyền chúng trong đối tượng options. Một số tính năng (ví dụ, hint, readConcern, collation) yêu cầu dùng tài liệu options hoặc gọi phương thức riêng.
Tại sao find() không dùng chỉ mục tôi đã tạo? Tôi đã tạo chỉ mục nhưng truy vấn find() vẫn chậm.⌄
Không phải mọi truy vấn đều tự động dùng chỉ mục. Thứ tự trường, kiểu dữ liệu và cài đặt collation có thể ảnh hưởng việc sử dụng chỉ mục. Dùng .explain("executionStats") để kiểm tra MongoDB có dùng chỉ mục (IXSCAN) hay quét toàn bộ (COLLSCAN).
Projection của tôi có thực sự cải thiện hiệu năng không? Tôi chỉ chiếu hai trường nhưng có vẻ không nhanh hơn.⌄
Nếu các trường được chiếu không nằm trong một chỉ mục, MongoDB vẫn cần tải toàn bộ tài liệu từ đĩa. Hãy dùng chỉ mục bao phủ (covering index) để phục vụ truy vấn hoàn toàn từ chỉ mục, tránh tra cứu tài liệu.
Tôi có thể dùng find() để nhóm hoặc tổng hợp dữ liệu không? Tôi có thể đếm số phim phát hành mỗi năm bằng find() không?⌄
Không. find() dùng để truy xuất tài liệu. Để nhóm hoặc tổng hợp, hãy dùng Aggregation Framework ($group, $count, v.v.). Mặc dù find() + .countDocuments() xử lý đếm cơ bản, các nhu cầu phân tích thực sự nên dùng .aggregate().
Sự khác nhau giữa find() và findOne() trong MongoDB là gì?⌄
find() trả về một con trỏ tới tất cả tài liệu khớp, trong khi indOne() trả về trực tiếp tài liệu khớp đầu tiên. Dùng findOne() khi bạn chỉ cần một kết quả.
Làm sao truy vấn một trường mảng bằng find() trong MongoDB?⌄
Bạn có thể khớp phần tử mảng trực tiếp. Ví dụ, { tags: "tech" } khớp nếu "tech" nằm trong mảng tags. Dùng $elemMatch cho các điều kiện phức tạp hơn.
Tôi có thể dùng regular expression trong truy vấn find() của MongoDB không?⌄
Có, MongoDB hỗ trợ biểu thức chính quy với cú pháp như { title: /sci-fi/i } để thực hiện khớp mẫu không phân biệt hoa thường trên chuỗi.
Làm sao dùng find() với khoảng thời gian ngày tháng trong MongoDB?⌄
Hãy dùng đối tượng ISODate trong truy vấn. Ví dụ: { createdAt: { $gte: ISODate("2024-01-01"), $lte: ISODate("2024-12-31") } }.
Tác động hiệu năng khi dùng find() mà không có chỉ mục là gì?⌄
Truy vấn trên các trường chưa lập chỉ mục sẽ kích hoạt quét toàn bộ bộ sưu tập, điều này chậm với bộ dữ liệu lớn. Luôn lập chỉ mục cho các trường được truy vấn thường xuyên.
MongoDB là một cơ sở dữ liệu NoSQL phổ biến, được thiết kế cho tính linh hoạt và hiệu năng. Khác với các cơ sở dữ liệu quan hệ truyền thống dùng bảng có cấu trúc và SQL, MongoDB lưu trữ dữ liệu trong các tài liệu linh hoạt giống JSON. Điều này khiến nó lý tưởng cho nhà phát triển làm việc với dữ liệu động hoặc bán cấu trúc.
Một trong những thao tác quan trọng nhất bạn sẽ thực hiện trong MongoDB là truy vấn dữ liệu, và phương thức được dùng thường xuyên nhất là
find(). Dù bạn mới bắt đầu hay muốn cải thiện kỹ năng truy vấn, hiểu cáchfind()hoạt động là điều thiết yếu để điều hướng và thao tác dữ liệu MongoDB hiệu quả.Hãy tưởng tượng bạn đang khám phá một cơ sở dữ liệu phim. Bạn muốn tìm tất cả phim khoa học viễn tưởng phát hành sau năm 2000, hoặc có thể liệt kê tất cả bình luận người dùng trong một khoảng thời gian cụ thể. Đó là lúc
find()phát huy tác dụng.Trong bài viết này, bạn sẽ học cách dùng phương thức
find()để truy xuất tài liệu từ một bộ sưu tập, áp dụng bộ lọc, chiếu trường, sắp xếp kết quả và tối ưu hiệu năng—tất cả với bộ dữ liệusample_mflix.Để xem hướng dẫn thực hành sử dụng MongoDB trong quy trình khoa học dữ liệu, hãy xem khóa học Introduction to MongoDB in Python.
Giới thiệu về find() trong MongoDB
Phương thức
find()là cách chính của MongoDB để truy xuất tài liệu từ một bộ sưu tập. Nó trả về một con trỏ (cursor) trỏ tới các tài liệu khớp với truy vấn đã chỉ định. Sau đó, bạn có thể lặp qua con trỏ này, hoặc chuyển nó thành một mảng, tùy theo nhu cầu ứng dụng của bạn.Cú pháp đầy đủ như sau:
Tham số
query(tùy chọn): Một tài liệu xác định tiêu chí chọn tài liệu. Nếu bạn bỏ qua tham số này hoặc truyền một tài liệu trống ({}),find()sẽ trả về tất cả tài liệu trong bộ sưu tập.projection(tùy chọn): Một tài liệu chỉ định những trường nào sẽ được trả về. Bạn có thể bao gồm (1) hoặc loại trừ (0) trường. Mặc định, MongoDB trả về tất cả trường, bao gồm_id.options(tùy chọn): Một tài liệu bổ sung để cấu hình hành vi truy vấn. Bạn có thể đặt các thuộc tính như thứ tự sắp xếp, giới hạn phân trang, thời gian chờ và gợi ý chỉ mục.Ví dụ
Để các khái niệm rõ ràng và dễ áp dụng nhất, chúng ta sẽ dùng bộ dữ liệu mẫu MongoDB Atlas—cụ thể là cơ sở dữ liệu
sample_mflix. Bộ dữ liệu này gồm các bộ sưu tập như movies, comments, users và theaters, mà chúng ta sẽ tham chiếu xuyên suốt bài viết để minh họa từng ví dụ.Truy vấn này:
_id).Hình 1. Truy vấn này tìm các phim phát hành sau năm 2000, sắp xếp theo tiêu đề và giới hạn ở năm kết quả.
Kết quả là một con trỏ, tải lười (lazy) các tài liệu khớp khi bạn lặp qua chúng. Bạn có thể dùng
.toArray()hoặc.forEach()để xử lý kết quả.Nếu bạn kết nối qua shell MongoDB (
mongosh) và không gán kết quả cho biến, mặc định chỉ 20 tài liệu đầu tiên sẽ hiển thị. Để xem thêm, nhập nó rồi nhấn Enter. Quá trình này sẽ tiếp tục lấy lô tiếp theo từ con trỏ.> Lưu ý: Trên đây là hành vi riêng của shell, không phải hạn chế của MongoDB. Trong hầu hết driver MongoDB (ví dụ, Node.js, Python, Java), gọi
.toArray()hoặc lặp qua con trỏ sẽ tự động lấy tất cả kết quả khớp. Hành vi con trỏ có thể khác nhau tùy môi trường và ngôn ngữ bạn dùng.Hình 2. Truy vấn này truy xuất tất cả phim, chỉ trả về trường
_idvà dùng con trỏ để lấy thêm kết quả.Nếu bạn mới với cơ sở dữ liệu NoSQL, khóa học Introduction to NoSQL cung cấp các khái niệm nền tảng bổ trợ cho hướng dẫn này.
Viết các truy vấn đầu tiên của bạn
Tiếp tục với ví dụ phim, giả sử bạn muốn lấy tất cả phim thuộc thể loại Sci-Fi. Cách làm như sau:
Bây giờ, bạn chỉ muốn các phim phát hành từ năm 2000 trở đi:
Vậy nếu muốn cả hai điều kiện? Hãy kết hợp điều kiện:
Hoặc, bạn có thể viết tường minh bằng
$and:Cả hai truy vấn trả về cùng kết quả—các tài liệu có thể loại Sci-Fi và năm phát hành từ 2000 trở đi. Dạng đầu ngắn gọn hơn và được ưa dùng khi các trường khác nhau.
Hình 3. Các cách viết truy vấn khác nhau này cho ra cùng một kết quả.
Hãy dùng
find()như một phễu lọc—mỗi điều kiện sẽ thu hẹp kết quả của bạn.Sử dụng toán tử so sánh và logic
Giờ hãy xem cách dùng các toán tử so sánh và logic để đưa truy vấn của bạn lên tầm tiếp theo.
Toán tử so sánh
$eq,$ne: bằng, không bằng$gt,$gte,$lt,$lte: lớn hơn/nhỏ hơn (hoặc bằng)$in,$nin: thuộc hoặc không thuộc mảngHãy tưởng tượng bạn đang theo dõi đơn hàng khách hàng và muốn các đơn trong khoảng 100–500 USD:
Toán tử logic
$and,$or,$nor,$notGiờ, giả sử bạn muốn hiển thị sản phẩm trong danh mục "Books" hoặc có giá dưới 20 USD. Dưới đây là một truy vấn ví dụ:
Truy vấn trường lồng nhau
Bạn có thể truy cập trường lồng nhau bằng cú pháp chấm:
Chiếu trường (Projection)
Projection cho phép bạn chỉ trả về những gì cần thiết, từ đó cải thiện đáng kể hiệu năng và độ rõ ràng.
Quan trọng: Khi chỉ định projection, MongoDB yêu cầu bạn chọn giữa hai chế độ: Hoặc bao gồm các trường cụ thể (1) hoặc loại trừ các trường cụ thể (0). Bạn không thể trộn cả hai kiểu trong cùng một projection, ngoại trừ trường
_id, có thể loại trừ ngay cả khi đang bao gồm các trường khác.✅ Hợp lệ (chỉ bao gồm):
✅ Hợp lệ (chỉ loại trừ):
❌ Không hợp lệ (trộn bao gồm và loại trừ):
Ngoại lệ duy nhất là trường
_id, bạn có thể loại trừ ngay cả khi đang bao gồm các trường khác:Hình 4. Ví dụ này minh họa lỗi khi trộn bao gồm và loại trừ trường trong một projection, điều MongoDB không cho phép, ngoại trừ `_id`.
Sắp xếp, giới hạn và bỏ qua kết quả
Khi làm việc với bộ dữ liệu lớn, hiển thị cho người dùng chỉ một phần dữ liệu mỗi lần là điều thường gặp—kỹ thuật này gọi là phân trang. Ví dụ, một ứng dụng streaming có thể hiển thị 10 phim mỗi trang thay vì tải hàng nghìn phim cùng lúc. Điều này không chỉ cải thiện hiệu năng mà còn nâng cao trải nghiệm người dùng.
Trong MongoDB, phân trang được thực hiện bằng cách kết hợp
sort(),limit()vàskip().SQL vs. MongoDB: So sánh cơ bản
Nếu bạn đến từ nền tảng SQL, dưới đây là cách các khái niệm phân trang phổ biến tương ứng:
Truy vấn cơ bản trong SQL:
Tương tự trong MongoDB như sau:
Chọn trường trong SQL:
Chọn trường trong MongoDB:
Giờ, hãy xem thêm vài ví dụ về các thao tác truy vấn phổ biến:
Sắp xếp:
ORDER BY title ASC.sort({ title: 1 })Giới hạn:
LIMIT 10.limit(10)Bỏ qua:
OFFSET 10.skip(10)Hiểu những điểm tương đồng này có thể giúp người dùng SQL chuyển sang hệ thống truy vấn dựa trên tài liệu của MongoDB dễ dàng hơn.
Sắp xếp kết quả
Sắp xếp giúp bạn tổ chức kết quả theo một trường cụ thể—ví dụ, sắp xếp phim theo tiêu đề theo thứ tự bảng chữ cái:
1nghĩa là thứ tự tăng dần (A–Z, 0–9)-1nghĩa là thứ tự giảm dần (Z–A, 9–0)Giới hạn kết quả
Chỉ trả về một số lượng kết quả nhất định—ví dụ, 10 kết quả đầu tiên:
Điều này hữu ích khi triển khai các danh sách “Top 10” hoặc màn hình ban đầu.
Bỏ qua kết quả
Dùng để bỏ qua các tài liệu. Thường kết hợp với limit() cho phân trang:
Truy vấn này bỏ qua 10 tài liệu đầu và trả về 10 tài liệu tiếp theo, hiệu quả hiển thị Trang 2 khi pageSize = 10.
Công thức phân trang:
> Lưu ý: Dùng
skip()cho phân trang sâu (ví dụ,skip(10000)) có thể kém hiệu quả, vì MongoDB vẫn quét và loại bỏ các tài liệu bị bỏ qua. Để hiệu năng tốt hơn với bộ dữ liệu lớn, hãy cân nhắc phân trang theo phạm vi dựa trên các trường được lập chỉ mục như_id.Ví dụ kết hợp: Trang 3, 10 phim mỗi trang, sắp xếp theo tiêu đề
Mẫu này cũng hữu ích cho các tính năng như “tải thêm” hoặc cuộn vô hạn.
Hình 5. Truy vấn này truy xuất trang thứ ba của kết quả (10 phim mỗi trang), sắp xếp theo tiêu đề.
Làm việc với con trỏ (Cursor)
Phương thức
find()trả về một con trỏ—một con trỏ tới tập kết quả. Bạn có thể lặp qua nó:Hoặc chuyển nó thành mảng:
Hình 6. Sau khi gọi `find()`, bạn nhận được một con trỏ. Bạn có thể dùng nó để lặp qua tài liệu hoặc chuyển toàn bộ tập kết quả thành mảng.
> Lưu ý: Đối tượng con trỏ có sẵn trong
mongoshhoặc trong mã qua các driver (ví dụ, Node.js, Python). Các công cụ GUI như MongoDB Compass xử lý con trỏ nội bộ. Bạn sẽ không truy cập hoặc lặp qua chúng trực tiếp.Tùy chọn nâng cao cho con trỏ
Để tinh chỉnh truy vấn, bạn có thể dùng thêm các phương thức trên con trỏ:
maxTimeMS(ms): hủy truy vấn nếu mất quá nhiều thời gianhint(): buộc MongoDB dùng một chỉ mục cụ thểbatchSize(n): kiểm soát số tài liệu mỗi lôĐang chuẩn bị phỏng vấn? Khám phá Top 25 câu hỏi phỏng vấn MongoDB kèm lời giải để kiểm tra mức hiểu biết của bạn về các khái niệm cốt lõi như
find().Chỉ mục và tối ưu hiệu năng
Chỉ mục giống như “phao cứu trợ”—chúng giúp MongoDB truy xuất tài liệu nhanh hơn.
Tạo chỉ mục
Sử dụng .explain()
Điều này cho biết liệu các chỉ mục có được sử dụng không và truy vấn của bạn hiệu quả thế nào.
Không có chỉ mục:
Hình 7. Chạy `explain()` cho truy vấn trước khi lập chỉ mục.
Tạo chỉ mục:
Hình 8. Tạo một chỉ mục đơn giản trên trường type.
Có chỉ mục:
Hình 9. Chạy lại truy vấn với `explain()`, lúc này dùng chỉ mục.
Bạn có thể thấy giai đoạn trước đây là
CollScan(quét bộ sưu tập) đã trở thànhFetch + IXSCAN, về cơ bản là dùng chỉ mục + tìm thêm dữ liệu không nằm trong chỉ mục (vì chúng ta đang trả về mọi thứ).Dùng MongoDB Compass để trực quan hóa truy vấn
Nếu bạn thích công cụ trực quan, MongoDB Compass là ứng dụng GUI chính thức. Nó cho phép bạn:
Bạn thậm chí có thể chuyển sang "JSON view" để sao chép/dán truy vấn vào mã của bạn.
Dùng Atlas + AI để tạo truy vấn
Nếu bạn đang dùng MongoDB Atlas, bạn có thể tận dụng Atlas Search với trình tạo truy vấn hỗ trợ AI.
Chỉ cần nhập lời nhắc ngôn ngữ tự nhiên như:
"Hiển thị tất cả khách hàng từ Brazil đã đặt đơn trên 100 USD trong tháng trước."
Atlas sẽ gợi ý một truy vấn MongoDB phù hợp bằng
find()hoặc$search. Đây là một công cụ tăng năng suất tuyệt vời, đặc biệt khi bạn chưa chắc về cú pháp.Hình 10. Dùng Compass để tạo truy vấn từ ngôn ngữ tự nhiên.
Những lỗi thường gặp và cách tránh
limit+skip)_idSo sánh MongoDB find() với SQL
Nếu bạn đến từ nền tảng SQL, hiểu cách
find()của MongoDB so với các truy vấn SQL quen thuộc có thể giúp quá trình chuyển đổi dễ dàng hơn. Dưới đây là so sánh tổng quan các thao tác phổ biến:Khái niệm
Ví dụ SQL
Tương đương trong MongoDB
Truy vấn cơ bản
SELECT * FROM users WHERE age >= 18db.users.find({ age: { $gte: 18 } })Chọn trường
SELECT name, email FROM usersdb.users.find({}, { name: 1, email: 1, _id: 0 })Sắp xếp
ORDER BY created_at DESC.sort({ createdAt: -1 })Giới hạn
LIMIT 10.limit(10)Bỏ qua
OFFSET 10.skip(10)Như bạn thấy, cú pháp của MongoDB giống JavaScript hơn và diễn đạt theo định dạng JSON, trong khi SQL mang tính khai báo và có cấu trúc. Biết những điểm tương đồng này sẽ giúp bạn nhanh chóng cảm thấy quen thuộc với MongoDB.
Tóm tắt và thực tiễn tốt
find()là bộ lọc dữ liệu của bạn—kết hợp điều kiện để có kết quả chính xác.Dù bạn đang xây dựng bảng điều khiển cho startup, công cụ phân tích dữ liệu hay back end thương mại điện tử, thành thạo
find()sẽ giúp trải nghiệm MongoDB của bạn mượt mà, nhanh và hiệu quả hơn.Hãy đón đọc thêm các bài viết về MongoDB trong thời gian tới!
Muốn xác nhận kỹ năng MongoDB của bạn? Đọc Hướng dẫn toàn diện về chứng chỉ MongoDB để tìm hiểu lựa chọn phù hợp.