Ten artykuł jest częścią serii:• Prosta implementacja tagowania z Elasticsearch
• Prosta implementacja tagowania za pomocą JPA
• Zaawansowana implementacja tagowania z JPA
• Prosta implementacja tagowania z MongoDB (aktualny artykuł)
1. Przegląd
W tym samouczku przyjrzymy się prostej implementacji tagowania przy użyciu Javy i MongoDB.
Dla osób niezaznajomionych z tą koncepcją tag jest słowem kluczowym używanym jako „etykieta” do grupowania dokumentów w różne kategorie. Pozwala to użytkownikom szybko poruszać się po podobnych treściach i jest szczególnie przydatne w przypadku dużej ilości danych.
Biorąc to pod uwagę, nie jest zaskakujące, że ta technika jest bardzo powszechnie stosowana na blogach. W tym scenariuszu każdy post ma jeden lub więcej tagów zgodnie z poruszanymi tematami. Kiedy użytkownik skończy czytać, może podążać za jednym z tagów, aby wyświetlić więcej treści związanych z tym tematem.
Zobaczmy, jak możemy wdrożyć ten scenariusz.
2. Zależność
Aby wykonać zapytanie do bazy danych, musimy uwzględnić zależność sterownika MongoDB w naszym pom.xml :
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.6.3</version>
</dependency>
Aktualną wersję tej zależności można znaleźć tutaj.
3. Model danych
Przede wszystkim zacznijmy od zaplanowania, jak powinien wyglądać dokument pocztowy.
Aby to uprościć, nasz model danych będzie miał tylko tytuł, którego użyjemy również jako identyfikatora dokumentu, autora i niektórych tagów.
Będziemy przechowywać tagi w tablicy, ponieważ post prawdopodobnie będzie zawierał więcej niż jeden:
{
"_id" : "Java 8 and MongoDB",
"author" : "Donato Rimenti",
"tags" : ["Java", "MongoDB", "Java 8", "Stream API"]
}
Stworzymy również odpowiednią klasę modelu Java:
public class Post {
private String title;
private String author;
private List<String> tags;
// getters and setters
}
4. Aktualizowanie tagów
Teraz, po skonfigurowaniu bazy danych i wstawieniu kilku przykładowych postów, zobaczmy, jak możemy je zaktualizować.
Nasza klasa repozytorium będzie zawierać dwie metody obsługi dodawania i usuwania tagów używając tytułu, aby je znaleźć. Zwrócimy również wartość logiczną, aby wskazać, czy zapytanie zaktualizowało element, czy nie:
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;
}
Użyliśmy addEachToSet metoda zamiast wypychania do dodania, aby jeśli tagi już tam były, nie dodamy ich ponownie.
Zauważ również, że addToSet operator również nie zadziałałby, ponieważ dodałby nowe tagi jako zagnieżdżoną tablicę, co nie jest tym, czego chcemy.
Innym sposobem, w jaki możemy wykonać nasze aktualizacje, jest użycie powłoki Mongo. Na przykład zaktualizujmy post JUnit5 o Javę. W szczególności chcemy dodać tagi Java i JJednostka5 i usuń tagi Wiosna i ODPOCZYNEK :
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. Zapytania
Na koniec przejrzyjmy niektóre z najczęstszych zapytań, które mogą nas zainteresować podczas pracy z tagami. W tym celu skorzystamy w szczególności z trzech operatorów tablicowych:
- $w – zwraca dokumenty, w których pole zawiera dowolną wartość określonej tablicy
- Nin $ – zwraca dokumenty, w których pole nie zawiera żadnej wartości określonej tablicy
- $wszystkie – zwraca dokumenty, w których pole zawiera wszystkie wartości określonej tablicy
Zdefiniujemy trzy metody wysyłania zapytań o posty w odniesieniu do zbioru tagów przekazanych jako argumenty . Zwrócą posty, które pasują do co najmniej jednego tagu, wszystkich tagów i żadnego tagu. Stworzymy również metodę mapowania do obsługi konwersji między dokumentem a naszym modelem przy użyciu Stream API 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;
}
Ponownie, przyjrzyjmy się również zapytaniom odpowiadającym powłoce . Pobierzemy trzy różne kolekcje postów, odpowiednio oznaczone tagiem MongoDB lub strumień API, oznaczone tagami Java 8 i JUnit 5 i nie oznaczone tagiem Groovy ani 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" ] }
});