Bài viết này là một phần của loạt bài:• Triển khai gắn thẻ đơn giản với Elasticsearch
• Triển khai gắn thẻ đơn giản với JPA
• Triển khai gắn thẻ nâng cao với JPA
• Triển khai gắn thẻ đơn giản với MongoDB (bài viết hiện tại)
1. Tổng quan
Trong hướng dẫn này, chúng ta sẽ xem xét cách triển khai gắn thẻ đơn giản bằng Java và MongoDB.
Đối với những người chưa quen với khái niệm này, thẻ là một từ khóa được sử dụng làm “nhãn” để nhóm tài liệu thành các danh mục khác nhau. Điều này cho phép người dùng nhanh chóng điều hướng qua các nội dung tương tự và nó đặc biệt hữu ích khi xử lý một lượng lớn dữ liệu.
Nói như vậy, không có gì ngạc nhiên khi kỹ thuật này được sử dụng rất phổ biến trong các blog. Trong trường hợp này, mỗi bài đăng có một hoặc nhiều thẻ theo các chủ đề được đề cập. Khi người dùng đọc xong, họ có thể theo dõi một trong các thẻ để xem thêm nội dung liên quan đến chủ đề đó.
Hãy xem chúng ta có thể thực hiện tình huống này như thế nào.
2. Sự phụ thuộc
Để truy vấn cơ sở dữ liệu, chúng tôi sẽ phải bao gồm phụ thuộc trình điều khiển MongoDB trong pom.xml của chúng tôi :
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.6.3</version>
</dependency>
Bạn có thể tìm thấy phiên bản hiện tại của phần phụ thuộc này tại đây.
3. Mô hình dữ liệu
Trước hết, hãy bắt đầu bằng cách lên kế hoạch cho một tài liệu đăng sẽ trông như thế nào.
Để đơn giản, mô hình dữ liệu của chúng tôi sẽ chỉ có tiêu đề, mà chúng tôi cũng sẽ sử dụng làm id tài liệu, tác giả và một số thẻ.
Chúng tôi sẽ lưu trữ các thẻ bên trong một mảng vì một bài đăng có thể sẽ có nhiều hơn một thẻ:
{
"_id" : "Java 8 and MongoDB",
"author" : "Donato Rimenti",
"tags" : ["Java", "MongoDB", "Java 8", "Stream API"]
}
Chúng tôi cũng sẽ tạo lớp mô hình Java tương ứng:
public class Post {
private String title;
private String author;
private List<String> tags;
// getters and setters
}
4. Đang cập nhật thẻ
Bây giờ chúng tôi đã thiết lập cơ sở dữ liệu và chèn một vài bài đăng mẫu, hãy xem cách chúng tôi có thể cập nhật chúng.
Lớp kho lưu trữ của chúng tôi sẽ bao gồm hai phương pháp để xử lý việc thêm và xóa thẻ bằng cách sử dụng tiêu đề để tìm chúng. Chúng tôi cũng sẽ trả về một boolean để cho biết truy vấn có cập nhật một phần tử hay không:
public boolean addTags(String title, List<String> tags) {
UpdateResult result = collection.updateOne(
new BasicDBObject(DBCollection.ID_FIELD_NAME, title),
Updates.addEachToSet(TAGS_FIELD, tags));
return result.getModifiedCount() == 1;
}
public boolean removeTags(String title, List<String> tags) {
UpdateResult result = collection.updateOne(
new BasicDBObject(DBCollection.ID_FIELD_NAME, title),
Updates.pullAll(TAGS_FIELD, tags));
return result.getModifiedCount() == 1;
}
Chúng tôi đã sử dụng addEachToSet thay vì push để bổ sung sao cho nếu các thẻ đã ở đó, chúng tôi sẽ không thêm lại.
Cũng lưu ý rằng addToSet toán tử cũng sẽ không hoạt động vì nó sẽ thêm các thẻ mới dưới dạng một mảng lồng nhau không phải là điều chúng ta muốn.
Một cách khác mà chúng tôi có thể thực hiện cập nhật của mình là thông qua trình bao Mongo. Ví dụ:hãy cập nhật bài đăng JUnit5 với Java. Đặc biệt, chúng tôi muốn thêm các thẻ Java và J Unit5 và xóa các thẻ Mùa xuân và REST :
db.posts.updateOne(
{ _id : "JUnit 5 with Java" },
{ $addToSet :
{ "tags" :
{ $each : ["Java", "JUnit5"] }
}
});
db.posts.updateOne(
{_id : "JUnit 5 with Java" },
{ $pull :
{ "tags" : { $in : ["Spring", "REST"] }
}
});
5. Truy vấn
Cuối cùng nhưng không kém phần quan trọng, hãy cùng điểm qua một số truy vấn phổ biến nhất mà chúng ta có thể quan tâm khi làm việc với các thẻ. Với mục đích này, chúng tôi sẽ tận dụng ba toán tử mảng cụ thể:
- $ trong - trả về các tài liệu trong đó trường chứa bất kỳ giá trị nào của mảng được chỉ định
- $ nin - trả về các tài liệu trong đó trường không chứa bất kỳ giá trị nào của mảng được chỉ định
- $ tất cả - trả về các tài liệu trong đó một trường chứa tất cả các giá trị của mảng được chỉ định
Chúng tôi sẽ xác định ba phương pháp để truy vấn các bài đăng liên quan đến tập hợp các thẻ được chuyển dưới dạng đối số . Họ sẽ trả về các bài đăng phù hợp với ít nhất một thẻ, tất cả các thẻ và không có thẻ nào. Chúng tôi cũng sẽ tạo một phương pháp ánh xạ để xử lý việc chuyển đổi giữa tài liệu và mô hình của chúng tôi bằng cách sử dụng API luồng của Java 8:
public List<Post> postsWithAtLeastOneTag(String... tags) {
FindIterable<Document> results = collection
.find(Filters.in(TAGS_FIELD, tags));
return StreamSupport.stream(results.spliterator(), false)
.map(TagRepository::documentToPost)
.collect(Collectors.toList());
}
public List<Post> postsWithAllTags(String... tags) {
FindIterable<Document> results = collection
.find(Filters.all(TAGS_FIELD, tags));
return StreamSupport.stream(results.spliterator(), false)
.map(TagRepository::documentToPost)
.collect(Collectors.toList());
}
public List<Post> postsWithoutTags(String... tags) {
FindIterable<Document> results = collection
.find(Filters.nin(TAGS_FIELD, tags));
return StreamSupport.stream(results.spliterator(), false)
.map(TagRepository::documentToPost)
.collect(Collectors.toList());
}
private static Post documentToPost(Document document) {
Post post = new Post();
post.setTitle(document.getString(DBCollection.ID_FIELD_NAME));
post.setAuthor(document.getString("author"));
post.setTags((List<String>) document.get(TAGS_FIELD));
return post;
}
Một lần nữa, hãy cũng xem xét các truy vấn tương đương với shell . Chúng tôi sẽ tìm nạp ba bộ sưu tập bài đăng khác nhau được gắn thẻ tương ứng với MongoDB hoặc Luồng API, được gắn thẻ với cả Java 8 và JUnit 5 và không được gắn thẻ với Groovy cũng không phải Scala :
db.posts.find({
"tags" : { $in : ["MongoDB", "Stream API" ] }
});
db.posts.find({
"tags" : { $all : ["Java 8", "JUnit 5" ] }
});
db.posts.find({
"tags" : { $nin : ["Groovy", "Scala" ] }
});